Random segfaults for python rqt plugin
I've tried to write a couple of rqt plugins in order to make interaction with our ROS system easier. I'm not confident or familiar with QT (or C++) from beforehand, so I wrote the plugins with basis in the rqt-plugin tutorials.
One of the plugins is a used for controlling a simulation. This interacts with a node controlling a simulated clock using a service, and echoes the simulated clock to a QLineEdit. The plugin works well, but randomly exits with segfault. This seems to happen more often when my system has been running for a while. If I restart the plugin after it segfaults, it may segfault straight away, or run for an extended time period. After a restart, the plugin runs for a long time before I experience problems.
Some thought on what may cause this, without being able to fix it:
- Python garbage collection: I understand that python may garbage collect QT objects that have gone out of scope in Python, while the objects are still referenced/used in C++. I've made all my QT objects attributes of the plugin class to tackle this.
- The clock-topic is published at a high rate (100Hz). I figured that it may be a problem that the clock-callback got "stuck" if the plugin was occupied in a service call. I tried adding a lock to ignore received clock topics in such cases, without it seeming to have an effect.
I've run the plugin both by launching rqt
and loading it (using python 2
and PyQt5
), and running it standalone (using python 3
and PySide2
). The problem occurs for both approaches.
I'm running Meldic Morenia on ubuntu 18.04 with kernel 5.3.0-45-generic. All inputs or suggestions are highly appreciated!
The plugin code:
import os
import rospy
import rospkg
from qt_gui.plugin import Plugin
from python_qt_binding import loadUi
from python_qt_binding.QtWidgets import QWidget
from threading import Lock
import std_srvs.srv as stdsrvs
from rosgraph_msgs.msg import Clock
class SimControl(Plugin):
gui_lock = Lock()
def __init__(self, context):
super(SimControl, self).__init__(context)
# Give QObjects reasonable names
self.setObjectName('SimControl')
# Process standalone plugin command-line arguments
from argparse import ArgumentParser
parser = ArgumentParser()
# Add argument(s) to the parser.
parser.add_argument("-q", "--quiet", action="store_true",
dest="quiet",
help="Put plugin in silent mode")
args, unknowns = parser.parse_known_args(context.argv())
if not args.quiet:
print('arguments: ', args)
print('unknowns: ', unknowns)
# Create QWidget
self._widget = QWidget()
# Get path to UI file which should be in the "resource" folder of this package
ui_file = os.path.join(rospkg.RosPack().get_path('ros_af_sim'),
'resource', 'sim_control.ui')
# Extend the widget with all attributes and children from UI file
loadUi(ui_file, self._widget)
# Give QObjects reasonable names
self._widget.setObjectName('SimControlUi')
# Show _widget.windowTitle on left-top of each plugin (when
# it's set in _widget). This is useful when you open multiple
# plugins at once. Also if you open multiple instances of your
# plugin at once, these lines add number to make it easy to
# tell from pane to ...