def _switch_perspective(self, name, settings_changed, save_before, without_plugin_changes=False): # convert from unicode name = str(name.replace('/', '__')) qDebug('PerspectiveManager.switch_perspective() switching to perspective "%s"' % name) if self._current_perspective is not None and self._menu_manager is not None: self._menu_manager.set_item_checked(self._current_perspective, False) self._menu_manager.set_item_disabled(self._current_perspective, False) # create perspective if necessary if name not in self.perspectives: self._create_perspective(name, clone_perspective=False) # update current perspective self._current_perspective = name if self._menu_manager is not None: self._menu_manager.set_item_checked(self._current_perspective, True) self._menu_manager.set_item_disabled(self._current_perspective, True) if not self._current_perspective.startswith(self.HIDDEN_PREFIX): self._settings_proxy.set_value('', 'current-perspective', self._current_perspective) self._perspective_settings = self._get_perspective_settings(self._current_perspective) # emit signals self.perspective_changed_signal.emit(self._current_perspective.lstrip(self.HIDDEN_PREFIX)) if settings_changed: if not without_plugin_changes: self.restore_settings_signal.emit(self._global_settings, self._perspective_settings) else: self.restore_settings_without_plugin_changes_signal.emit(self._global_settings, self._perspective_settings)
def _init_node(self): # initialize node once if not self._node_initialized: name = 'rqt_gui_py_node_%d' % os.getpid() qDebug('RosPyPluginProvider._init_node() initialize ROS node "%s"' % name) rospy.init_node(name, disable_signals=True) self._node_initialized = True
def restore_settings_without_plugins(self, global_settings, perspective_settings): qDebug('PluginManager.restore_settings_without_plugins()') self._global_settings = global_settings.get_settings('pluginmanager') self._perspective_settings = perspective_settings.get_settings( 'pluginmanager') self._restore_settings_restore()
def _on_remove_perspective(self): # input dialog to choose perspective to be removed names = list(self.perspectives) names.remove(self._current_perspective) name, return_value = QInputDialog.getItem( self._menu_manager.menu, self._menu_manager.tr('Remove perspective'), self._menu_manager.tr('Select the perspective'), names, 0, False) # convert from unicode name = str(name) if return_value == QInputDialog.Rejected: return if name not in self.perspectives: raise UserWarning('unknown perspective: %s' % name) qDebug('PerspectiveManager._on_remove_perspective(%s)' % str(name)) # remove from list of perspectives self.perspectives.remove(name) self._settings_proxy.set_value('', 'perspectives', self.perspectives) # remove settings settings = self._get_perspective_settings(name) settings.remove('') # remove from menu self._menu_manager.remove_item(name) # disable remove-action if self._menu_manager.count_items() < 2: self._remove_action.setEnabled(False)
def shutdown(self): qDebug('Shutting down RosPyPluginProvider') if self._spinner: self._spinner.quit() if self._node: self._destroy_node() super().shutdown()
def _reload_plugin_restore(self, handler, exception): qDebug('PluginManager._reload_plugin_restore()') self._load_plugin_completed(handler, exception) if exception is None: # restore settings after load self._restore_plugin_settings(handler.instance_id(), self._emit_load_plugin_completed)
def _close_application_shutdown_callback(self, instance_id=None): if instance_id is not None: self._number_of_ongoing_calls = self._number_of_ongoing_calls - 1 if self._number_of_ongoing_calls == 0: qDebug('PluginManager.close_application() completed') self._number_of_ongoing_calls = None self.close_application_signal.emit()
def load_bag(self, filename): qDebug("Loading '%s'..." % filename) self.set_status_text.emit("Loading '%s'..." % filename) try: bag = rosbag.Bag(filename) self.capture_button.setEnabled(True) self.play_button.setEnabled(True) self.thumbs_button.setEnabled(True) self.zoom_in_button.setEnabled(True) self.zoom_out_button.setEnabled(True) self.zoom_all_button.setEnabled(True) self.next_button.setEnabled(True) self.previous_button.setEnabled(True) self.faster_button.setEnabled(True) self.slower_button.setEnabled(True) self.begin_button.setEnabled(True) self.end_button.setEnabled(True) self.save_button.setEnabled(True) self.record_button.setEnabled(False) self._timeline.add_bag(bag) qDebug("Done loading '%s'" % filename) # put the progress bar back the way it was self.set_status_text.emit("") except rosbag.ROSBagException as e: qWarning("Loading '%s' failed due to: %s" % (filename, e)) self.set_status_text.emit("Loading '%s' failed due to: %s" % (filename, e))
def _discover(self): discovery_data = self._settings.get_settings('discovery_data') # check timestamp of the cached discovery data cache_stamp = self._settings.value('discovery_timestamp', default_value=None) if cache_stamp: cache_stamp = float(cache_stamp) # wipe cached data when forcing discovery or when cache is to old (or from the future) now = time.time() if self._application_context.options.force_discover or not cache_stamp or cache_stamp > now or cache_stamp + PluginManager.discovery_cache_max_age < now: qDebug('PluginManager._discover() force discovery of plugins') cache_stamp = None for k in discovery_data.all_keys(): discovery_data.remove(k) else: qDebug( 'PluginManager._discover() using cached plugin discovery information' ) plugin_descriptors = self._plugin_provider.discover(discovery_data) # store timestamp and newly discovered data if not cache_stamp: self._settings.set_value('discovery_timestamp', now) return plugin_descriptors
def _save_settings_callback(self, instance_id=None): if instance_id is not None: self._number_of_ongoing_calls = self._number_of_ongoing_calls - 1 if self._number_of_ongoing_calls == 0: qDebug('PluginManager.save_settings() completed') self._number_of_ongoing_calls = None self.save_settings_completed_signal.emit()
def _close_application_shutdown_callback(self, instance_id=None): if instance_id is not None: self._number_of_ongoing_calls = self._number_of_ongoing_calls - 1 if self._number_of_ongoing_calls == 0: qDebug('PluginManager.close_application() completed') self._number_of_ongoing_calls = None self._close_application_signal()
def load_bag(self, filename): qDebug("Loading '%s'..." % filename.encode(errors='replace')) # QProgressBar can EITHER: show text or show a bouncing loading bar, # but apparently the text is hidden when the bounding loading bar is # shown # self.progress_bar.setRange(0, 0) self.set_status_text.emit("Loading '%s'..." % filename) # progress_format = self.progress_bar.format() # progress_text_visible = self.progress_bar.isTextVisible() # self.progress_bar.setFormat("Loading %s" % filename) # self.progress_bar.setTextVisible(True) try: bag = rosbag.Bag(filename) self.play_button.setEnabled(True) self.thumbs_button.setEnabled(True) self.zoom_in_button.setEnabled(True) self.zoom_out_button.setEnabled(True) self.zoom_all_button.setEnabled(True) self.next_button.setEnabled(True) self.previous_button.setEnabled(True) self.faster_button.setEnabled(True) self.slower_button.setEnabled(True) self.begin_button.setEnabled(True) self.end_button.setEnabled(True) self.save_button.setEnabled(True) self.record_button.setEnabled(False) self._timeline.add_bag(bag) qDebug("Done loading '%s'" % filename.encode(errors='replace')) # put the progress bar back the way it was self.set_status_text.emit("") except rosbag.ROSBagException as e: qWarning("Loading '%s' failed due to: %s" % (filename.encode(errors='replace'), e)) self.set_status_text.emit("Loading '%s' failed due to: %s" % (filename, e))
def save_settings(self, global_settings, perspective_settings): qDebug('MainWindow.save_settings()') self._global_settings = global_settings self._perspective_settings = perspective_settings self._settings = self._perspective_settings.get_settings('mainwindow') # store current setup to current _settings self._save_geometry_to_perspective() self._save_state_to_perspective()
def load_plugin(self, plugin_id, serial_number=None, argv=None): qDebug('PluginManager.load_plugin(%s, %s)' % (str(plugin_id), str(serial_number) if serial_number is not None else '')) # save state of top-level widgets self.plugins_about_to_change_signal.emit() if serial_number is None: serial_number = self._next_serial_number(plugin_id) instance_id = PluginInstanceId(plugin_id, serial_number) self._load_plugin_load(instance_id, self._load_plugin_restore, argv)
def restore_settings(self, global_settings, perspective_settings): qDebug('MainWindow.restore_settings()') # remember new _settings self._global_settings = global_settings self._perspective_settings = perspective_settings self._settings = self._perspective_settings.get_settings('mainwindow') # only restore geometry, restoring state is triggered after PluginManager has been updated self._restore_geometry_from_perspective()
def emit_save_settings_completed(self): qDebug('PluginHandler.emit_save_settings_completed()') self._call_method_on_all_dock_widgets('save_settings', self.__instance_settings) self.__instance_settings = None if self.__callback is not None: callback = self.__callback self.__callback = None callback(self._instance_id)
def run(self): qDebug('Start called on RclpySpinner, spinning ros2 node') executor = MultiThreadedExecutor() executor.add_node(self._node) while rclpy.ok() and not self._abort: executor.spin_once(timeout_sec=1.0) if not self._abort: qWarning('rclpy.shutdown() was called before QThread.quit()')
def emit_restore_settings_completed(self): qDebug('PluginHandler.emit_restore_settings_completed()') # call after plugin has restored settings as it may spawn additional dock widgets self._call_method_on_all_dock_widgets('restore_settings', self.__instance_settings) self.__instance_settings = None if self.__callback is not None: callback = self.__callback self.__callback = None callback(self._instance_id)
def _restore_settings_restore_callback(self, instance_id=None): if instance_id is not None: self._number_of_ongoing_calls = self._number_of_ongoing_calls - 1 if self._number_of_ongoing_calls == 0: if instance_id is not None: qDebug('PluginManager.restore_settings() all plugin settings restored') self._number_of_ongoing_calls = None # restore state of top-level widgets self.plugins_changed_signal.emit()
def _save_settings_from_remote(self): qDebug('PluginHandlerXEmbedClient._save_settings_from_remote()') try: plugin_settings = Settings(self._remote_plugin_settings, '') instance_settings = Settings(self._remote_instance_settings, '') self._save_settings(plugin_settings, instance_settings) except Exception: qCritical('PluginHandlerXEmbedClient._save_settings_from_remote() plugin "%s" raised an exception:\n%s' % (str(self._instance_id), traceback.format_exc())) self.emit_save_settings_completed()
def _restore_settings_unload_obsolete_callback(self, instance_id=None): if instance_id is not None: self._number_of_ongoing_calls = self._number_of_ongoing_calls - 1 self._remove_running_plugin(instance_id) if self._number_of_ongoing_calls == 0: if instance_id is not None: qDebug('PluginManager.restore_settings() all obsolete plugins unloaded') self._number_of_ongoing_calls = None self._restore_settings_load_missing()
def load_bag(self, filename): qDebug("Loading '%s'..." % filename) self.set_status_text.emit("Loading '%s'..." % filename) try: bag = rosbag.Bag(filename) self.capture_button.setEnabled(True) except: print("Error.")
def _restore_settings_load_missing_callback(self, handler=None, exception=None): if handler is not None: self._number_of_ongoing_calls = self._number_of_ongoing_calls - 1 self._load_plugin_completed(handler, exception) if self._number_of_ongoing_calls == 0: if handler is not None: qDebug('PluginManager.restore_settings() all missing plugins loaded') self._number_of_ongoing_calls = None self._restore_settings_restore()
def closeEvent(self, event): qDebug('MainWindow.closeEvent()') if not self._save_on_close_signaled: self._save_geometry_to_perspective() self._save_state_to_perspective() self._save_on_close_signaled = True self.save_settings_before_close_signal.emit(self._global_settings, self._perspective_settings) event.ignore() else: event.accept()
def _init_node(self): # initialize node once if not self._node_initialized: name = 'rqt_gui_py_node_%d' % os.getpid() qDebug('RosPyPluginProvider._init_node() initialize ROS node "%s"' % name) if not rclpy.ok(): rclpy.init() self._node = rclpy.create_node(name) self._spinner = RclpySpinner(self._node) self._spinner.start() self._node_initialized = True
def unload_plugin(self, instance_id_str): # unloading a plugin with locked perspective or running standalone triggers close of application if self._application_context.options.lock_perspective or self._application_context.options.standalone_plugin: self.close_application_signal.emit() return instance_id = PluginInstanceId(instance_id=instance_id_str) qDebug('PluginManager.unload_plugin(%s)' % str(instance_id)) # save state of top-level widgets self.plugins_about_to_change_signal.emit() # save settings before shutdown and unloading self._save_plugin_settings(instance_id, self._unload_plugin_shutdown)
def unload_plugin(self, instance_id_str): # unloading a plugin with locked perspective or running standalone triggers close of application if self._application_context.options.lock_perspective or self._application_context.options.standalone_plugin: self._close_application_signal() return instance_id = PluginInstanceId(instance_id=instance_id_str) qDebug('PluginManager.unload_plugin(%s)' % str(instance_id)) # save state of top-level widgets self.plugins_about_to_change_signal.emit() # save settings before shutdown and unloading self._save_plugin_settings(instance_id, self._unload_plugin_shutdown)
def _save_settings(self, global_settings, perspective_settings, callback): qDebug('PluginManager.save_settings()') self._global_settings = global_settings.get_settings('pluginmanager') self._perspective_settings = perspective_settings.get_settings('pluginmanager') self._store_running_plugins() # trigger async call on all running plugins self._number_of_ongoing_calls = len(self._running_plugins) if self._number_of_ongoing_calls > 0: for info in self._running_plugins.values(): self._save_plugin_settings(info['instance_id'], callback) else: callback()
def update(self): """ Update all the stats. This method may take awhile to complete as it will communicate with all nodes + master. """ last_node_refresh = self.last_node_refresh # nodes left to check update_queue = None # True if there are still more stats to fetch this cycle work_to_do = True # return value. True if new data differs from old updated = False # number of nodes we checked num_nodes = 0 start_time = time.time() while work_to_do: # each time through the loop try to talk to either the master # or a node. stop when we have talked to everybody. # get a new node list from the master if time.time() > (self.last_graph_refresh + self.graph_stale): updated = self._graph_refresh() self.last_graph_refresh = time.time() # contact the nodes for stats else: # initialize update_queue based on most current nodes list if update_queue is None: update_queue = list(self.nn_nodes) # no nodes left to contact elif not update_queue: work_to_do = False # contact next node else: # figure out the next node to contact next = update_queue.pop() # rate limit talking to any particular node if time.time() > (last_node_refresh.get(next, 0.0) + self.node_stale): updated = self._node_refresh(next) or updated # include in random offset (max 1/5th normal update interval) # to help spread out updates last_node_refresh[next] = \ time.time() + (random.random() * self.node_stale / 5.0) num_nodes += 1 # small yield to keep from torquing the processor time.sleep(0.01) end_time = time.time() qDebug("ROS stats update took %ss" % (end_time - start_time)) return updated
def event(self, e): if e.type() == ReparentEvent.reparent_event_type: qDebug('ContainerManager.event() reparent event: new parent=%s' % e.new_parent.objectName()) floating = e.dock_widget.isFloating() pos = e.dock_widget.pos() e.new_parent.addDockWidget(Qt.BottomDockWidgetArea, e.dock_widget) if floating: e.dock_widget.setFloating(floating) e.dock_widget.move(pos) e.accept() return True return super(ContainerManager, self).event(e)
def restore_settings(self, plugin_settings, instance_settings, callback=None): """ Restore settings of the plugin (`Plugin.restore_settings()`) and all dock widget title bars. Completion is signaled asynchronously if a callback is passed. """ qDebug('PluginHandler.restore_settings()') self.__instance_settings = instance_settings self.__callback = callback try: self._restore_settings(plugin_settings, instance_settings) except Exception: qCritical('PluginHandler.restore_settings() plugin "%s" raised an exception:\n%s' % (str(self._instance_id), traceback.format_exc())) self.emit_restore_settings_completed()
def _load(self): if not Main.main_filename: raise RuntimeError( 'PluginHandlerXEmbedContainer._load() filename of initially started script is unknown' ) self._dbus_server = Server('tcp:bind=*') self._dbus_server.on_connection_added.append(self._add_dbus_connection) self._dbus_container_service = PluginHandlerDBusService( self, self._dbus_object_path) self._dbus_plugin_settings_service = SettingsProxyDBusService( self._dbus_object_path + '/plugin') self._dbus_instance_settings_service = SettingsProxyDBusService( self._dbus_object_path + '/instance') self._process = QProcess(self) self._process.setProcessChannelMode(QProcess.SeparateChannels) self._process.readyReadStandardOutput.connect( self._print_process_output) self._process.readyReadStandardError.connect(self._print_process_error) self._process.finished.connect(self._emit_close_plugin) # start python with unbuffered stdout/stderr so that the order of the output is retained cmd = sys.executable + ' -u' cmd += ' %s' % Main.main_filename cmd += ' --qt-binding=%s' % QT_BINDING cmd += ' --embed-plugin=%s --embed-plugin-serial=%s --embed-plugin-address=%s' % ( self.instance_id().plugin_id, self.instance_id().serial_number, self._dbus_server.address) if self.argv(): cmd += ' --args %s' % ' '.join(self.argv()) #qDebug('PluginHandlerXEmbedContainer._load() starting command: %s' % cmd) self._process.start(cmd) started = self._process.waitForStarted(3000) if not started: self._dbus_container_service.remove_from_connection() self._dbus_plugin_settings_service.remove_from_connection() self._dbus_instance_settings_service.remove_from_connection() raise RuntimeError( 'PluginHandlerXEmbedContainer._load() could not start subprocess in reasonable time' ) # QProcess.pid() has been added to PySide in 1.0.5 if hasattr(self._process, 'pid'): self._pid = self._process.pid() else: # use serial number as a replacement for pid if not available self.__class__._serial_number = self._serial_number + 1 self._pid = self._serial_number qDebug( 'PluginHandlerXEmbedContainer._load() started subprocess (#%s) for plugin "%s"' % (self._pid, str(self._instance_id)))
def start_plugin(self, plugin_name, argv): qDebug('PluginManagerDBusInterface.start_plugin(%s)' % plugin_name) plugins = self._plugin_manager.find_plugins_by_name(plugin_name) if len(plugins) == 0: msg = 'PluginManagerDBusInterface.start_plugin() found no plugin matching "%s"' % plugin_name qWarning(msg) return (1, msg) elif len(plugins) > 1: msg = 'PluginManagerDBusInterface.start_plugin() found multiple plugins matching "%s"\n%s' % (plugin_name, '\n'.join(plugins.values())) qWarning(msg) return (1, msg) plugin_id = plugins.keys()[0] self._plugin_manager.load_plugin(plugin_id, argv=argv.split(' ') if argv else []) return (0, plugin_id)
def _restore_settings_save_obsolete(self): # trigger shutdown of obsolete plugins plugins = self._restore_running_plugins_get_plugins() obsolete = [] for instance_id in self._running_plugins.keys(): if instance_id not in plugins.keys(): obsolete.append(PluginInstanceId(instance_id=instance_id)) self._number_of_ongoing_calls = len(obsolete) if self._number_of_ongoing_calls > 0: qDebug('PluginManager.restore_settings() unloading %d obsolete plugins' % self._number_of_ongoing_calls) for instance_id in obsolete: self._shutdown_plugin(instance_id, self._restore_settings_unload_obsolete) else: self._restore_settings_unload_obsolete_callback()
def _restore_settings_load_missing(self): # trigger_load of not yet loaded plugins plugins = self._restore_running_plugins_get_plugins() loading = [] for instance_id_str, instance_id in plugins.items(): if instance_id_str not in self._running_plugins.keys(): loading.append(instance_id) self._number_of_ongoing_calls = len(loading) if self._number_of_ongoing_calls > 0: qDebug('PluginManager.restore_settings() loading %d plugins' % self._number_of_ongoing_calls) for instance_id in loading: self._load_plugin_load(instance_id, self._restore_settings_load_missing_callback) else: self._restore_settings_load_missing_callback()
def _load_plugin_completed(self, handler, exception): instance_id = handler.instance_id() if exception is not None: if isinstance(exception, PluginLoadError): qWarning('PluginManager._load_plugin() could not load plugin "%s": %s' % (instance_id.plugin_id, exception)) else: qCritical('PluginManager._load_plugin() could not load plugin "%s"%s' % (instance_id.plugin_id, (':\n%s' % traceback.format_exc() if exception != True else ''))) self._remove_running_plugin(instance_id) # quit embed application if self._application_context.options.embed_plugin: exit(-1) return qDebug('PluginManager._load_plugin(%s) successful' % str(instance_id)) handler.close_signal.connect(self.unload_plugin) handler.reload_signal.connect(self.reload_plugin) handler.help_signal.connect(self._emit_plugin_help_signal)
def _create_perspective(self, name, clone_perspective=True): # convert from unicode name = str(name) if name.find('/') != -1: raise RuntimeError('PerspectiveManager._create_perspective() name must not contain forward slashs (/)') qDebug('PerspectiveManager._create_perspective(%s, %s)' % (name, clone_perspective)) # add to list of perspectives self.perspectives.append(name) self._settings_proxy.set_value('', 'perspectives', self.perspectives) # save current settings if self._global_settings is not None and self._perspective_settings is not None: self._callback = self._create_perspective_continued self._callback_args = [name, clone_perspective] self.save_settings_signal.emit(self._global_settings, self._perspective_settings) else: self._create_perspective_continued(name, clone_perspective)
def _remove_perspective(self, name): if name not in self.perspectives: raise UserWarning('unknown perspective: %s' % name) qDebug('PerspectiveManager._remove_perspective(%s)' % str(name)) # remove from list of perspectives self.perspectives.remove(name) self._settings_proxy.set_value('', 'perspectives', self.perspectives) # remove settings settings = self._get_perspective_settings(name) settings.remove('') # remove from menu self._menu_manager.remove_item(name) # disable remove-action if self._menu_manager.count_items() < 2: self._remove_action.setEnabled(False)
def load_plugins(self): from rqt_gui.rospkg_plugin_provider import RospkgPluginProvider self.plugin_provider = RospkgPluginProvider('rqt_bag', 'rqt_bag::Plugin') plugin_descriptors = self.plugin_provider.discover(None) for plugin_descriptor in plugin_descriptors: try: plugin = self.plugin_provider.load(plugin_descriptor.plugin_id(), plugin_context=None) except Exception as e: qWarning('rqt_bag.TimelineFrame.load_plugins() failed to load plugin "%s":\n%s' % (plugin_descriptor.plugin_id(), e)) continue try: view = plugin.get_view_class() except Exception as e: qWarning('rqt_bag.TimelineFrame.load_plugins() failed to get view from plugin "%s":\n%s' % (plugin_descriptor.plugin_id(), e)) continue timeline_renderer = None try: timeline_renderer = plugin.get_renderer_class() except AttributeError: pass except Exception as e: qWarning('rqt_bag.TimelineFrame.load_plugins() failed to get renderer from plugin "%s":\n%s' % (plugin_descriptor.plugin_id(), e)) msg_types = [] try: msg_types = plugin.get_message_types() except AttributeError: pass except Exception as e: qWarning('rqt_bag.TimelineFrame.load_plugins() failed to get message types from plugin "%s":\n%s' % (plugin_descriptor.plugin_id(), e)) finally: if not msg_types: qWarning('rqt_bag.TimelineFrame.load_plugins() plugin "%s" declares no message types:\n%s' % (plugin_descriptor.plugin_id(), e)) for msg_type in msg_types: self._viewer_types.setdefault(msg_type, []).append(view) if timeline_renderer: self._timeline_renderers[msg_type] = timeline_renderer(self) qDebug('rqt_bag.TimelineFrame.load_plugins() loaded plugin "%s"' % plugin_descriptor.plugin_id())
def _load(self): if not Main.main_filename: raise RuntimeError('PluginHandlerXEmbedContainer._load() filename of initially started script is unknown') self._dbus_server = Server('tcp:bind=*') self._dbus_server.on_connection_added.append(self._add_dbus_connection) self._dbus_container_service = PluginHandlerDBusService(self, self._dbus_object_path) self._dbus_plugin_settings_service = SettingsProxyDBusService(self._dbus_object_path + '/plugin') self._dbus_instance_settings_service = SettingsProxyDBusService(self._dbus_object_path + '/instance') self._process = QProcess(self) self._process.setProcessChannelMode(QProcess.SeparateChannels) self._process.readyReadStandardOutput.connect(self._print_process_output) self._process.readyReadStandardError.connect(self._print_process_error) self._process.finished.connect(self._emit_close_plugin) # start python with unbuffered stdout/stderr so that the order of the output is retained cmd = sys.executable + ' -u' cmd += ' %s' % Main.main_filename cmd += ' --qt-binding=%s' % QT_BINDING cmd += ' --embed-plugin=%s --embed-plugin-serial=%s --embed-plugin-address=%s' % (self.instance_id().plugin_id, self.instance_id().serial_number, self._dbus_server.address) if self.argv(): cmd += ' --args %s' % ' '.join(self.argv()) #qDebug('PluginHandlerXEmbedContainer._load() starting command: %s' % cmd) self._process.start(cmd) started = self._process.waitForStarted(3000) if not started: self._dbus_container_service.remove_from_connection() self._dbus_plugin_settings_service.remove_from_connection() self._dbus_instance_settings_service.remove_from_connection() raise RuntimeError('PluginHandlerXEmbedContainer._load() could not start subprocess in reasonable time') # QProcess.pid() has been added to PySide in 1.0.5 if hasattr(self._process, 'pid'): self._pid = self._process.pid() else: # use serial number as a replacement for pid if not available self.__class__._serial_number = self._serial_number + 1 self._pid = self._serial_number qDebug('PluginHandlerXEmbedContainer._load() started subprocess (#%s) for plugin "%s"' % (self._pid, str(self._instance_id)))
def get_field_type(topic_name): """ Get the Python type of a specific field in the given registered topic. If the field is an array, the type of the array's values are returned and the is_array flag is set to True. This is a static type check, so it works for unpublished topics and with empty arrays. :param topic_name: name of field of a registered topic, ``str``, i.e. '/rosout/file' :returns: field_type, is_array """ # get topic_type and message_evaluator topic_type, real_topic_name, _ = get_topic_type(topic_name) if topic_type is None: #qDebug('topic_helpers.get_field_type(%s): get_topic_type failed' % (topic_name)) return None, False message_class = roslib.message.get_message_class(topic_type) if message_class is None: qDebug('topic_helpers.get_field_type(%s): get_message_class(%s) failed' % (topic_name, topic_type)) return None, False slot_path = topic_name[len(real_topic_name):] return get_slot_type(message_class, slot_path)
def _discover(self): discovery_data = self._settings.get_settings('discovery_data') # check timestamp of the cached discovery data cache_stamp = self._settings.value('discovery_timestamp', default_value=None) if cache_stamp: cache_stamp = float(cache_stamp) # wipe cached data when forcing discovery or when cache is to old (or from the future) now = time.time() if self._application_context.options.force_discover or not cache_stamp or cache_stamp > now or cache_stamp + PluginManager.discovery_cache_max_age < now: qDebug('PluginManager._discover() force discovery of plugins') cache_stamp = None for k in discovery_data.all_keys(): discovery_data.remove(k) else: qDebug('PluginManager._discover() using cached plugin discovery information') plugin_descriptors = self._plugin_provider.discover(discovery_data) # store timestamp and newly discovered data if not cache_stamp: self._settings.set_value('discovery_timestamp', now) return plugin_descriptors
def sigint_handler(*args): qDebug('\nsigint_handler()') main_window.close()
def main(self, argv=None, standalone=None, plugin_argument_provider=None, plugin_manager_settings_prefix=''): if argv is None: argv = sys.argv # extract --args and everything behind manually since argparse can not handle that arguments = argv[1:] # extract plugin specific args when not being invoked in standalone mode programmatically if not standalone: plugin_args = [] if '--args' in arguments: index = arguments.index('--args') plugin_args = arguments[index + 1:] arguments = arguments[0:index + 1] parser = ArgumentParser(os.path.basename(Main.main_filename), add_help=False) self.add_arguments(parser, standalone=bool(standalone), plugin_argument_provider=plugin_argument_provider) self._options = parser.parse_args(arguments) if standalone: # rerun parsing to separate common arguments from plugin specific arguments parser = ArgumentParser(os.path.basename(Main.main_filename), add_help=False) self.add_arguments(parser, standalone=bool(standalone)) self._options, plugin_args = parser.parse_known_args(arguments) self._options.plugin_args = plugin_args # set default values for options not available in standalone mode if standalone: self._options.freeze_layout = False self._options.lock_perspective = False self._options.multi_process = False self._options.perspective = None self._options.perspective_file = None self._options.standalone_plugin = standalone self._options.list_perspectives = False self._options.list_plugins = False self._options.command_pid = None self._options.command_start_plugin = None self._options.command_switch_perspective = None self._options.embed_plugin = None self._options.embed_plugin_serial = None self._options.embed_plugin_address = None # check option dependencies try: if self._options.plugin_args and not self._options.standalone_plugin and not self._options.command_start_plugin and not self._options.embed_plugin: raise RuntimeError('Option --args can only be used together with either --standalone, --command-start-plugin or --embed-plugin option') if self._options.freeze_layout and not self._options.lock_perspective: raise RuntimeError('Option --freeze_layout can only be used together with the --lock_perspective option') list_options = (self._options.list_perspectives, self._options.list_plugins) list_options_set = [opt for opt in list_options if opt is not False] if len(list_options_set) > 1: raise RuntimeError('Only one --list-* option can be used at a time') command_options = (self._options.command_start_plugin, self._options.command_switch_perspective) command_options_set = [opt for opt in command_options if opt is not None] if len(command_options_set) > 0 and not self._dbus_available: raise RuntimeError('Without DBus support the --command-* options are not available') if len(command_options_set) > 1: raise RuntimeError('Only one --command-* option can be used at a time (except --command-pid which is optional)') if len(command_options_set) == 0 and self._options.command_pid is not None: raise RuntimeError('Option --command_pid can only be used together with an other --command-* option') embed_options = (self._options.embed_plugin, self._options.embed_plugin_serial, self._options.embed_plugin_address) embed_options_set = [opt for opt in embed_options if opt is not None] if len(command_options_set) > 0 and not self._dbus_available: raise RuntimeError('Without DBus support the --embed-* options are not available') if len(embed_options_set) > 0 and len(embed_options_set) < len(embed_options): raise RuntimeError('Missing option(s) - all \'--embed-*\' options must be set') if len(embed_options_set) > 0 and self._options.clear_config: raise RuntimeError('Option --clear-config can only be used without any --embed-* option') groups = (list_options_set, command_options_set, embed_options_set) groups_set = [opt for opt in groups if len(opt) > 0] if len(groups_set) > 1: raise RuntimeError('Options from different groups (--list, --command, --embed) can not be used together') perspective_options = (self._options.perspective, self._options.perspective_file) perspective_options_set = [opt for opt in perspective_options if opt is not None] if len(perspective_options_set) > 1: raise RuntimeError('Only one --perspective-* option can be used at a time') if self._options.perspective_file is not None and not os.path.isfile(self._options.perspective_file): raise RuntimeError('Option --perspective-file must reference existing file') except RuntimeError as e: print(str(e)) #parser.parse_args(['--help']) # calling --help will exit return 1 # set implicit option dependencies if self._options.standalone_plugin is not None: self._options.lock_perspective = True # create application context containing various relevant information from .application_context import ApplicationContext context = ApplicationContext() context.qtgui_path = self._qtgui_path context.options = self._options if self._dbus_available: from dbus import DBusException, Interface, SessionBus # non-special applications provide various dbus interfaces if self._dbus_available: context.provide_app_dbus_interfaces = len(groups_set) == 0 context.dbus_base_bus_name = 'org.ros.qt_gui' if context.provide_app_dbus_interfaces: context.dbus_unique_bus_name = context.dbus_base_bus_name + '.pid%d' % os.getpid() # provide pid of application via dbus from .application_dbus_interface import ApplicationDBusInterface _dbus_server = ApplicationDBusInterface(context.dbus_base_bus_name) # determine host bus name, either based on pid given on command line or via dbus application interface if any other instance is available if len(command_options_set) > 0 or len(embed_options_set) > 0: host_pid = None if self._options.command_pid is not None: host_pid = self._options.command_pid else: try: remote_object = SessionBus().get_object(context.dbus_base_bus_name, '/Application') except DBusException: pass else: remote_interface = Interface(remote_object, context.dbus_base_bus_name + '.Application') host_pid = remote_interface.get_pid() if host_pid is not None: context.dbus_host_bus_name = context.dbus_base_bus_name + '.pid%d' % host_pid # execute command on host application instance if len(command_options_set) > 0: if self._options.command_start_plugin is not None: try: remote_object = SessionBus().get_object(context.dbus_host_bus_name, '/PluginManager') except DBusException: (rc, msg) = (1, 'unable to communicate with GUI instance "%s"' % context.dbus_host_bus_name) else: remote_interface = Interface(remote_object, context.dbus_base_bus_name + '.PluginManager') (rc, msg) = remote_interface.start_plugin(self._options.command_start_plugin, ' '.join(self._options.plugin_args)) if rc == 0: print('qt_gui_main() started plugin "%s" in GUI "%s"' % (msg, context.dbus_host_bus_name)) else: print('qt_gui_main() could not start plugin "%s" in GUI "%s": %s' % (self._options.command_start_plugin, context.dbus_host_bus_name, msg)) return rc elif self._options.command_switch_perspective is not None: remote_object = SessionBus().get_object(context.dbus_host_bus_name, '/PerspectiveManager') remote_interface = Interface(remote_object, context.dbus_base_bus_name + '.PerspectiveManager') remote_interface.switch_perspective(self._options.command_switch_perspective) print('qt_gui_main() switched to perspective "%s" in GUI "%s"' % (self._options.command_switch_perspective, context.dbus_host_bus_name)) return 0 raise RuntimeError('Unknown command not handled') # choose selected or default qt binding setattr(sys, 'SELECT_QT_BINDING', self._options.qt_binding) from python_qt_binding import QT_BINDING from python_qt_binding.QtCore import qDebug, qInstallMsgHandler, QSettings, Qt, QtCriticalMsg, QtDebugMsg, QtFatalMsg, QTimer, QtWarningMsg from python_qt_binding.QtGui import QAction, QIcon, QMenuBar from .about_handler import AboutHandler from .composite_plugin_provider import CompositePluginProvider from .container_manager import ContainerManager from .help_provider import HelpProvider from .icon_loader import get_icon from .main_window import MainWindow from .minimized_dock_widgets_toolbar import MinimizedDockWidgetsToolbar from .perspective_manager import PerspectiveManager from .plugin_manager import PluginManager def message_handler(type_, msg): colored_output = 'TERM' in os.environ and 'ANSI_COLORS_DISABLED' not in os.environ cyan_color = '\033[36m' if colored_output else '' red_color = '\033[31m' if colored_output else '' reset_color = '\033[0m' if colored_output else '' if type_ == QtDebugMsg and self._options.verbose: print(msg, file=sys.stderr) elif type_ == QtWarningMsg: print(cyan_color + msg + reset_color, file=sys.stderr) elif type_ == QtCriticalMsg: print(red_color + msg + reset_color, file=sys.stderr) elif type_ == QtFatalMsg: print(red_color + msg + reset_color, file=sys.stderr) sys.exit(1) qInstallMsgHandler(message_handler) app = self.create_application(argv) self._check_icon_theme_compliance() settings = QSettings(QSettings.IniFormat, QSettings.UserScope, 'ros.org', self._settings_filename) if len(embed_options_set) == 0: if self._options.clear_config: settings.clear() main_window = MainWindow() if self._options.on_top: main_window.setWindowFlags(Qt.WindowStaysOnTopHint) main_window.statusBar() def sigint_handler(*args): qDebug('\nsigint_handler()') main_window.close() signal.signal(signal.SIGINT, sigint_handler) # the timer enables triggering the sigint_handler timer = QTimer() timer.start(500) timer.timeout.connect(lambda: None) # create own menu bar to share one menu bar on Mac menu_bar = QMenuBar() if 'darwin' in platform.platform().lower(): menu_bar.setNativeMenuBar(True) else: menu_bar.setNativeMenuBar(False) if not self._options.lock_perspective: main_window.setMenuBar(menu_bar) file_menu = menu_bar.addMenu(menu_bar.tr('&File')) action = QAction(file_menu.tr('&Quit'), file_menu) action.setIcon(QIcon.fromTheme('application-exit')) action.triggered.connect(main_window.close) file_menu.addAction(action) else: app.setQuitOnLastWindowClosed(False) main_window = None menu_bar = None self._add_plugin_providers() # setup plugin manager plugin_provider = CompositePluginProvider(self.plugin_providers) plugin_manager = PluginManager(plugin_provider, settings, context, settings_prefix=plugin_manager_settings_prefix) if self._options.list_plugins: # output available plugins print('\n'.join(sorted(plugin_manager.get_plugins().values()))) return 0 help_provider = HelpProvider() plugin_manager.plugin_help_signal.connect(help_provider.plugin_help_request) # setup perspective manager if main_window is not None: perspective_manager = PerspectiveManager(settings, context) if self._options.list_perspectives: # output available perspectives print('\n'.join(sorted(perspective_manager.perspectives))) return 0 else: perspective_manager = None if main_window is not None: container_manager = ContainerManager(main_window, plugin_manager) plugin_manager.set_main_window(main_window, menu_bar, container_manager) if not self._options.freeze_layout: minimized_dock_widgets_toolbar = MinimizedDockWidgetsToolbar(container_manager, main_window) main_window.addToolBar(Qt.BottomToolBarArea, minimized_dock_widgets_toolbar) plugin_manager.set_minimized_dock_widgets_toolbar(minimized_dock_widgets_toolbar) if menu_bar is not None: perspective_menu = menu_bar.addMenu(menu_bar.tr('P&erspectives')) perspective_manager.set_menu(perspective_menu) # connect various signals and slots if perspective_manager is not None and main_window is not None: # signal changed perspective to update window title perspective_manager.perspective_changed_signal.connect(main_window.perspective_changed) # signal new settings due to changed perspective perspective_manager.save_settings_signal.connect(main_window.save_settings) perspective_manager.restore_settings_signal.connect(main_window.restore_settings) perspective_manager.restore_settings_without_plugin_changes_signal.connect(main_window.restore_settings) if perspective_manager is not None and plugin_manager is not None: perspective_manager.save_settings_signal.connect(plugin_manager.save_settings) plugin_manager.save_settings_completed_signal.connect(perspective_manager.save_settings_completed) perspective_manager.restore_settings_signal.connect(plugin_manager.restore_settings) perspective_manager.restore_settings_without_plugin_changes_signal.connect(plugin_manager.restore_settings_without_plugins) if plugin_manager is not None and main_window is not None: # signal before changing plugins to save window state plugin_manager.plugins_about_to_change_signal.connect(main_window.save_setup) # signal changed plugins to restore window state plugin_manager.plugins_changed_signal.connect(main_window.restore_state) # signal save settings to store plugin setup on close main_window.save_settings_before_close_signal.connect(plugin_manager.close_application) # signal save and shutdown called for all plugins, trigger closing main window again plugin_manager.close_application_signal.connect(main_window.close, type=Qt.QueuedConnection) if main_window is not None and menu_bar is not None: about_handler = AboutHandler(context.qtgui_path, main_window) help_menu = menu_bar.addMenu(menu_bar.tr('&Help')) action = QAction(file_menu.tr('&About'), help_menu) action.setIcon(QIcon.fromTheme('help-about')) action.triggered.connect(about_handler.show) help_menu.addAction(action) # set initial size - only used without saved configuration if main_window is not None: main_window.resize(600, 450) main_window.move(100, 100) # ensure that qt_gui/src is in sys.path src_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..')) if src_path not in sys.path: sys.path.append(src_path) # load specific plugin plugin = None plugin_serial = None if self._options.embed_plugin is not None: plugin = self._options.embed_plugin plugin_serial = self._options.embed_plugin_serial elif self._options.standalone_plugin is not None: plugin = self._options.standalone_plugin plugin_serial = 0 if plugin is not None: plugins = plugin_manager.find_plugins_by_name(plugin) if len(plugins) == 0: print('qt_gui_main() found no plugin matching "%s"' % plugin) return 1 elif len(plugins) > 1: print('qt_gui_main() found multiple plugins matching "%s"\n%s' % (plugin, '\n'.join(plugins.values()))) return 1 plugin = plugins.keys()[0] qDebug('QtBindingHelper using %s' % QT_BINDING) plugin_manager.discover() if self._options.reload_import: qDebug('ReloadImporter() automatically reload all subsequent imports') from .reload_importer import ReloadImporter _reload_importer = ReloadImporter() self._add_reload_paths(_reload_importer) _reload_importer.enable() # switch perspective if perspective_manager is not None: if plugin: perspective_manager.set_perspective(plugin, hide_and_without_plugin_changes=True) elif self._options.perspective_file: perspective_manager.import_perspective_from_file(self._options.perspective_file, perspective_manager.HIDDEN_PREFIX + '__cli_perspective_from_file') else: perspective_manager.set_perspective(self._options.perspective) # load specific plugin if plugin: plugin_manager.load_plugin(plugin, plugin_serial, self._options.plugin_args) running = plugin_manager.is_plugin_running(plugin, plugin_serial) if not running: return 1 if self._options.standalone_plugin: # use icon of standalone plugin (if available) for application plugin_descriptor = plugin_manager.get_plugin_descriptor(plugin) action_attributes = plugin_descriptor.action_attributes() if 'icon' in action_attributes and action_attributes['icon'] is not None: base_path = plugin_descriptor.attributes().get('plugin_path') try: icon = get_icon(action_attributes['icon'], action_attributes.get('icontype', None), base_path) except UserWarning: pass else: app.setWindowIcon(icon) if main_window is not None: main_window.show() if sys.platform == 'darwin': main_window.raise_() return app.exec_()
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. import numpy from qt_gui_py_common.simple_settings_dialog import SimpleSettingsDialog from python_qt_binding.QtCore import Qt, qDebug, qWarning, Signal from python_qt_binding.QtGui import QColor, QWidget, QHBoxLayout try: from pyqtgraph_data_plot import PyQtGraphDataPlot except ImportError: qDebug('[DEBUG] rqt_plot.plot: import of PyQtGraphDataPlot failed (trying other backends)') PyQtGraphDataPlot = None try: from mat_data_plot import MatDataPlot except ImportError: qDebug('[DEBUG] rqt_plot.plot: import of MatDataPlot failed (trying other backends)') MatDataPlot = None try: from qwt_data_plot import QwtDataPlot except ImportError: qDebug('[DEBUG] rqt_plot.plot: import of QwtDataPlot failed (trying other backends)') QwtDataPlot = None # separate class for DataPlot exceptions, just so that users can differentiate