rqt plugin connection error (ros subscriber)
-
We are trying to create a plugin for rqt to display data coming from ros publisher. We designed the plugin code as a subscriber, so it can subscribe to the publisher directly.
import os import rospy import rospkg from qt_gui.plugin import Plugin from python_qt_binding import loadUi from python_qt_binding.QtCore import Qt, Slot from python_qt_binding.QtWidgets import QWidget from std_msgs.msg import String class MyPlugin(Plugin): def __init__(self, context): super(MyPlugin, self).__init__(context) # Give QObjects reasonable names self.setObjectName('MyPlugin') # Process standalone plugin command-line arguments from argparse import ArgumentParser parser = ArgumentParser() # Add sub code rospy.Subscriber('topic',String,self.veriYaz) # 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('rqt_mypkg'), 'resource', 'MyPlugin.ui') # Extend the widget with all attributes and children from UI file loadUi(ui_file, self._widget) # Give QObjects reasonable names self._widget.setObjectName('MyPluginUi') # 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 pane. if context.serial_number() > 1: self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % context.serial_number())) # Add widget to the user interface context.add_widget(self._widget) def veriYaz(self,data): self._widget.veri_txt.setPlainText(data.data) def shutdown_plugin(self): # TODO unregister all publishers here pass def save_settings(self, plugin_settings, instance_settings): # TODO save intrinsic configuration, usually using: # instance_settings.set_value(k, v) pass def restore_settings(self, plugin_settings, instance_settings): # TODO restore intrinsic configuration, usually using: # v = instance_settings.value(k) pass #def trigger_configuration(self): # Comment in to signal that the plugin has a way to configure # This will enable a setting button (gear icon) in each dock widget title bar # Usually used to open a modal configuration dialog
But as we tried to connect a PlainText to the constantly changing data, an error occured. The plugin can be opened but when we start the publisher, plugin crashes and closes itself. The occured error is like this:
We searched forums and found out that we should connect them as signal/slot. We don't know how to change that code to signal/slot logic. Can someone help?
-
@aliemrenebiler It seems that the veriYaz method is invoked in a secondary thread causing this problem, a possible solution is to use a QObject as an intermediary and use signals.
class Signaller(QObject): text_changed = Signal(str)
# Extend the widget with all attributes and children from UI file loadUi(ui_file, self._widget) self.signaller = Signaller() self.signaller.text_changed.connect(self._widget.veri_txt.setPlainText) # ...
def veriYaz(self,data): self.signaller.text_changed.emit(data.data)
-
Hi,
Are you using that example as a base ?
-
@aliemrenebiler It seems that the veriYaz method is invoked in a secondary thread causing this problem, a possible solution is to use a QObject as an intermediary and use signals.
class Signaller(QObject): text_changed = Signal(str)
# Extend the widget with all attributes and children from UI file loadUi(ui_file, self._widget) self.signaller = Signaller() self.signaller.text_changed.connect(self._widget.veri_txt.setPlainText) # ...
def veriYaz(self,data): self.signaller.text_changed.emit(data.data)
-
@eyllanesc That worked like a charm! Thank you!
Final code:
import os import rospy import rospkg from qt_gui.plugin import Plugin from python_qt_binding import loadUi from python_qt_binding.QtCore import QObject from python_qt_binding.QtCore import Qt, Slot from python_qt_binding.QtCore import Qt, Signal from python_qt_binding.QtWidgets import QWidget from std_msgs.msg import String class MyPlugin(Plugin): def __init__(self, context): class Signaller(QObject): text_changed= Signal(str) super(MyPlugin, self).__init__(context) # Give QObjects reasonable names self.setObjectName('MyPlugin') # Process standalone plugin command-line arguments from argparse import ArgumentParser parser = ArgumentParser() # Add sub code rospy.Subscriber('topic',String,self.veriYaz) # 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('rqt_mypkg'), 'resource', 'MyPlugin.ui') # Extend the widget with all attributes and children from UI file loadUi(ui_file, self._widget) self.signaller = Signaller() self.signaller.text_changed.connect(self._widget.veri_txt.setPlainText) # Give QObjects reasonable names self._widget.setObjectName('MyPluginUi') # 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 pane. if context.serial_number() > 1: self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % context.serial_number())) # Add widget to the user interface context.add_widget(self._widget) def veriYaz(self,data): self.signaller.text_changed.emit(data.data) def shutdown_plugin(self): # TODO unregister all publishers here pass def save_settings(self, plugin_settings, instance_settings): # TODO save intrinsic configuration, usually using: # instance_settings.set_value(k, v) pass def restore_settings(self, plugin_settings, instance_settings): # TODO restore intrinsic configuration, usually using: # v = instance_settings.value(k) pass #def trigger_configuration(self): # Comment in to signal that the plugin has a way to configure # This will enable a setting button (gear icon) in each dock widget title bar # Usually used to open a modal configuration dialog