class QtRappManager(Plugin): _update_rapps_signal = Signal() def __init__(self, context): self._context = context super(QtRappManager, self).__init__(context) self._init_ui(context) self._init_events() self._init_variables() self.spin() def _init_ui(self, context): self._widget = QWidget() self.setObjectName('QtRappManger') rospack = rospkg.RosPack() ui_file = os.path.join(rospack.get_path('rocon_qt_app_manager'), 'ui', 'qt_rapp_manager.ui') self._widget.setObjectName('QtRappManger') loadUi(ui_file, self._widget, {}) if context.serial_number() > 1: self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % context.serial_number())) context.add_widget(self._widget) # Set view mode self._widget.rapp_grid.setViewMode(QListView.IconMode) # self._widget.rapp_grid.setViewMode(QListView.ListMode) def _init_events(self): # combo box change event self._widget.namespace_cbox.currentIndexChanged.connect( self._change_namespace) # Rapp single click event self._widget.rapp_grid.clicked.connect(self._rapp_single_click) # Rapp double click event self._widget.rapp_grid.doubleClicked.connect(self._rapp_double_click) def _init_variables(self): self.initialised = False # init self._qt_rapp_manager_info = QtRappManagerInfo(self._refresh_rapps) self._update_rapps_signal.connect(self._update_rapp_list) self._rapp_view_model = QStandardItemModel() self._widget.rapp_grid.setModel(self._rapp_view_model) self._widget.rapp_grid.setWrapping(True) self._widget.rapp_grid.setIconSize(QSize(60, 60)) self._widget.rapp_grid.setSpacing(10) self._selected_rapp = None def spin(self): self._get_appmanager_namespaces() def _cleanup_rapps(self): self._rapp_view_model.clear() def _get_appmanager_namespaces(self): namespaces = self._qt_rapp_manager_info._get_namespaces() for namespace in namespaces: ns = namespace[:namespace.find('list_rapps')] self._widget.namespace_cbox.addItem(ns) def _exit(self): pass ################################################################### # Events ################################################################### def _change_namespace(self, event): self._qt_rapp_manager_info.select_rapp_manager( self._widget.namespace_cbox.currentText()) def _refresh_rapps(self): """ Updates from qt_app_manager_info. """ self._update_rapps_signal.emit() def _update_rapp_list(self): """ Rapp manager namespace event """ self._cleanup_rapps() available_rapps = self._qt_rapp_manager_info.get_available_rapps() running_rapps = self._qt_rapp_manager_info.get_running_rapps() for r, v in available_rapps.items(): if r in running_rapps.keys(): item = QRappItem(v, running=True) else: item = QRappItem(v, running=False) self._rapp_view_model.appendRow(item) def _rapp_single_click(self, index): qrapp = self._rapp_view_model.item(index.row()) rapp = qrapp.getRapp() self._create_rapp_dialog(rapp) def _create_rapp_dialog(self, rapp): is_running = self._qt_rapp_manager_info.is_running_rapp(rapp) self._selected_rapp = rapp self._dialog = QtRappDialog(self._widget, rapp, self._qt_rapp_manager_info.start_rapp, self._qt_rapp_manager_info.stop_rapp, is_running) self._dialog.show() def _rapp_double_click(self, item): running_rapps = self._qt_rapp_manager_info.get_running_rapps() if len(running_rapps) > 0: names = [r['display_name'] for r in running_rapps.values()] show_message(self._widget, "Error", "Rapp %s are already running" % names) else: self._start_rapp() def _start_rapp(self): result = self._qt_rapp_manager_info.start_rapp( self._selected_rapp['name'], [], self._selected_rapp['public_parameters']) show_message(self._widget, str(result.started), result.message) self._selected_rapp = None return result def _stop_rapp(self): result = self._qt_rapp_manager_info.stop_rapp() show_message(self._widget, str(result.stopped), result.message) self._selected_rapp = None return result
class QtRappManager(Plugin): _update_rapps_signal = Signal() def __init__(self, context): self._context = context super(QtRappManager, self).__init__(context) self._init_ui(context) self._init_events() self._init_variables() self.spin() def _init_ui(self, context): self._widget = QWidget() self.setObjectName('QtRappManger') rospack = rospkg.RosPack() ui_file = os.path.join(rospack.get_path('rocon_qt_app_manager'), 'ui', 'qt_rapp_manager.ui') self._widget.setObjectName('QtRappManger') loadUi(ui_file, self._widget, {}) if context.serial_number() > 1: self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % context.serial_number())) context.add_widget(self._widget) # Set view mode self._widget.rapp_grid.setViewMode(QListView.IconMode) #self._widget.rapp_grid.setViewMode(QListView.ListMode) def _init_events(self): #combo box change event self._widget.namespace_cbox.currentIndexChanged.connect( self._change_namespace) # Rapp single click event self._widget.rapp_grid.clicked.connect(self._rapp_single_click) # Rapp double click event self._widget.rapp_grid.doubleClicked.connect(self._rapp_double_click) def _init_variables(self): self.initialised = False #init self._qt_rapp_manager_info = QtRappManagerInfo(self._refresh_rapps) self._update_rapps_signal.connect(self._update_rapp_list) self._rapp_view_model = QStandardItemModel() self._widget.rapp_grid.setModel(self._rapp_view_model) self._widget.rapp_grid.setWrapping(True) self._widget.rapp_grid.setIconSize(QSize(60, 60)) self._widget.rapp_grid.setSpacing(10) self._selected_rapp = None def spin(self): self._get_appmanager_namespaces() def _cleanup_rapps(self): self._rapp_view_model.clear() pass def _get_appmanager_namespaces(self): namespaces = self._qt_rapp_manager_info._get_namespaces() for namespace in namespaces: ns = namespace[:namespace.find('list_rapps')] self._widget.namespace_cbox.addItem(ns) def _exit(self): pass ################################################################### # Events ################################################################### def _change_namespace(self, event): self._qt_rapp_manager_info.select_rapp_manager( self._widget.namespace_cbox.currentText()) def _refresh_rapps(self): """ Updates from qt_app_manager_info. """ self._update_rapps_signal.emit() def _update_rapp_list(self): """ Rapp manager namespace event """ self._cleanup_rapps() available_rapps = self._qt_rapp_manager_info.get_available_rapps() running_rapps = self._qt_rapp_manager_info.get_running_rapps() rapp_items = [] for r, v in available_rapps.items(): if r in running_rapps.keys(): item = QRappItem(v, running=True) else: item = QRappItem(v, running=False) self._rapp_view_model.appendRow(item) def _rapp_single_click(self, index): qrapp = self._rapp_view_model.item(index.row()) rapp = qrapp.getRapp() self._create_rapp_dialog(rapp) def _create_rapp_dialog(self, rapp): is_running = self._qt_rapp_manager_info.is_running_rapp(rapp) self._selected_rapp = rapp self._dialog = QtRappDialog(self._widget, rapp, self._qt_rapp_manager_info.start_rapp, self._qt_rapp_manager_info.stop_rapp, is_running) self._dialog.show() def _rapp_double_click(self, item): running_rapps = self._qt_rapp_manager_info.get_running_rapps() if len(running_rapps) > 0: names = [r['display_name'] for r in running_rapps.values()] show_message(self._widget, "Error", "Rapp %s are already running" % names) else: self._start_rapp() def _start_rapp(self): result = self._qt_rapp_manager_info.start_rapp( self._selected_rapp['name'], [], self._selected_rapp['public_parameters']) show_message(self._widget, str(result.started), result.message) self._selected_rapp = None return result def _stop_rapp(self): result = self._qt_rapp_manager_info.stop_rapp() show_message(self._widget, str(result.stopped), result.message) self._selected_rapp = None return result ######################################## # Legacy ######################################## def _select_rapp_tree_item(self, item): if not item in self.rapps.keys(): print "HAS NO KEY" else: self.current_rapp = self.rapps[item] self._widget.rapp_info_text.clear() rapp_info = self.qt_rapp_manager_info._get_rapp_info(self.current_rapp) self._widget.rapp_info_text.appendHtml(rapp_info) self._update_rapp_parameter_layout(self.current_rapp) self._update_implementation_tree(self.current_rapp) def _update_rapp_parameter_layout(self, rapp): parameters_layout = self._widget.rapp_parameter_layout clear_layout(parameters_layout) for param in rapp['public_parameters']: one_param_layout = create_label_textedit_pair( param.key, param.value) parameters_layout.addLayout(one_param_layout) def _update_implementation_tree(self, rapp): self._widget.implementation_tree_widget.clear() self.selected_impl = None self.impls = {} for impl in rapp['implementations']: impl_item = QTreeWidgetItem( self._widget.implementation_tree_widget) impl_item.setText(0, impl) self.impls[impl_item] = impl def _select_implementation_tree_item(self, item): if not item in self.impls.keys(): print "HAS NO KEY" else: self.selected_impl = self.impls[item] def _get_public_parameters(self): public_parameters = {} parameters_layout = self._widget.rapp_parameter_layout for i in reversed(range(parameters_layout.count())): item = parameters_layout.itemAt(i) key_label = item.itemAt(0).widget() value_textbox = item.itemAt(1).widget() public_parameters[key_label.text()] = str( value_textbox.toPlainText()) return public_parameters
class Conman(Plugin): update_graph_sig = Signal(str) def __init__(self, context): super(Conman, self).__init__(context) self._dotcode_sub = None self._topic_dict = {} self._update_thread = WorkerThread(self._update_thread_run, self._update_finished) # Give QObjects reasonable names self.setObjectName('Conman') # 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 is a sibling of this file # in this example the .ui and .py file are in the same folder ui_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'rqt_conman.ui') # Extend the widget with all attributes and children from UI file loadUi(ui_file, self._widget) # Give QObjects reasonable names self._widget.ns_refresh_button.setIcon(QIcon.fromTheme('view-refresh')) self._widget.setObjectName('ConmanPluginUi') # 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) palette = QPalette() palette.setColor(QPalette.Background, Qt.white) self._widget.setPalette(palette) #self._widget.subscribe_button.setCheckable(True) self._widget.namespace_input.currentIndexChanged.connect( self._handle_refresh_clicked) self._widget.ns_refresh_button.clicked.connect(self.refresh_combo_box) self._widget.refresh_button.clicked[bool].connect( self._handle_refresh_clicked) self._widget.commit_button.clicked[bool].connect( self._handle_commit_clicked) #self._widget.xdot_widget.connect( #self._widget.xdot_widget, SIGNAL('_update_graph'), self._widget.xdot_widget.set_dotcode) self.update_graph_sig.connect(self._update_graph) self.blocks = {} self.groups = {} self._ns = "" self._actions_connected = False self.enable_widgets(False) self.new_dotcode_data = '' self.update_timer = QTimer(self) self.update_timer.setInterval(50) self.update_timer.timeout.connect(self._update_widgets) #self.update_timer.start() self._get_blocks = None self._set_blocks = None self._blocks_model = QStandardItemModel(0, 4) self._blocks_model.setHeaderData(0, Qt.Horizontal, "") self._blocks_model.setHeaderData(1, Qt.Horizontal, "Action") self._blocks_model.setHeaderData(2, Qt.Horizontal, "State") self._blocks_model.setHeaderData(3, Qt.Horizontal, "Block") self._widget.blocks_table.setModel(self._blocks_model) self._blocks_delegate = BlocksDelegate(self) self._widget.blocks_table.setItemDelegate(self._blocks_delegate) self._blocks_model.itemChanged.connect(self.block_changed) self._groups_model = QStandardItemModel(0, 4) self._groups_model.setHeaderData(0, Qt.Horizontal, "") self._groups_model.setHeaderData(1, Qt.Horizontal, "") self._groups_model.setHeaderData(2, Qt.Horizontal, "") self._groups_model.setHeaderData(3, Qt.Horizontal, "Group") self._widget.groups_table.setModel(self._groups_model) self._groups_delegate = GroupsDelegate(self) self._widget.groups_table.setItemDelegate(self._groups_delegate) self.refresh_combo_box() def block_changed(self, item): row = item.row() name = self._blocks_model.item(row, 3).text() block = self.blocks[name] checked = item.checkState() == Qt.Checked def shutdown_plugin(self): # TODO unregister all publishers here self._update_thread.kill() 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 @Slot() def refresh_combo_box(self): self._update_thread.kill() self._widget.namespace_input.setEnabled(False) self._widget.namespace_input.setEditText('updating...') self._update_thread.start() def _update_thread_run(self): _, _, topic_types = rospy.get_master().getTopicTypes() self._topic_dict = dict(topic_types) keys = list(self._topic_dict.keys()) namespaces = list() for i in keys: if i.endswith("get_blocks_action/goal"): namespaces.append(i[0:i.index("get_blocks_action/goal")]) self._widget.namespace_input.setItems.emit(namespaces) @Slot() def _update_finished(self): self._widget.namespace_input.setEnabled(True) def _get_result_cb(self, status, res): rospy.loginfo("got result!") self._blocks_model.setRowCount(0) self._blocks_model.setRowCount(len(res.blocks)) for (i, block) in zip(range(len(res.blocks)), res.blocks): # Store in dict self.blocks[block.name] = block cb = QStandardItem(True) cb.setCheckable(True) cb.setCheckState(Qt.Checked if block.state.value == conman_msgs. msg.TaskState.RUNNING else Qt.Unchecked) cb.setTextAlignment(Qt.AlignHCenter) cb.setTristate(True) action = QStandardItem(True) action.setText("") state = QStandardItem(True) state.setText(state_map[block.state.value]) name = QStandardItem(True) name.setText(str(block.name)) self._blocks_model.setItem(i, 0, cb) self._blocks_model.setItem(i, 1, action) self._blocks_model.setItem(i, 2, state) self._blocks_model.setItem(i, 3, name) for (i, group) in zip(range(len(res.groups)), res.groups): self.groups[group.name] = group cb = QStandardItem(True) cb.setCheckable(True) cb.setCheckState(Qt.Checked) cb.setTextAlignment(Qt.AlignHCenter) cb.setEnabled(False) name = QStandardItem(True) name.setText(str(group.name)) self._groups_model.setItem(i, 0, cb) self._groups_model.setItem(i, 3, name) self._update_groups() self._update_blocks() self._widget.blocks_table.resizeColumnsToContents() self._widget.blocks_table.horizontalHeader().setStretchLastSection( True) self._widget.groups_table.resizeColumnsToContents() self._widget.groups_table.horizontalHeader().setStretchLastSection( True) def _update_blocks(self): for (name, block) in self.blocks.items(): items = self._blocks_model.findItems(name, column=3) if len(items) > 0: item = items[0] else: continue row = item.row() checked = self._blocks_model.item(row, 0).checkState() == Qt.Checked if checked and block.state.value != conman_msgs.msg.TaskState.RUNNING: self._blocks_model.item(row, 1).setText("ENABLE") elif not checked and block.state.value == conman_msgs.msg.TaskState.RUNNING: self._blocks_model.item(row, 1).setText("DISABLE") else: self._blocks_model.item(row, 1).setText("") self._update_groups() def _enable_group(self, index, enable): name = self._groups_model.item(index, 3).text() group = self.groups[name] for member in group.members: items = self._blocks_model.findItems(member, column=3) if len(items) > 0: item = items[0] else: continue row = item.row() self._blocks_model.item( row, 0).setCheckState(Qt.Checked if enable else Qt.Unchecked) self._update_blocks() def _update_groups(self): for (name, group) in self.groups.items(): group_items = self._groups_model.findItems(name, column=3) if len(group_items) > 0: group_item = group_items[0] else: continue group_row = group_item.row() members_checked = [] for member in group.members: items = self._blocks_model.findItems(member, column=3) if len(items) > 0: item = items[0] else: continue row = item.row() members_checked.append( self._blocks_model.item(row, 0).checkState() == Qt.Checked) if all(members_checked): check = Qt.Checked else: check = Qt.Unchecked self._groups_model.item(group_row, 0).setCheckState(check) def _query_blocks(self): if self._get_blocks and self._actions_connected: if self._get_blocks.simple_state == actionlib.SimpleGoalState.DONE: rospy.loginfo("Getting blocks...") goal = conman_msgs.msg.GetBlocksGoal() goal.publish_flow_graph = self._widget.generate_graph_checkbox.isChecked( ) self._get_blocks.send_goal(goal, done_cb=self._get_result_cb) def enable_widgets(self, enable): #self._widget.generate_graph_checkbox.setEnabled(enable) self._widget.force_enable_checkbox.setEnabled(enable) #self._widget.disable_unused_button.setEnabled(enable) self._widget.xdot_widget.setEnabled(enable) self._widget.blocks_table.setEnabled(enable) self._widget.groups_table.setEnabled(enable) self._widget.regenerate_graph_button.setEnabled(enable) def _handle_refresh_clicked(self, checked): ns = self._widget.namespace_input.currentText() if len(ns) > 0: if self._ns != ns: self._actions_connected = False self._ns = ns self.enable_widgets(False) self._dotcode_sub = rospy.Subscriber(ns + '/dotcode', std_msgs.msg.String, self._dotcode_msg_cb) self._get_blocks = actionlib.SimpleActionClient( ns + '/get_blocks_action', conman_msgs.msg.GetBlocksAction) self._set_blocks = actionlib.SimpleActionClient( ns + '/set_blocks_action', conman_msgs.msg.SetBlocksAction) rospy.loginfo("Creating action clients on namespace '%s'..." % ns) if not self._get_blocks.wait_for_server(rospy.Duration(2)): rospy.loginfo("Timed out waiting for %s." % self._get_blocks.action_client.ns) return if not self._set_blocks.wait_for_server(rospy.Duration(2)): rospy.loginfo("Timed out waiting for %s." % self._set_blocks.action_client.ns) return rospy.loginfo("Action clients created.") self._actions_connected = True self.enable_widgets(True) self._query_blocks() def _handle_commit_clicked(self, checked): if self._set_blocks and self._actions_connected: if self._get_blocks.simple_state == actionlib.SimpleGoalState.DONE: rospy.loginfo("Setting blocks...") goal = conman_msgs.msg.SetBlocksGoal() goal.diff = True goal.force = True for i in range(self._blocks_model.rowCount()): name = self._blocks_model.item(i, 3).text() action = self._blocks_model.item(i, 1).text() if action == 'DISABLE': goal.disable.append(name) elif action == 'ENABLE': goal.enable.append(name) self._set_blocks.send_goal(goal, done_cb=self._get_set_result_cb) def _get_set_result_cb(self, status, res): self._query_blocks() @Slot(str) def _update_graph(self, dotcode): global initialized if initialized: self._widget.xdot_widget.set_dotcode(dotcode, center=False) else: self._widget.xdot_widget.set_dotcode(dotcode, center=True) self._widget.xdot_widget.zoom_to_fit() initialized = 1 def _dotcode_msg_cb(self, msg): #self.new_dotcode_data = msg.data self.update_graph_sig.emit(msg.data) def _update_widgets(self): self._update_groups() self._update_blocks()
class OutputDialog(QDialog): def __init__(self, mode, dotgraph, dotcode_factory, dotparser, param_manager, models_desc_file_path, nodename='', parent=None): super(OutputDialog, self).__init__(parent) if mode not in ['add', 'edit']: raise Exception('Wrong mode for Ouput Dialog') self.setWindowTitle("Add a output pipe") self.setGeometry(300, 300, 450, 250) # self._widget = QDialog() ui_file = os.path.join(rospkg.RosPack().get_path('rqt_vino_plugin'), 'resource', 'add_output_dialog.ui') loadUi(ui_file, self) self.dotgraph = dotgraph self.dotparser = dotparser self.dotcode_factory = dotcode_factory self.available_infers_list = param_manager.parse_inferlist_file( models_desc_file_path) self.connect_from_listmodel = QStandardItemModel() self.output_type_combobox.currentTextChanged.connect( self._update_display) self.update_output_types() if mode == 'add': self.buttonBox.accepted.connect(self._create_node_in_graph) elif mode == 'edit': self.nodename = nodename self.buttonBox.accepted.connect(self._edit_node_in_graph) def update_output_types(self, outputlist_file=None): self.output_type_combobox.clear() self.output_type_combobox.addItem('ImageWindow') self.output_type_combobox.addItem('Rviz') self.output_type_combobox.addItem('Rostopic') def get_infer_desc(self, infer_name): for infer in self.available_infers_list: if infer['infer_name'] == infer_name: return infer def _update_display(self, output_name): self.output_name_display_lineEdit.setText(output_name) self.update_connect_to_list(output_name) def update_connect_to_list(self, output_name): self.connect_from_listmodel.clear() for node in self.dotgraph.get_node_list(): if node.get('nodetype') == 'input': pass elif node.get('nodetype') == 'output': pass elif node.get('nodetype') == 'infer': if output_name in self.get_infer_desc( node.get_name())['connect_to']: pipeline_item = QStandardItem() pipeline_item.setText(node.get_name()) pipeline_item.setCheckable(True) pipeline_item.setCheckState(False) self.connect_from_listmodel.appendRow(pipeline_item) self.connect_from_listview.setModel(self.connect_from_listmodel) def _create_node_in_graph(self): output_node_name = str(self.output_type_combobox.currentText()) #Create Node self.dotparser.parse_dotgraph_from_outputs(self.dotcode_factory, self.dotgraph, [output_node_name]) #Create connection from input node to infer node for i in range(self.connect_from_listview.model().rowCount()): node_name = self.connect_from_listmodel.item(i).text() check_state = self.connect_from_listmodel.item(i).checkState() if check_state: connects = {node_name: [output_node_name]} self.dotparser.parse_dotgraph_from_connects( self.dotcode_factory, self.dotgraph, connects) def _edit_node_in_graph(self): self.dotcode_factory.del_node_from_graph(self.dotgraph, self.nodename) self._create_node_in_graph()
class Conman(Plugin): update_graph_sig = Signal(str) def __init__(self, context): super(Conman, self).__init__(context) self._dotcode_sub = None # Give QObjects reasonable names self.setObjectName('Conman') # 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 is a sibling of this file # in this example the .ui and .py file are in the same folder ui_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'rqt_conman.ui') # Extend the widget with all attributes and children from UI file loadUi(ui_file, self._widget) # Give QObjects reasonable names self._widget.setObjectName('ConmanPluginUi') # 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) palette = QPalette () palette.setColor(QPalette.Background, Qt.white) self._widget.setPalette(palette) #self._widget.subscribe_button.setCheckable(True) self._widget.refresh_button.clicked[bool].connect(self._handle_refresh_clicked) self._widget.commit_button.clicked[bool].connect(self._handle_commit_clicked) #self._widget.xdot_widget.connect( #self._widget.xdot_widget, SIGNAL('_update_graph'), self._widget.xdot_widget.set_dotcode) self.update_graph_sig.connect(self._update_graph) self.blocks = { } self.groups = { } self._ns = "" self._actions_connected = False self.enable_widgets(False) self.new_dotcode_data = '' self.update_timer = QTimer(self) self.update_timer.setInterval(50) self.update_timer.timeout.connect(self._update_widgets) #self.update_timer.start() self._get_blocks = None self._set_blocks = None self._blocks_model = QStandardItemModel(0,4) self._blocks_model.setHeaderData(0, Qt.Horizontal, "") self._blocks_model.setHeaderData(1, Qt.Horizontal, "Action") self._blocks_model.setHeaderData(2, Qt.Horizontal, "State") self._blocks_model.setHeaderData(3, Qt.Horizontal, "Block") self._widget.blocks_table.setModel(self._blocks_model) self._blocks_delegate = BlocksDelegate(self) self._widget.blocks_table.setItemDelegate(self._blocks_delegate) self._blocks_model.itemChanged.connect(self.block_changed) self._groups_model = QStandardItemModel(0,4) self._groups_model.setHeaderData(0, Qt.Horizontal, "") self._groups_model.setHeaderData(1, Qt.Horizontal, "") self._groups_model.setHeaderData(2, Qt.Horizontal, "") self._groups_model.setHeaderData(3, Qt.Horizontal, "Group") self._widget.groups_table.setModel(self._groups_model) self._groups_delegate = GroupsDelegate(self) self._widget.groups_table.setItemDelegate(self._groups_delegate) def block_changed(self, item): row = item.row() name = self._blocks_model.item(row,3).text() block = self.blocks[name] checked = item.checkState() == Qt.Checked 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 def _get_result_cb(self, status, res): rospy.loginfo("got result!") self._blocks_model.setRowCount(0) self._blocks_model.setRowCount(len(res.blocks)) for (i,block) in zip(range(len(res.blocks)),res.blocks): # Store in dict self.blocks[block.name] = block cb = QStandardItem(True) cb.setCheckable(True) cb.setCheckState(Qt.Checked if block.state.value == conman_msgs.msg.TaskState.RUNNING else Qt.Unchecked) cb.setTextAlignment(Qt.AlignHCenter) cb.setTristate(True) action = QStandardItem(True) action.setText("") state = QStandardItem(True) state.setText(state_map[block.state.value]) name = QStandardItem(True) name.setText(str(block.name)) self._blocks_model.setItem(i,0,cb) self._blocks_model.setItem(i,1,action) self._blocks_model.setItem(i,2,state) self._blocks_model.setItem(i,3,name) for (i,group) in zip(range(len(res.groups)),res.groups): self.groups[group.name] = group cb = QStandardItem(True) cb.setCheckable(True) cb.setCheckState(Qt.Checked) cb.setTextAlignment(Qt.AlignHCenter) cb.setEnabled(False) name = QStandardItem(True) name.setText(str(group.name)) self._groups_model.setItem(i,0,cb) self._groups_model.setItem(i,3,name) self._update_groups() self._update_blocks() self._widget.blocks_table.resizeColumnsToContents() self._widget.blocks_table.horizontalHeader().setStretchLastSection(True) self._widget.groups_table.resizeColumnsToContents() self._widget.groups_table.horizontalHeader().setStretchLastSection(True) def _update_blocks(self): for (name, block) in self.blocks.items(): items = self._blocks_model.findItems(name, column=3) if len(items) > 0: item = items[0] else: continue row = item.row() checked = self._blocks_model.item(row,0).checkState() == Qt.Checked if checked and block.state.value != conman_msgs.msg.TaskState.RUNNING: self._blocks_model.item(row,1).setText("ENABLE") elif not checked and block.state.value == conman_msgs.msg.TaskState.RUNNING: self._blocks_model.item(row,1).setText("DISABLE") else: self._blocks_model.item(row,1).setText("") self._update_groups() def _enable_group(self, index, enable): name = self._groups_model.item(index, 3).text() group = self.groups[name] for member in group.members: items = self._blocks_model.findItems(member, column=3) if len(items) > 0: item = items[0] else: continue row = item.row() self._blocks_model.item(row,0).setCheckState(Qt.Checked if enable else Qt.Unchecked) self._update_blocks() def _update_groups(self): for (name, group) in self.groups.items(): group_items = self._groups_model.findItems(name, column=3) if len(group_items) > 0: group_item = group_items[0] else: continue group_row = group_item.row() members_checked = [] for member in group.members: items = self._blocks_model.findItems(member, column=3) if len(items) > 0: item = items[0] else: continue row = item.row() members_checked.append(self._blocks_model.item(row,0).checkState() == Qt.Checked) if all(members_checked): check = Qt.Checked else: check = Qt.Unchecked self._groups_model.item(group_row,0).setCheckState(check) def _query_blocks(self): if self._get_blocks and self._actions_connected: if self._get_blocks.simple_state == actionlib.SimpleGoalState.DONE: rospy.loginfo("Getting blocks...") goal = conman_msgs.msg.GetBlocksGoal() goal.publish_flow_graph = self._widget.generate_graph_checkbox.isChecked() self._get_blocks.send_goal(goal, done_cb=self._get_result_cb) def enable_widgets(self, enable): #self._widget.generate_graph_checkbox.setEnabled(enable) self._widget.force_enable_checkbox.setEnabled(enable) #self._widget.disable_unused_button.setEnabled(enable) self._widget.xdot_widget.setEnabled(enable) self._widget.blocks_table.setEnabled(enable) self._widget.groups_table.setEnabled(enable) self._widget.regenerate_graph_button.setEnabled(enable) def _handle_refresh_clicked(self, checked): ns = self._widget.namespace_input.text() if len(ns) > 0: if self._ns != ns: self._actions_connected = False self._ns = ns self.enable_widgets(False) self._dotcode_sub = rospy.Subscriber( ns+'/dotcode', std_msgs.msg.String, self._dotcode_msg_cb) self._get_blocks = actionlib.SimpleActionClient( ns+'/get_blocks_action', conman_msgs.msg.GetBlocksAction) self._set_blocks = actionlib.SimpleActionClient( ns+'/set_blocks_action', conman_msgs.msg.SetBlocksAction) rospy.loginfo("Creating action clients on namespace '%s'..." % ns) if not self._get_blocks.wait_for_server(rospy.Duration(2)): rospy.loginfo("Timed out waiting for %s." % self._get_blocks.action_client.ns) return if not self._set_blocks.wait_for_server(rospy.Duration(2)): rospy.loginfo("Timed out waiting for %s." % self._set_blocks.action_client.ns) return rospy.loginfo("Action clients created.") self._actions_connected = True self.enable_widgets(True) self._query_blocks() def _handle_commit_clicked(self, checked): if self._set_blocks and self._actions_connected: if self._get_blocks.simple_state == actionlib.SimpleGoalState.DONE: rospy.loginfo("Setting blocks...") goal = conman_msgs.msg.SetBlocksGoal() goal.diff = True goal.force = True for i in range(self._blocks_model.rowCount()): name = self._blocks_model.item(i,3).text() action = self._blocks_model.item(i,1).text() if action == 'DISABLE': goal.disable.append(name) elif action == 'ENABLE': goal.enable.append(name) self._set_blocks.send_goal(goal, done_cb=self._get_set_result_cb) def _get_set_result_cb(self, status, res): self._query_blocks() @Slot(str) def _update_graph(self,dotcode): self._widget.xdot_widget.set_dotcode(dotcode, center=False) def _dotcode_msg_cb(self, msg): #self.new_dotcode_data = msg.data self.update_graph_sig.emit(msg.data) def _update_widgets(self): self._update_groups() self._update_blocks()
class InferenceDialog(QDialog): def __init__(self, mode, dotgraph , dotcode_factory, dotparser , param_manager, models_desc_file_path, nodename='', parent = None): super(InferenceDialog, self).__init__(parent) if mode not in ['add','edit']: raise Exception('wrong mode for InferenceDialog') self.nodename = nodename self.setWindowTitle("Dialog") self.setGeometry(300,300,450,250) # self._widget = QDialog() ui_file = os.path.join(rospkg.RosPack().get_path('rqt_vino_plugin'), 'resource', 'add_inference_dialog.ui') loadUi(ui_file, self) self.dotgraph = dotgraph self.dotparser = dotparser self.dotcode_factory = dotcode_factory self.available_infers_list = param_manager.parse_inferlist_file(models_desc_file_path) # self.node_name_lineedit.textEdited.connect(self._edit_infer_name) #self.node_infer_name_combobox.activated.connect(self.update_infer_names) self.node_infer_combobox.currentTextChanged.connect(self._select_infer_type) self.connect_from_listmodel = QStandardItemModel()#(self.connect_from_listview) self.connect_to_listmodel = QStandardItemModel()#(self.connect_to_listview) # self.connect_from_listmodel.itemChanged.connect(self._connect_from_changed) #self.connect_to_listview.setModel(self.connect_to_listmodel) self.update_infer_names() self.update_connect_from_list() if mode == 'add': self.buttonBox.accepted.connect(self._create_node_in_graph) elif mode == 'edit': self.buttonBox.accepted.connect(self._edit_node_in_graph) def update_infer_names(self): self.node_infer_combobox.clear() for infer in self.available_infers_list: self.node_infer_combobox.addItem(infer['infer_name']) def get_infer_desc(self,infer_name): for infer in self.available_infers_list: if infer['infer_name'] == infer_name: return infer def get_model_desc(self,infer_name,model_type): for model_desc in self.get_infer_desc(infer_name)['available_models']: if model_desc['name'] == model_type: return model_desc def update_connect_to_list(self, infer_name): self.connect_from_listmodel.clear() self.connect_to_listmodel.clear() for node in self.dotgraph.get_node_list(): pipeline_item = QStandardItem() pipeline_item.setText(node.get_name()) pipeline_item.setCheckable(True) pipeline_item.setCheckState(False) if node.get_name() == infer_name.encode('utf-8'): continue if node.get('nodetype') == 'input': self.connect_from_listmodel.appendRow(pipeline_item) # self.connect_from_listview.addItem(node.get_name()) elif node.get('nodetype') == 'output': self.connect_to_listmodel.appendRow(pipeline_item) # self.connect_to_listview.addItem(node.get_name()) elif node.get('nodetype') == 'infer': if infer_name in self.get_infer_desc(infer_name)['connect_from']: self.connect_from_listmodel.appendRow(pipeline_item) elif infer_name in self.get_infer_desc(infer_name)['connect_to']: self.connect_to_listmodel.appendRow(pipeline_item) self.connect_from_listview.setModel(self.connect_from_listmodel) self.connect_to_listview.setModel(self.connect_to_listmodel) def update_connect_from_list(self): pass def update_model_type(self,infer_name): self.node_model_combobox.clear() # infer = self.get_infer_desc(infer_name) for model in self.get_infer_desc(infer_name)['available_models']: self.node_model_combobox.addItem(model['name']) # self.node_model_combobox.addItem("MobileNetSSD") # self.node_model_combobox.addItem("YoloV2") def _select_infer_type(self,infer_name): self.node_engine_combobox.clear() infer_name = str(infer_name) for infer in self.available_infers_list: if infer['infer_name'] == infer_name: for engine in infer['available_engine']: self.node_engine_combobox.addItem(engine) self.update_model_type(infer_name) self.update_connect_to_list(infer_name) def _edit_infer_name(self,infer_name): self.node_name_display_lineEdit.setText(infer_name) def _create_node_in_graph(self): infer_node_name = str(self.node_infer_combobox.currentText()) print('ava_list',self.available_infers_list) infer_node_engine = self.node_engine_combobox.currentText() print(self.get_infer_desc(infer_node_name)) infer_node_model_type = self.node_model_combobox.currentText() infer_node_label = self.get_model_desc(infer_node_name, infer_node_model_type)['label'] infer_node_model = self.get_model_desc(infer_node_name, infer_node_model_type)['model'] #Create Node self.dotparser.parse_dotgraph_from_infers(self.dotcode_factory,self.dotgraph, [{'name': infer_node_name, 'engine':infer_node_engine, 'model':infer_node_model, 'label':infer_node_label, 'batch':1}]) #To-do Implement batch configure #Create connection from input node to infer node for i in range(self.connect_from_listview.model().rowCount()): node_name = self.connect_from_listmodel.item(i).text() check_state = self.connect_from_listmodel.item(i).checkState() if check_state: connects = { node_name: [infer_node_name] } self.dotparser.parse_dotgraph_from_connects(self.dotcode_factory,self.dotgraph,connects) #Create connection from infer node to output node for i in range(self.connect_to_listview.model().rowCount()): node_name = self.connect_to_listmodel.item(i).text() check_state = self.connect_to_listmodel.item(i).checkState() if check_state: connects = { infer_node_name :[node_name] } self.dotparser.parse_dotgraph_from_connects(self.dotcode_factory,self.dotgraph,connects) def _edit_node_in_graph(self): self.dotcode_factory.del_node_from_graph(self.dotgraph,self.nodename) self._create_node_in_graph()
class InteractionsChooserUI(): def __init__(self, rocon_master_uri='localhost', host_name='localhost', with_rqt=False): self.with_rqt = with_rqt self.widget = QWidget() self.pairings_view_model = QStandardItemModel() self.interactions_view_model = QStandardItemModel() self.interactions_remocon = InteractionsRemocon( rocon_master_uri, host_name) self.interactions_remocon.connect( [self.update_group_combobox, self.refresh_grids]) self.interactions_remocon.connect( [self.update_pairings_group_combobox, self.refresh_grids]) self.default_group = "All" rospack = rospkg.RosPack() ui_file = os.path.join(rospack.get_path('rocon_remocon'), 'ui', 'interactions_chooser.ui') loadUi(ui_file, self.widget, {}) # create a few directories for caching icons and ... utils.setup_home_dirs() self._init_ui() self._init_events() def shutdown(self): self.interactions_remocon.shutdown() @Slot() def update_group_combobox(self): """ The underyling ros part of the remocon might get fresh data about the group list. Connect to this slot to update the combobox in the ui. """ new_groups = copy.copy( self.interactions_remocon.interactions_table.groups()) new_groups = [g for g in new_groups if g != "Hidden"] # did the underlying groups change - if so, update the combobox current_group = self.widget.interactions_group_combobox.currentText() current_size = self.widget.interactions_group_combobox.count() target_group = current_group if current_size != 1 else self.default_group current_group_list = [ self.widget.interactions_group_combobox.itemText(i) for i in range(self.widget.interactions_group_combobox.count()) ] if set(current_group_list) != set(['All'] + new_groups): self.widget.interactions_group_combobox.clear() self.widget.interactions_group_combobox.addItems(['All'] + new_groups) index = self.widget.interactions_group_combobox.findText( target_group) if index != -1: self.widget.interactions_group_combobox.setCurrentIndex(index) self.refresh_grids() @Slot() def update_pairings_group_combobox(self): """ The underyling ros part of the remocon might get fresh data about the group list. Connect to this slot to update the combobox in the ui. """ new_groups = copy.copy( self.interactions_remocon.pairings_table.groups()) # did the underlying groups change - if so, update the combobox current_group = self.widget.pairings_group_combobox.currentText() current_size = self.widget.pairings_group_combobox.count() target_group = current_group if current_size != 1 else self.default_pairings_group current_group_list = [ self.widget.pairings_group_combobox.itemText(i) for i in range(self.widget.pairings_group_combobox.count()) ] if set(current_group_list) != set(['All'] + new_groups): self.widget.pairings_group_combobox.clear() self.widget.pairings_group_combobox.addItems(['All'] + new_groups) index = self.widget.pairings_group_combobox.findText(target_group) if index != -1: self.widget.pairings_group_combobox.setCurrentIndex(index) self.refresh_grids() @Slot() def refresh_grids(self): """ This just does a complete redraw of the interactions with the currently selected role. It's a bit brute force doing this every time the interactions' 'state' changes, but this suffices for now. """ self.pairings_view_model.clear() self.interactions_view_model.clear() active_pairing = copy.copy(self.interactions_remocon.active_pairing) group = self.widget.pairings_group_combobox.currentText() for p in self.interactions_remocon.pairings_table.sorted(): if group != "All" and p.group != group: continue is_running = False enabled = False if active_pairing is not None and p.name == active_pairing.name: is_running = True enabled = True elif active_pairing is None: enabled = True # enabled = not p.requires_interaction item = icon.QModelIconItem(p, enabled=enabled, running=is_running) self.pairings_view_model.appendRow(item) group = self.widget.interactions_group_combobox.currentText() for i in self.interactions_remocon.interactions_table.sorted(): if group != "All" and i.group != group: continue if i.hidden: continue extra_tooltip_info = "" if i.required_pairings: extra_tooltip_info += " Requires " for required_pairing in i.required_pairings: extra_tooltip_info += "'" + required_pairing + "', " extra_tooltip_info = extra_tooltip_info.rstrip(', ') if not i.bringup_pairing: extra_tooltip_info += " to be running" extra_tooltip_info += "." item = icon.QModelIconItem( i, enabled=self._is_interaction_enabled(i), running=self._is_interaction_running(i), extended_tooltip_info=extra_tooltip_info) self.interactions_view_model.appendRow(item) def _init_ui(self): self.widget.pairings_grid.setViewMode(QListView.IconMode) self.widget.pairings_grid.setModel(self.pairings_view_model) self.widget.pairings_grid.setWordWrap(True) self.widget.pairings_grid.setWrapping(True) # really need to get away from listview, or subclass it if we want to control better how many lines of text show up # self.widget.pairings_grid.setTextElideMode(Qt.ElideNone) self.widget.pairings_grid.setIconSize(QSize(60, 60)) self.widget.pairings_grid.setSpacing(10) self.widget.interactions_grid.setViewMode(QListView.IconMode) self.widget.interactions_grid.setModel(self.interactions_view_model) self.widget.interactions_grid.setWordWrap(True) self.widget.interactions_grid.setWrapping(True) self.widget.interactions_grid.setIconSize(QSize(60, 60)) self.widget.interactions_grid.setSpacing(10) for ns in self.interactions_remocon.namespaces: self.widget.namespace_checkbox.addItem(ns) self.refresh_grids() self.widget.pairings_group_combobox.addItems( ['All'] + self.interactions_remocon.pairings_table.groups()) self.widget.interactions_group_combobox.addItems( ['All'] + self.interactions_remocon.interactions_table.groups()) # TODO namespace checkbox to self.interactions_remocon.active_namespace ############################################################################## # Private ############################################################################## def _init_events(self): self.widget.namespace_checkbox.currentIndexChanged.connect( self._event_change_namespace) self.widget.pairings_grid.clicked.connect(self._pairing_single_click) self.widget.interactions_grid.clicked.connect( self._interaction_single_click) self.widget.button_stop_all_interactions.clicked.connect( self.interactions_remocon.stop_all_interactions) self.widget.pairings_group_combobox.currentIndexChanged.connect( self.refresh_grids) self.widget.interactions_group_combobox.currentIndexChanged.connect( self.refresh_grids) def _event_change_namespace(self): rospy.logwarn( "Remocon : changing interaction managers is currently not supported." ) def _pairing_single_click(self, index): pairing_item = self.pairings_view_model.item(index.row()) pairing = pairing_item.implementation self._create_pairing_dialog(pairing) def _create_pairing_dialog(self, pairing): active_pairing = copy.copy(self.interactions_remocon.active_pairing) if active_pairing is not None: is_running = (active_pairing.name == pairing.name) is_enabled = is_running else: is_enabled = True # not pairing.requires_interaction is_running = False self.selected_pairing = pairing self.dialog = PairingDialog(self.widget, pairing, self.interactions_remocon.start_pairing, self.interactions_remocon.stop_pairing, is_enabled, is_running) self.dialog.show() def _interaction_single_click(self, index): interaction_item = self.interactions_view_model.item(index.row()) interaction = interaction_item.implementation self._create_interaction_dialog(interaction) def _create_interaction_dialog(self, interaction): self.selected_interaction = interaction self.dialog = InteractionDialog( self.widget, interaction, self.interactions_remocon.start_interaction, self.interactions_remocon.stop_interaction, self._is_interaction_enabled(interaction), self._is_interaction_running(interaction), self._is_interaction_permitted_new_launches(interaction)) self.dialog.show() def _is_interaction_permitted_new_launches(self, interaction): current_number_of_launches = len( self.interactions_remocon.launched_interactions.get_launch_details( interaction.hash)) if interaction.max == -1 or current_number_of_launches < interaction.max: return True else: return False def _is_interaction_enabled(self, interaction): active_pairing = copy.copy(self.interactions_remocon.active_pairing) enabled = True if interaction.required_pairings: if active_pairing and active_pairing.name not in interaction.required_pairings: enabled = False elif active_pairing is None: if not interaction.bringup_pairing: enabled = False else: available_required_pairings = [ name for name in interaction.required_pairings if self.interactions_remocon.pairings_table.find(name) is not None ] if not available_required_pairings: enabled = False return enabled def _is_interaction_running(self, interaction): return True if self.interactions_remocon.launched_interactions.get_launch_details( interaction.hash) else False