class ConsoleDashWidget(IconToolButton): """ A widget which brings up the ROS console. :param context: The plugin context to create the monitor in. :type context: qt_gui.plugin_context.PluginContext """ def __init__(self, context, icon_paths=None, minimal=True): ok_icon = ['bg-green.svg', 'ic-console.svg'] warn_icon = ['bg-yellow.svg', 'ic-console.svg', 'ol-warn-badge.svg'] err_icon = ['bg-red.svg', 'ic-console.svg', 'ol-err-badge.svg'] stale_icon = ['bg-grey.svg', 'ic-console.svg', 'ol-stale-badge.svg'] icons = [ok_icon, warn_icon, err_icon, stale_icon] super(ConsoleDashWidget, self).__init__('Console Widget', icons, icon_paths=icon_paths) self.minimal = minimal self.setFixedSize(self._icons[0].actualSize(QSize(50, 30))) self._datamodel = MessageDataModel() self._proxymodel = MessageProxyModel() self._proxymodel.setSourceModel(self._datamodel) self._console = None self._rospack = rospkg.RosPack() if self._console is None: self._console = ConsoleWidget(self._proxymodel, self._rospack, minimal=self.minimal) self._console.destroyed.connect(self._console_destroyed) self._message_queue = [] self._mutex = QMutex() self._subscriber = rospy.Subscriber('/rosout_agg', Log, self._message_cb) self.context = context self.clicked.connect(self._show_console) self.context.add_widget(self._console) self.update_state(0) self._timer = QTimer() self._timer.timeout.connect(self._insert_messages) self._timer.start(100) self._console_shown = False self.setToolTip("Rosout") def _show_console(self): if self._console is None: self._console = ConsoleWidget(self._proxymodel, self._rospack, minimal=self.minimal) self._console.destroyed.connect(self._console_destroyed) try: if self._console_shown: self.context.remove_widget(self._console) self._console_shown = not self._console_shown else: self.context.add_widget(self._console) self._console_shown = not self._console_shown except Exception: self._console_shown = not self._console_shown self._show_console() def _insert_messages(self): with QMutexLocker(self._mutex): msgs = self._message_queue self._message_queue = [] if msgs: self._datamodel.insert_rows(msgs) # The console may not yet be initialized or may have been closed # So fail silently try: self.update_rosout() except: pass def _message_cb(self, log_msg): if not self._console._paused: msg = Console.convert_rosgraph_log_message(log_msg) with QMutexLocker(self._mutex): self._message_queue.append(msg) def update_rosout(self): summary_dur = 30.0 if (rospy.get_time() < 30.0): summary_dur = rospy.get_time() - 1.0 if (summary_dur < 0): summary_dur = 0.0 summary = self._console.get_message_summary(summary_dur) if (summary.fatal or summary.error): self.update_state(2) elif (summary.warn): self.update_state(1) else: self.update_state(0) tooltip = "" if (summary.fatal): tooltip += "\nFatal: %s" % (summary.fatal) if (summary.error): tooltip += "\nError: %s" % (summary.error) if (summary.warn): tooltip += "\nWarn: %s" % (summary.warn) if (summary.info): tooltip += "\nInfo: %s" % (summary.info) if (summary.debug): tooltip += "\nDebug: %s" % (summary.debug) if (len(tooltip) == 0): tooltip = "Rosout: no recent activity" else: tooltip = "Rosout: recent activity:" + tooltip if tooltip != self.toolTip(): self.setToolTip(tooltip) def _console_destroyed(self): if self._console: self._console.cleanup_browsers_on_close() self._console = None def shutdown_widget(self): if self._console: self._console.cleanup_browsers_on_close() if self._subscriber: self._subscriber.unregister() self._timer.stop() def save_settings(self, plugin_settings, instance_settings): self._console.save_settings(plugin_settings, instance_settings) def restore_settings(self, plugin_settings, instance_settings): self._console.restore_settings(plugin_settings, instance_settings)
class Console(Plugin): """ rqt_console plugin's main class. Handles communication with ros_gui and contains callbacks to handle incoming message """ def __init__(self, context): """ :param context: plugin context hook to enable adding widgets as a ROS_GUI pane, ''PluginContext'' """ super(Console, self).__init__(context) self.setObjectName('Console') self._context = context self._model = MessageDataModel() self._proxy_model = MessageProxyModel() self._proxy_model.setSourceModel(self._model) self._widget = ConsoleWidget(self._proxy_model) if context.serial_number() > 1: self._widget.setWindowTitle( self._widget.windowTitle() + (' (%d)' % context.serial_number())) context.add_widget(self._widget) # queue to store incoming data which get flushed periodically to the model # required since QSortProxyModel can not handle a high insert rate self._message_queue = [] self._mutex = QMutex() self._timer = QTimer() self._timer.timeout.connect(self.insert_messages) self._timer.start(100) self._subscriber = None self._topic = '/rosout' self._subscribe(self._topic) def queue_message(self, log_msg): """ Callback for adding an incomming message to the queue. """ if not self._widget._paused: msg = Console.convert_rosgraph_log_message(log_msg) with QMutexLocker(self._mutex): self._message_queue.append(msg) @staticmethod def convert_rosgraph_log_message(log_msg): msg = Message() msg.set_stamp_format('hh:mm:ss.ZZZ (yyyy-MM-dd)') msg.stamp = (log_msg.stamp.sec, log_msg.stamp.nanosec) msg.message = log_msg.msg msg.severity = log_msg.level msg.node = log_msg.name msg.location = log_msg.file + ':' + log_msg.function + ':' + str(log_msg.line) return msg def insert_messages(self): """ Callback for flushing incoming Log messages from the queue to the model. """ with QMutexLocker(self._mutex): msgs = self._message_queue self._message_queue = [] if msgs: self._model.insert_rows(msgs) def shutdown_plugin(self): self._context.node.destroy_subscription(self._subscriber) self._timer.stop() self._widget.cleanup_browsers_on_close() def save_settings(self, plugin_settings, instance_settings): self._widget.save_settings(plugin_settings, instance_settings) def restore_settings(self, plugin_settings, instance_settings): self._widget.restore_settings(plugin_settings, instance_settings) def trigger_configuration(self): topics = sorted([ topic_name for topic_name, topic_types in self._context.node.get_topic_names_and_types() if 'rcl_interfaces/msg/Log' in topic_types ]) dialog = ConsoleSettingsDialog(topics) (topic, message_limit) = dialog.query(self._topic, self._model.get_message_limit()) if topic != self._topic: self._subscribe(topic) if message_limit != self._model.get_message_limit(): self._model.set_message_limit(message_limit) def _subscribe(self, topic): if self._subscriber: self._context.node.destroy_subscription(self._subscriber) self._subscriber = self._context.node.create_subscription( Log, topic, self.queue_message, qos_profile_system_default ) self._currenttopic = topic
class PluginLogManager(Plugin): """ rqt_console plugin's main class. Handles communication with ros_gui and contains callbacks to handle incoming message """ def __init__(self, context): Plugin.__init__(self, context) def onCreate(self, param): layout = QGridLayout(self) layout.setContentsMargins(2, 2, 2, 2) self._rospack = rospkg.RosPack() self._model = MessageDataModel() self._proxy_model = MessageProxyModel() self._proxy_model.setSourceModel(self._model) self.__widget = ConsoleWidget(self._proxy_model, self._rospack) layout.addWidget(self.__widget, 0, 0, 0, 0) # queue to store incoming data which get flushed periodically to the model # required since QSortProxyModel can not handle a high insert rate self._message_queue = [] self._mutex = QMutex() self._timer = QTimer() self._timer.timeout.connect(self.insert_messages) self._timer.start(100) self._subscriber = None self._topic = '/rosout_agg' self._subscribe(self._topic) def queue_message(self, log_msg): """ Callback for adding an incomming message to the queue. """ if not self.__widget._paused: msg = PluginLogManager.convert_rosgraph_log_message(log_msg) with QMutexLocker(self._mutex): self._message_queue.append(msg) @staticmethod def convert_rosgraph_log_message(log_msg): msg = Message() msg.set_stamp_format('hh:mm:ss.ZZZ (yyyy-MM-dd)') msg.message = log_msg.msg msg.severity = log_msg.level msg.node = log_msg.name msg.stamp = (log_msg.header.stamp.secs, log_msg.header.stamp.nsecs) msg.topics = sorted(log_msg.topics) msg.location = log_msg.file + ':' + log_msg.function + ':' + str( log_msg.line) return msg def insert_messages(self): """ Callback for flushing incoming Log messages from the queue to the model. """ with QMutexLocker(self._mutex): msgs = self._message_queue self._message_queue = [] if msgs: self._model.insert_rows(msgs) def shutdown_plugin(self): self._subscriber.unregister() self._timer.stop() self.__widget.cleanup_browsers_on_close() def save_settings(self, plugin_settings, instance_settings): self.__widget.save_settings(plugin_settings, instance_settings) def restore_settings(self, plugin_settings, instance_settings): self.__widget.restore_settings(plugin_settings, instance_settings) def trigger_configuration(self): topics = [ t for t in rospy.get_published_topics() if t[1] == 'rosgraph_msgs/Log' ] topics.sort(key=lambda tup: tup[0]) dialog = ConsoleSettingsDialog(topics, self._rospack) (topic, message_limit) = dialog.query(self._topic, self._model.get_message_limit()) if topic != self._topic: self._subscribe(topic) if message_limit != self._model.get_message_limit(): self._model.set_message_limit(message_limit) def _subscribe(self, topic): if self._subscriber: self._subscriber.unregister() self._subscriber = rospy.Subscriber(topic, Log, self.queue_message) self._currenttopic = topic def onStart(self): pass def onPause(self): pass def onResume(self): pass def onControlModeChanged(self, mode): if mode == ControlMode.AUTOMATIC: self.setEnabled(False) else: self.setEnabled(True) def onUserChanged(self, user_info): pass def onTranslate(self, lng): pass def onEmergencyStop(self, state): pass def onDestroy(self): self.shutdown_plugin()
class ConsoleDashWidget(IconToolButton): """ A widget which brings up the ROS console. :param context: The plugin context to create the monitor in. :type context: qt_gui.plugin_context.PluginContext """ def __init__(self, context, icon_paths=None, minimal=True): ok_icon = ['bg-green.svg', 'ic-console.svg'] warn_icon = ['bg-yellow.svg', 'ic-console.svg', 'ol-warn-badge.svg'] err_icon = ['bg-red.svg', 'ic-console.svg', 'ol-err-badge.svg'] stale_icon = ['bg-grey.svg', 'ic-console.svg', 'ol-stale-badge.svg'] icons = [ok_icon, warn_icon, err_icon, stale_icon] super(ConsoleDashWidget, self).__init__('Console Widget', icons, icon_paths=icon_paths) self.minimal = minimal self.setFixedSize(self._icons[0].actualSize(QSize(50, 30))) self._datamodel = MessageDataModel() self._proxymodel = MessageProxyModel() self._proxymodel.setSourceModel(self._datamodel) self._message_queue = [] self._mutex = QMutex() self._subscriber = rospy.Subscriber('/rosout_agg', Log, self._message_cb) self._console = None self.context = context self.clicked.connect(self._show_console) self.update_state(0) self._timer = QTimer() self._timer.timeout.connect(self._insert_messages) self._timer.start(100) self._rospack = rospkg.RosPack() if self._console is None: self._console = ConsoleWidget(self._proxymodel, self._rospack, minimal=self.minimal) self._console.destroyed.connect(self._console_destroyed) self._console_shown = False self.setToolTip("Rosout") def _show_console(self): if self._console is None: self._console = ConsoleWidget(self._proxymodel, self._rospack, minimal=self.minimal) self._console.destroyed.connect(self._console_destroyed) try: if self._console_shown: self.context.remove_widget(self._console) self._console_shown = not self._console_shown else: self.context.add_widget(self._console) self._console_shown = not self._console_shown except Exception: self._console_shown = not self._console_shown self._show_console() def _insert_messages(self): with QMutexLocker(self._mutex): msgs = self._message_queue self._message_queue = [] if msgs: self._datamodel.insert_rows(msgs) # The console may not yet be initialized or may have been closed # So fail silently try: self.update_rosout() except: pass def _message_cb(self, log_msg): if not self._console._paused: msg = Console.convert_rosgraph_log_message(log_msg) with QMutexLocker(self._mutex): self._message_queue.append(msg) def update_rosout(self): summary_dur = 30.0 if (rospy.get_time() < 30.0): summary_dur = rospy.get_time() - 1.0 if (summary_dur < 0): summary_dur = 0.0 summary = self._console.get_message_summary(summary_dur) if (summary.fatal or summary.error): self.update_state(2) elif (summary.warn): self.update_state(1) else: self.update_state(0) tooltip = "" if (summary.fatal): tooltip += "\nFatal: %s" % (summary.fatal) if (summary.error): tooltip += "\nError: %s" % (summary.error) if (summary.warn): tooltip += "\nWarn: %s" % (summary.warn) if (summary.info): tooltip += "\nInfo: %s" % (summary.info) if (summary.debug): tooltip += "\nDebug: %s" % (summary.debug) if (len(tooltip) == 0): tooltip = "Rosout: no recent activity" else: tooltip = "Rosout: recent activity:" + tooltip if tooltip != self.toolTip(): self.setToolTip(tooltip) def _console_destroyed(self): if self._console: self._console.cleanup_browsers_on_close() self._console = None def shutdown_widget(self): if self._console: self._console.cleanup_browsers_on_close() if self._subscriber: self._subscriber.unregister() self._timer.stop() def save_settings(self, plugin_settings, instance_settings): self._console.save_settings(plugin_settings, instance_settings) def restore_settings(self, plugin_settings, instance_settings): self._console.restore_settings(plugin_settings, instance_settings)