def invalidateFilter(self): """ Invalidates the filter """ QSortFilterProxyModel.invalidateFilter(self) # invalidate cache self.filterAcceptsRow.cache_clear()
def invalidateFilter(self): """ Invalidates the filter """ QSortFilterProxyModel.invalidateFilter(self) #invalidate cache self.filterAcceptsRow.cache_clear()
def __init__(self, parent=None): ''' Creates the window, connects the signals and init the class. ''' QDockWidget.__init__(self, parent) # initialize parameter self.__current_path = os.path.expanduser('~') # load the UI file ui_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'LaunchFilesDockWidget.ui') loadUi(ui_file, self, custom_widgets={'EnhancedLineEdit': EnhancedLineEdit}) self._current_search = '' pal = self.palette() self._default_color = pal.color(QPalette.Window) # initialize the progress queue self.progress_queue = ProgressQueue(self.ui_frame_progress_cfg, self.ui_bar_progress_cfg, self.ui_button_progress_cancel_cfg, 'Launch File') # initialize the view for the launch files self.launchlist_model = LaunchListModel( progress_queue=self.progress_queue, viewobj=self.ui_file_view) self.launchlist_proxy_model = QSortFilterProxyModel(self) self.launchlist_proxy_model.setSourceModel(self.launchlist_model) self.name_delegate = HTMLDelegate(check_for_ros_names=False, palette=self.palette()) self.ui_file_view.setItemDelegateForColumn(0, self.name_delegate) self.ui_file_view.setModel(self.launchlist_proxy_model) self.ui_file_view.setAlternatingRowColors(True) self.ui_file_view.activated.connect(self.on_launch_selection_activated) self.ui_file_view.setDragDropMode(QAbstractItemView.DragOnly) self.ui_file_view.setDragEnabled(True) sm = self.ui_file_view.selectionModel() sm.selectionChanged.connect(self.on_ui_file_view_selection_changed) self.launchlist_model.pathlist_handled.connect( self.on_pathlist_handled) self.launchlist_model.error_on_path.connect(self.on_error_on_path) self.ui_search_line.refresh_signal.connect(self.set_package_filter) self.ui_search_line.stop_signal.connect(self.stop) # connect to the button signals self.ui_button_reload.clicked.connect(self.on_reload_clicked) self.ui_button_edit.clicked.connect(self.on_edit_xml_clicked) self.ui_button_new.clicked.connect(self.on_new_xml_clicked) self.ui_button_transfer.clicked.connect(self.on_transfer_file_clicked) self.ui_button_save_profile.clicked.connect( self.on_save_profile_clicked) self.ui_button_load.clicked.connect(self.on_load_xml_clicked) self._masteruri2name = {} self._reload_timer = None
def __init__(self, parent=None): ''' Creates the window, connects the signals and init the class. ''' QDockWidget.__init__(self, parent) # initialize parameter self.__current_path = os.path.expanduser('~') # load the UI file ui_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'LaunchFilesDockWidget.ui') loadUi(ui_file, self) # initialize the view for the launch files self.launchlist_model = LaunchListModel() self.launchlist_proxyModel = QSortFilterProxyModel(self) self.launchlist_proxyModel.setSourceModel(self.launchlist_model) self.xmlFileView.setModel(self.launchlist_proxyModel) self.xmlFileView.setAlternatingRowColors(True) self.xmlFileView.activated.connect(self.on_launch_selection_activated) self.xmlFileView.setDragDropMode(QAbstractItemView.DragOnly) self.xmlFileView.setDragEnabled(True) sm = self.xmlFileView.selectionModel() sm.selectionChanged.connect(self.on_xmlFileView_selection_changed) # self.searchPackageLine.setVisible(False) self.searchPackageLine.textChanged.connect(self.set_package_filter) self.searchPackageLine.focusInEvent = self._searchline_focusInEvent # connect to the button signals self.refreshXmlButton.clicked.connect(self.on_refresh_xml_clicked) self.editXmlButton.clicked.connect(self.on_edit_xml_clicked) self.newXmlButton.clicked.connect(self.on_new_xml_clicked) self.openXmlButton.clicked.connect(self.on_open_xml_clicked) self.transferButton.clicked.connect(self.on_transfer_file_clicked) self.loadXmlButton.clicked.connect(self.on_load_xml_clicked) self.loadXmlAsDefaultButton.clicked.connect(self.on_load_as_default) # creates a default config menu start_menu = QMenu(self) self.loadDeafaultAtHostAct = QAction( "&Load default config on host", self, statusTip="Loads the default config at given host", triggered=self.on_load_as_default_at_host) start_menu.addAction(self.loadDeafaultAtHostAct) self.loadXmlAsDefaultButton.setMenu(start_menu) # initialize the progress queue self.progress_queue = ProgressQueue(self.progressFrame_cfg, self.progressBar_cfg, self.progressCancelButton_cfg, 'Launch File')
def __init__(self, parent=None): ''' Creates the window, connects the signals and init the class. ''' QDockWidget.__init__(self, parent) # load the UI file settings_dock_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'SettingsDockWidget.ui') loadUi(settings_dock_file, self) # initialize the settings view model self.settings_model = SettingsModel() self.settings_proxyModel = QSortFilterProxyModel(self) self.settings_proxyModel.setSourceModel(self.settings_model) self.settingsTreeView.setModel(self.settings_proxyModel) self.settingsTreeView.setAlternatingRowColors(True) for i, (_, width) in enumerate(SettingsModel.header): self.settingsTreeView.setColumnWidth(i, width) self.item_delegate = ItemDelegate() self.item_delegate.settings_path_changed_signal.connect(self.reload_settings) self.settingsTreeView.setItemDelegateForColumn(1, self.item_delegate) self.reload_settings()
def __init__(self, parent=None): super(ExtendedComboBox, self).__init__(parent) self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) # add a filter model to filter matching items self.filter_model = QSortFilterProxyModel(self) self.filter_model.setFilterCaseSensitivity(Qt.CaseInsensitive) self.filter_model.setSourceModel(self.model()) # add a completer, which uses the filter model self.completer = QCompleter(self.filter_model, self) # always show all (filtered) completions self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.setCompleter(self.completer) # connect signals self.lineEdit().textEdited[unicode].connect(self.filter_model.setFilterFixedString) self.completer.activated.connect(self.on_completer_activated) self.setItems.connect(self.onSetItems)
def filterAcceptsRow(self, source_row, source_parent): """ Tells by analysing the given row if it should be shown or not. This behaviour can be modified via setFilterRegExp method so that e.g. only the entries of a specific host can be shown. :param source_row: the source row :type source_row: int :param source_parent: the source of the parent :type source_parent: QModelIndex :returns: bool """ return QSortFilterProxyModel.filterAcceptsRow(self, source_row, source_parent)
class ExtendedComboBox(QComboBox): setItems = Signal(list) def __init__(self, parent=None): super(ExtendedComboBox, self).__init__(parent) self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) # add a filter model to filter matching items self.filter_model = QSortFilterProxyModel(self) self.filter_model.setFilterCaseSensitivity(Qt.CaseInsensitive) self.filter_model.setSourceModel(self.model()) # add a completer, which uses the filter model self.completer = QCompleter(self.filter_model, self) # always show all (filtered) completions self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.setCompleter(self.completer) # connect signals self.lineEdit().textEdited[unicode].connect(self.filter_model.setFilterFixedString) self.completer.activated.connect(self.on_completer_activated) self.setItems.connect(self.onSetItems) # on selection of an item from the completer, select the corresponding item from combobox def on_completer_activated(self, text): if text: index = self.findText(text) self.setCurrentIndex(index) # on model change, update the models of the filter and completer as well def setModel(self, model): super(ExtendedComboBox, self).setModel(model) self.filter_model.setSourceModel(model) self.completer.setModel(self.filter_model) # on model column change, update the model column of the filter and completer as well def setModelColumn(self, column): self.completer.setCompletionColumn(column) self.filter_model.setFilterKeyColumn(column) super(ExtendedComboBox, self).setModelColumn(column) @Slot(list) def onSetItems(self, items): self.clear() self.addItems(items)
def __init__(self, parent=None): ''' Creates the window, connects the signals and init the class. ''' QDockWidget.__init__(self, parent) # initialize parameter self.__current_path = os.path.expanduser('~') # load the UI file ui_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'LaunchFilesDockWidget.ui') loadUi(ui_file, self) # initialize the view for the launch files self.launchlist_model = LaunchListModel() self.launchlist_proxyModel = QSortFilterProxyModel(self) self.launchlist_proxyModel.setSourceModel(self.launchlist_model) self.xmlFileView.setModel(self.launchlist_proxyModel) self.xmlFileView.setAlternatingRowColors(True) self.xmlFileView.activated.connect(self.on_launch_selection_activated) self.xmlFileView.setDragDropMode(QAbstractItemView.DragOnly) self.xmlFileView.setDragEnabled(True) sm = self.xmlFileView.selectionModel() sm.selectionChanged.connect(self.on_xmlFileView_selection_changed) # self.searchPackageLine.setVisible(False) self.searchPackageLine.textChanged.connect(self.set_package_filter) self.searchPackageLine.focusInEvent = self._searchline_focusInEvent # connect to the button signals self.refreshXmlButton.clicked.connect(self.on_refresh_xml_clicked) self.editXmlButton.clicked.connect(self.on_edit_xml_clicked) self.newXmlButton.clicked.connect(self.on_new_xml_clicked) self.openXmlButton.clicked.connect(self.on_open_xml_clicked) self.transferButton.clicked.connect(self.on_transfer_file_clicked) self.loadXmlButton.clicked.connect(self.on_load_xml_clicked) self.loadXmlAsDefaultButton.clicked.connect(self.on_load_as_default) # creates a default config menu start_menu = QMenu(self) self.loadDeafaultAtHostAct = QAction("&Load default config on host", self, statusTip="Loads the default config at given host", triggered=self.on_load_as_default_at_host) start_menu.addAction(self.loadDeafaultAtHostAct) self.loadXmlAsDefaultButton.setMenu(start_menu) # initialize the progress queue self.progress_queue = ProgressQueue(self.progressFrame_cfg, self.progressBar_cfg, self.progressCancelButton_cfg)
def filterAcceptsRow(self, source_row, source_parent): """ Tells by analysing the given row if it should be shown or not. This behaviour can be modified via setFilterRegExp method so that e.g. only the entries of a specific host can be shown. :param source_row: the source of the parent :type source_row: int :param source_parent: the source of the parent :type source_parent: QModelIndex :returns: True if the row should be shown :rtype: bool """ entries = [] item = source_parent.internalPointer() child = None if item is not None: if isinstance(item, TreeTopicItem): child = item.get_child(source_row, self.sourceModel().parent( source_parent).internalPointer()) else: child = source_parent.internalPointer().get_child(source_row) entries = [child.get_type(), child.get_seuid(), child.get_state(), child.get_short_data()] else: child = self.sourceModel().get_root_item().get_child(source_row) entries = [child.get_type(), child.get_seuid(), child.get_state(), child.get_short_data()] child_childs = child.get_childs( self.sourceModel().parent( source_parent).internalPointer()) for i in range(0, len(child_childs)): if self.filterAcceptsRow(i, self.sourceModel().index(source_row, 0, source_parent)): return True correct_type = False data = entries[0] if data[0] == "h": correct_type = True elif self.__show_nodes and data[0] == "n": correct_type = True elif self.__show_connections and data[0] == "c": if self.__show_subscribers: correct_type = True else: if child.is_subscriber: correct_type = False else: correct_type = True elif self.__show_topics is True: if data[0] == "t": correct_type = True if correct_type is False: return False if self.__hide_debug is True: for entry in self.__quiet_names: if entries[1].find(entry) is not -1: return False # todo: speed this implementation a lot up by not using the model!!! if self.__filter_string is not "": for i in range(0, len(entries)): if self.__filter_string in entries[i]: return QSortFilterProxyModel.filterAcceptsRow(self, source_row, source_parent) return False return QSortFilterProxyModel.filterAcceptsRow(self, source_row, source_parent)
class SettingsWidget(QDockWidget): ''' Settings widget to handle the settings changes. The changes will direct change the settings of the GUI. ''' def __init__(self, parent=None): ''' Creates the window, connects the signals and init the class. ''' QDockWidget.__init__(self, parent) # load the UI file settings_dock_file = os.path.join( os.path.dirname(os.path.realpath(__file__)), 'SettingsDockWidget.ui') loadUi(settings_dock_file, self) # initialize the settings view model self.settings_model = SettingsModel() self.settings_proxyModel = QSortFilterProxyModel(self) self.settings_proxyModel.setSourceModel(self.settings_model) self.settingsTreeView.setModel(self.settings_proxyModel) self.settingsTreeView.setAlternatingRowColors(True) for i, (_, width) in enumerate(SettingsModel.header): self.settingsTreeView.setColumnWidth(i, width) self.item_delegate = ItemDelegate() self.item_delegate.settings_path_changed_signal.connect( self.reload_settings) self.settingsTreeView.setItemDelegateForColumn(1, self.item_delegate) self.reload_settings() def reload_settings(self): ''' Load the current settings data into the model. The settings itself will not be loaded. ''' settings = { 'Default user:'******'value': nm.settings().default_user, 'settings': nm.settings(), 'attrname': 'default_user', 'value_default': nm.settings().USER_DEFAULT, 'tooltip': '<p>The user used for ssh connection to remote hosts</p>' }, ), 'Launch history length:': ({ 'value': nm.settings().launch_history_length, 'settings': nm.settings(), 'attrname': 'launch_history_length', 'value_default': nm.settings().LAUNCH_HISTORY_LENGTH, 'value_min': 0, 'value_max': 25, 'tooltip': '<p>The count of recent ' 'loaded launch files displayed in the root ' 'of the <span style=" font-weight:600;">launch ' 'files</span> view.</p>' }, ), 'Param history length:': ({ 'value': nm.settings().param_history_length, 'settings': nm.settings(), 'attrname': 'param_history_length', 'value_default': nm.settings().PARAM_HISTORY_LENGTH, 'value_min': 0, 'value_max': 25, 'tooltip': '<p>The count of parameters stored which ' 'are entered in a parameter dialog (Launch file arguments, ' 'paramter server, publishing to a topic, service call)</p>' }, ), 'Settings path:': ({ 'value': nm.settings().cfg_path, 'settings': nm.settings(), 'attrname': 'cfg_path', 'edit_type': SettingsValueItem.EDIT_TYPE_FOLDER, 'value_default': nm.settings().CFG_PATH, 'tooltip': '' }, ), 'Robot icon path:': ({ 'value': nm.settings().robots_path, 'settings': nm.settings(), 'attrname': 'robots_path', 'edit_type': SettingsValueItem.EDIT_TYPE_FOLDER, 'value_default': nm.settings().ROBOTS_DIR, 'tooltip': '<p>The path to the folder with robot images ' '(<span style=" font-weight:600;">.png</span>).' 'The images with robot name will be displayed in the ' 'info bar.</p>' }, ), 'Show files extensions:': ({ 'value': ', '.join(nm.settings().launch_view_file_ext), 'settings': nm.settings(), 'attrname': 'launch_view_file_ext', 'value_default': ', '.join(nm.settings().LAUNCH_VIEW_EXT), 'tooltip': '<p>Files that are displayed next to Launch ' 'files in the <span style="font-weight:600;">' 'launch files</span> view</p>' }, ), 'Store window layout:': ({ 'value': nm.settings().store_geometry, 'settings': nm.settings(), 'attrname': 'store_geometry', 'value_default': nm.settings().STORE_GEOMETRY, 'tooltip': '' }, ), 'Movable dock widgets:': ({ 'value': nm.settings().movable_dock_widgets, 'settings': nm.settings(), 'attrname': 'movable_dock_widgets', 'value_default': nm.settings().MOVABLE_DOCK_WIDGETS, 'tooltip': 'On false you can\'t reorganize docking widgets. Needs restart!', 'need_restart': True }, ), 'Max time difference:': ({ 'value': nm.settings().max_timediff, 'settings': nm.settings(), 'attrname': 'max_timediff', 'value_default': nm.settings().MAX_TIMEDIFF, 'value_step': 0.1, 'tooltip': '<p>Shows a warning if the time difference to ' 'remote host is greater than this value</p>' }, ), 'Autoupdate:': ({ 'value': nm.settings().autoupdate, 'settings': nm.settings(), 'attrname': 'autoupdate', 'value_default': nm.settings().AUTOUPDATE, 'tooltip': '<p>By default node manager updates the current ' 'state on changes. You can deactivate this behavior to ' 'reduce the network load. If autoupdate is deactivated ' 'you must refresh the state manually.</p>' }, ), 'Start sync with discovery:': ({ 'value': nm.settings().start_sync_with_discovery, 'settings': nm.settings(), 'attrname': 'start_sync_with_discovery', 'value_default': nm.settings().START_SYNC_WITH_DISCOVERY, 'tooltip': "<p>Sets 'start sync' in 'Start' master discovery " "dialog to True, if this option is set to true.</p>" }, ), 'Confirm exit when closing:': ({ 'value': nm.settings().confirm_exit_when_closing, 'settings': nm.settings(), 'attrname': 'confirm_exit_when_closing', 'value_default': nm.settings().CONFIRM_EXIT_WHEN_CLOSING, 'tooltip': "<p>Shows on closing of node_manager a dialog to stop " "all ROS nodes if this option is set to true.</p>" }, ), 'Highlight xml blocks:': ({ 'value': nm.settings().highlight_xml_blocks, 'settings': nm.settings(), 'attrname': 'highlight_xml_blocks', 'value_default': nm.settings().HIGHLIGHT_XML_BLOCKS, 'tooltip': "<p>Highlights the current selected XML block, while " "editing ROS launch file.</p>" }, ), 'Colorize hosts:': ({ 'value': nm.settings().colorize_hosts, 'settings': nm.settings(), 'attrname': 'colorize_hosts', 'value_default': nm.settings().COLORIZE_HOSTS, 'tooltip': "<p>Determine automatic a default color for each host if True. " "Manually setted color will be prefered. You can select the color by " "double-click on hostname in description panel. To remove a setted color " "delete it manually from $HOME/.ros/node_manager/settings.ini</p>" }, ), 'Check for nodelets at start:': ({ 'value': nm.settings().check_for_nodelets_at_start, 'settings': nm.settings(), 'attrname': 'check_for_nodelets_at_start', 'value_default': nm.settings().CHECK_FOR_NODELETS_AT_START, 'tooltip': "Test the startlist for nodelet manager and all nodelets. " "If one of the nodes is not in the list a dialog is displayed with " "proposal to start other nodes, too.</p>" }, ), 'Show noscreen error:': ({ 'value': nm.settings().show_noscreen_error, 'settings': nm.settings(), 'attrname': 'show_noscreen_error', 'value_default': nm.settings().SHOW_NOSCREEN_ERROR, 'tooltip': "Shows an error if requested screen for a node is not available.</p>" }, ), 'Show domain suffix:': ({ 'value': nm.settings().show_domain_suffix, 'settings': nm.settings(), 'attrname': 'show_domain_suffix', 'value_default': nm.settings().SHOW_DOMAIN_SUFFIX, 'tooltip': "<p>Shows the domain suffix of the host in the host description" " panel and node tree view.</p>" }, ), 'Transpose pub/sub description:': ({ 'value': nm.settings().transpose_pub_sub_descr, 'settings': nm.settings(), 'attrname': 'transpose_pub_sub_descr', 'value_default': nm.settings().TRANSPOSE_PUB_SUB_DESCR, 'tooltip': "<p>Transpose publisher/subscriber in description dock.</p>" }, ) } self.settings_model.init_settings(settings) # self.settingsTreeView.setSortingEnabled(True) self.settingsTreeView.sortByColumn(0, Qt.AscendingOrder) self.settingsTreeView.expandAll()
class LaunchFilesWidget(QDockWidget): ''' Launch file browser. ''' load_signal = Signal(str) ''' load the launch file ''' load_as_default_signal = Signal(str, str) ''' load the launch file as default (path, host) ''' edit_signal = Signal(list) ''' list of paths to open in an editor ''' transfer_signal = Signal(list) ''' list of paths selected for transfer ''' def __init__(self, parent=None): ''' Creates the window, connects the signals and init the class. ''' QDockWidget.__init__(self, parent) # initialize parameter self.__current_path = os.path.expanduser('~') # load the UI file ui_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'LaunchFilesDockWidget.ui') loadUi(ui_file, self) # initialize the view for the launch files self.launchlist_model = LaunchListModel() self.launchlist_proxyModel = QSortFilterProxyModel(self) self.launchlist_proxyModel.setSourceModel(self.launchlist_model) self.xmlFileView.setModel(self.launchlist_proxyModel) self.xmlFileView.setAlternatingRowColors(True) self.xmlFileView.activated.connect(self.on_launch_selection_activated) self.xmlFileView.setDragDropMode(QAbstractItemView.DragOnly) self.xmlFileView.setDragEnabled(True) sm = self.xmlFileView.selectionModel() sm.selectionChanged.connect(self.on_xmlFileView_selection_changed) # self.searchPackageLine.setVisible(False) self.searchPackageLine.textChanged.connect(self.set_package_filter) self.searchPackageLine.focusInEvent = self._searchline_focusInEvent # connect to the button signals self.refreshXmlButton.clicked.connect(self.on_refresh_xml_clicked) self.editXmlButton.clicked.connect(self.on_edit_xml_clicked) self.newXmlButton.clicked.connect(self.on_new_xml_clicked) self.openXmlButton.clicked.connect(self.on_open_xml_clicked) self.transferButton.clicked.connect(self.on_transfer_file_clicked) self.loadXmlButton.clicked.connect(self.on_load_xml_clicked) self.loadXmlAsDefaultButton.clicked.connect(self.on_load_as_default) # creates a default config menu start_menu = QMenu(self) self.loadDeafaultAtHostAct = QAction("&Load default config on host", self, statusTip="Loads the default config at given host", triggered=self.on_load_as_default_at_host) start_menu.addAction(self.loadDeafaultAtHostAct) self.loadXmlAsDefaultButton.setMenu(start_menu) # initialize the progress queue self.progress_queue = ProgressQueue(self.progressFrame_cfg, self.progressBar_cfg, self.progressCancelButton_cfg) def stop(self): ''' Cancel the executing queued actions. This method must be called at the exit! ''' self.progress_queue.stop() def on_launch_selection_activated(self, activated): ''' Tries to load the launch file, if one was activated. ''' selected = self._launchItemsFromIndexes(self.xmlFileView.selectionModel().selectedIndexes(), False) for item in selected: try: lfile = self.launchlist_model.expandItem(item.name, item.path, item.id) self.searchPackageLine.setText('') if lfile is not None: if item.isLaunchFile(): self.launchlist_model.add2LoadHistory(item.path) key_mod = QApplication.keyboardModifiers() if key_mod & Qt.ShiftModifier: self.load_as_default_signal.emit(item.path, None) elif key_mod & Qt.ControlModifier: self.launchlist_model.setPath(os.path.dirname(item.path)) else: self.load_signal.emit(item.path) elif item.isConfigFile(): self.edit_signal.emit([lfile]) except Exception as e: rospy.logwarn("Error while load launch file %s: %s" % (item, e)) WarningMessageBox(QMessageBox.Warning, "Load error", 'Error while load launch file:\n%s' % item.name, "%s" % e).exec_() # self.launchlist_model.reloadCurrentPath() def on_xmlFileView_selection_changed(self, selected, deselected): ''' On selection of a launch file, the buttons are enabled otherwise disabled. ''' selected = self._launchItemsFromIndexes(self.xmlFileView.selectionModel().selectedIndexes(), False) for item in selected: islaunch = item.isLaunchFile() isconfig = item.isConfigFile() self.editXmlButton.setEnabled(islaunch or isconfig) self.loadXmlButton.setEnabled(islaunch) self.transferButton.setEnabled(islaunch or isconfig) self.loadXmlAsDefaultButton.setEnabled(islaunch) def set_package_filter(self, text): self.launchlist_proxyModel.setFilterRegExp(QRegExp(text, Qt.CaseInsensitive, QRegExp.Wildcard)) def on_refresh_xml_clicked(self): ''' Reload the current path. ''' self.launchlist_model.reloadCurrentPath() self.launchlist_model.reloadPackages() self.editXmlButton.setEnabled(False) self.loadXmlButton.setEnabled(False) self.transferButton.setEnabled(False) self.loadXmlAsDefaultButton.setEnabled(False) def on_edit_xml_clicked(self): ''' Opens an XML editor to edit the launch file. ''' selected = self._launchItemsFromIndexes(self.xmlFileView.selectionModel().selectedIndexes(), False) for item in selected: path = self.launchlist_model.expandItem(item.name, item.path, item.id) if path is not None: self.edit_signal.emit([path]) def on_new_xml_clicked(self): ''' Creates a new launch file. ''' # get new file from open dialog, use last path if one exists open_path = self.__current_path if self.launchlist_model.currentPath is not None: open_path = self.launchlist_model.currentPath (fileName, _) = QFileDialog.getSaveFileName(self, "New launch file", open_path, "Config files (*.launch *.yaml);;All files (*)") if fileName: try: (pkg, _) = package_name(os.path.dirname(fileName)) # _:=pkg_path if pkg is None: WarningMessageBox(QMessageBox.Warning, "New File Error", 'The new file is not in a ROS package').exec_() return with open(fileName, 'w+') as f: f.write("<launch>\n" " <arg name=\"robot_ns\" default=\"my_robot\"/>\n" " <group ns=\"$(arg robot_ns)\">\n" " <param name=\"tf_prefix\" value=\"$(arg robot_ns)\"/>\n" "\n" " <node pkg=\"my_pkg\" type=\"my_node\" name=\"my_name\" >\n" " <param name=\"capability_group\" value=\"MY_GROUP\"/>\n" " </node>\n" " </group>\n" "</launch>\n" ) self.__current_path = os.path.dirname(fileName) self.launchlist_model.setPath(self.__current_path) self.edit_signal.emit([fileName]) except EnvironmentError as e: WarningMessageBox(QMessageBox.Warning, "New File Error", 'Error while create a new file', '%s' % e).exec_() def on_open_xml_clicked(self): (fileName, _) = QFileDialog.getOpenFileName(self, "Load launch file", self.__current_path, "Config files (*.launch);;All files (*)") if fileName: self.__current_path = os.path.dirname(fileName) self.launchlist_model.add2LoadHistory(fileName) self.load_signal.emit(fileName) def on_transfer_file_clicked(self): ''' Emit the signal to copy the selected file to a remote host. ''' selected = self._launchItemsFromIndexes(self.xmlFileView.selectionModel().selectedIndexes(), False) paths = list() for item in selected: path = self.launchlist_model.expandItem(item.name, item.path, item.id) if path is not None: paths.append(path) if paths: self.transfer_signal.emit(paths) def on_load_xml_clicked(self): ''' Tries to load the selected launch file. The button is only enabled and this method is called, if the button was enabled by on_launch_selection_clicked() ''' selected = self._launchItemsFromIndexes(self.xmlFileView.selectionModel().selectedIndexes(), False) for item in selected: path = self.launchlist_model.expandItem(item.name, item.path, item.id) if path is not None: self.launchlist_model.add2LoadHistory(path) self.load_signal.emit(path) def on_load_as_default_at_host(self): ''' Tries to load the selected launch file as default configuration. The button is only enabled and this method is called, if the button was enabled by on_launch_selection_clicked() ''' selected = self._launchItemsFromIndexes(self.xmlFileView.selectionModel().selectedIndexes(), False) for item in selected: path = self.launchlist_model.expandItem(item.name, item.path, item.id) if path is not None: params = {'Host': ('string', 'localhost')} dia = ParameterDialog(params) dia.setFilterVisible(False) dia.setWindowTitle('Start node on...') dia.resize(350, 120) dia.setFocusField('Host') if dia.exec_(): try: params = dia.getKeywords() host = params['Host'] rospy.loginfo("LOAD the launch file on host %s as default: %s" % (host, path)) self.launchlist_model.add2LoadHistory(path) self.load_as_default_signal.emit(path, host) except Exception, e: WarningMessageBox(QMessageBox.Warning, "Load default config error", 'Error while parse parameter', '%s' % e).exec_()
def filterAcceptsRow(self, source_row, source_parent): """ Tells by analysing the given row if it should be shown or not. This behaviour can be modified via setFilterRegExp method so that e.g. only the entries of a specific host can be shown. :param source_row: the source of the parent :type source_row: int :param source_parent: the source of the parent :type source_parent: QModelIndex :returns: True if the row should be shown :rtype: bool """ entries = [] item = source_parent.internalPointer() child = None if item is not None: if isinstance(item, TreeTopicItem): child = item.get_child( source_row, self.sourceModel().parent(source_parent).internalPointer()) else: child = source_parent.internalPointer().get_child(source_row) entries = [ child.get_type(), child.get_seuid(), child.get_state(), child.get_short_data() ] else: child = self.sourceModel().get_root_item().get_child(source_row) entries = [ child.get_type(), child.get_seuid(), child.get_state(), child.get_short_data() ] child_childs = child.get_childs( self.sourceModel().parent(source_parent).internalPointer()) for i in range(0, len(child_childs)): if self.filterAcceptsRow( i, self.sourceModel().index(source_row, 0, source_parent)): return True correct_type = False data = entries[0] if data[0] == "h": correct_type = True elif self.__show_nodes and data[0] == "n": correct_type = True elif self.__show_connections and data[0] == "c": if self.__show_subscribers: correct_type = True else: if child.is_subscriber: correct_type = False else: correct_type = True elif self.__show_topics is True: if data[0] == "t": correct_type = True if correct_type is False: return False if self.__hide_debug is True: for entry in self.__quiet_names: if entries[1].find(entry) is not -1: return False # todo: speed this implementation a lot up by not using the model!!! if self.__filter_string is not "": for i in range(0, len(entries)): if self.__filter_string in entries[i]: return QSortFilterProxyModel.filterAcceptsRow( self, source_row, source_parent) return False return QSortFilterProxyModel.filterAcceptsRow(self, source_row, source_parent)
class LaunchFilesWidget(QDockWidget): ''' Launch file browser. ''' load_signal = Signal(str, dict, str) ''' load the launch file with given arguments (launchfile, args, masteruri)''' load_profile_signal = Signal(str) ''' load the profile file ''' edit_signal = Signal(str) ''' list of paths to open in an editor ''' transfer_signal = Signal(list) ''' list of tuples of (url, path) selected for transfer ''' save_profile_signal = Signal(str) ''':ivar str save_profile_signa: the signal is emitted, to save profile. (current path selected in launch files)''' def __init__(self, parent=None): ''' Creates the window, connects the signals and init the class. ''' QDockWidget.__init__(self, parent) # initialize parameter self.__current_path = os.path.expanduser('~') # load the UI file ui_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'ui', 'LaunchFilesDockWidget.ui') loadUi(ui_file, self, custom_widgets={'EnhancedLineEdit': EnhancedLineEdit}) self.ui_button_progress_cancel_cfg.setIcon( nm.settings().icon('crystal_clear_button_close.png')) self.ui_button_reload.setIcon( nm.settings().icon('oxygen_view_refresh.png')) self.ui_button_edit.setIcon( nm.settings().icon('crystal_clear_edit_launch.png')) self.ui_button_new.setIcon(nm.settings().icon('crystal_clear_add.png')) self.ui_button_transfer.setIcon( nm.settings().icon('crystal_clear_launch_file_transfer.png')) self.ui_button_save_profile.setIcon( nm.settings().icon('crystal_clear_profile_new.png')) self.ui_button_load.setIcon( nm.settings().icon('crystal_clear_launch_file.png')) self._current_search = '' pal = self.palette() self._default_color = pal.color(QPalette.Window) # initialize the progress queue self.progress_queue = ProgressQueue(self.ui_frame_progress_cfg, self.ui_bar_progress_cfg, self.ui_button_progress_cancel_cfg, 'Launch File') # initialize the view for the launch files self.launchlist_model = LaunchListModel( progress_queue=self.progress_queue, viewobj=self.ui_file_view) self.launchlist_proxy_model = QSortFilterProxyModel(self) self.launchlist_proxy_model.setSourceModel(self.launchlist_model) self.name_delegate = HTMLDelegate(check_for_ros_names=False, palette=self.palette()) self.ui_file_view.setItemDelegateForColumn(0, self.name_delegate) self.ui_file_view.setModel(self.launchlist_proxy_model) self.ui_file_view.setAlternatingRowColors(True) self.ui_file_view.activated.connect(self.on_launch_selection_activated) self.ui_file_view.setDragDropMode(QAbstractItemView.DragOnly) self.ui_file_view.setDragEnabled(True) sm = self.ui_file_view.selectionModel() sm.selectionChanged.connect(self.on_ui_file_view_selection_changed) self.launchlist_model.pathlist_handled.connect( self.on_pathlist_handled) self.launchlist_model.error_on_path.connect(self.on_error_on_path) self.ui_search_line.refresh_signal.connect(self.set_package_filter) self.ui_search_line.stop_signal.connect(self.stop) # connect to the button signals self.ui_button_reload.clicked.connect(self.on_reload_clicked) self.ui_button_edit.clicked.connect(self.on_edit_xml_clicked) #self.ui_button_new.clicked.connect(self.on_new_xml_clicked) self.ui_button_transfer.clicked.connect(self.on_transfer_file_clicked) self.ui_button_save_profile.clicked.connect( self.on_save_profile_clicked) self.ui_button_load.clicked.connect(self.on_load_xml_clicked) # add menu to create fiel or directory self._menu_add = QMenu() create_file_action = QAction( nm.settings().icon('crystal_clear_launch_file_new.png'), "create file", self, statusTip="", triggered=self.on_new_xml_clicked) create_dir_action = QAction( nm.settings().icon('crystal_clear_folder.png'), "create directory", self, statusTip="", triggered=self.on_new_dir_clicked) self._menu_add.addAction(create_file_action) self._menu_add.addAction(create_dir_action) self.ui_button_new.setMenu(self._menu_add) self._masteruri2name = {} self._reload_timer = None def stop(self): ''' Cancel the executing queued actions. This method must be called at the exit! ''' self.progress_queue.stop() self.ui_search_line.set_process_active(False) self._stop_timer_reload() def set_current_master(self, masteruri, mastername): self.launchlist_model.set_current_master(masteruri, mastername) self._masteruri2name[masteruri.rstrip(os.path.sep)] = mastername try: color = QColor.fromRgb(nm.settings().host_color( self._masteruri2name[nmdurl.masteruri( self.launchlist_model.current_path)], self._default_color.rgb())) self._new_color(color) except Exception as _: pass # import traceback # print traceback.format_exc() # rospy.logwarn("Error while set color in launch dock: %s" % utf8(err)) def on_launch_selection_activated(self, activated): ''' Tries to load the launch file, if one was activated. ''' selected = self._pathItemsFromIndexes( self.ui_file_view.selectionModel().selectedIndexes(), False) for item in selected: try: self.ui_search_line.set_process_active(True) lfile = self.launchlist_model.expand_item(item.path, item.id) # self.ui_search_line.setText('') if lfile is not None: self.ui_search_line.set_process_active(False) if item.is_launch_file(): nm.settings().launch_history_add(item.path) self.load_signal.emit(item.path, {}, None) elif item.is_profile_file(): nm.settings().launch_history_add(item.path) self.load_profile_signal.emit(item.path) elif item.is_config_file(): self.edit_signal.emit(lfile) if self.launchlist_model.current_path: self.setWindowTitle( 'Launch @%s' % get_hostname(self.launchlist_model.current_grpc)) else: self.setWindowTitle('Launch files') except Exception as e: import traceback print(traceback.format_exc()) rospy.logwarn("Error while load launch file %s: %s" % (item, utf8(e))) MessageBox.warning( self, "Load error", 'Error while load launch file:\n%s' % item.name, "%s" % utf8(e)) try: color = QColor.fromRgb(nm.settings().host_color( self._masteruri2name[nmdurl.masteruri( self.launchlist_model.current_path)], self._default_color.rgb())) self._new_color(color) except Exception as _: pass # import traceback # print traceback.format_exc() # rospy.logwarn("Error while set color in launch dock: %s" % utf8(err)) def _new_color(self, color): bg_style_launch_dock = "QWidget#ui_dock_widget_contents { background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 %s, stop: 0.7 %s);}" % ( color.name(), self._default_color.name()) self.setStyleSheet("%s" % (bg_style_launch_dock)) def on_pathlist_handled(self, gpath): self.ui_search_line.set_process_active(False) self.ui_button_new.setEnabled(not self.launchlist_model.is_in_root) self._stop_timer_reload() def on_error_on_path(self, gpath): if gpath == self._current_search or gpath == self.launchlist_model.current_path: self.ui_search_line.set_process_active(False) if self.launchlist_model.is_in_root: self._reload_timer = threading.Timer( 2., nm.nmd().file.list_path_threaded) self._reload_timer.start() def _stop_timer_reload(self): if self._reload_timer is not None and self._reload_timer.is_alive(): try: self._reload_timer.cancel() self._reload_timer = None except Exception: pass def _on_timer_reload_callback(self, event=None): nm.nmd().file.list_path_threaded(self.launchlist_model.current_path) self._reload_timer = threading.Timer(2., nm.nmd().file.list_path_threaded) self._reload_timer.start() def on_launch_selection_changed(self, selected, deselected): print("selection launch changed") def load_file(self, path, args={}, masteruri=None): ''' Tries to load the launch file, if one was activated. ''' if path is not None: if path.endswith('.launch'): self.load_signal.emit(path, args, masteruri) elif path.endswith('.nmprofile'): self.load_profile_signal.emit(path) def on_ui_file_view_selection_changed(self, selected, deselected): ''' On selection of a launch file, the buttons are enabled otherwise disabled. ''' selected = self._pathItemsFromIndexes( self.ui_file_view.selectionModel().selectedIndexes(), False) for item in selected: islaunch = item.is_launch_file() isconfig = item.is_config_file() isprofile = item.is_profile_file() self.ui_button_edit.setEnabled(islaunch or isconfig or isprofile) self.ui_button_load.setEnabled(islaunch or isprofile) self.ui_button_transfer.setEnabled(islaunch or isconfig) def set_package_filter(self, text): if text: if text.startswith(os.path.sep): self._current_search = nmdurl.join( self.launchlist_model.current_grpc, text) self.launchlist_model.set_path(text) else: # search for a package self.launchlist_model.show_packages(text) self.ui_search_line.set_process_active(False) else: self.launchlist_model.reload_current_path() def on_reload_clicked(self): ''' Clear daemon's cache. ''' self.launchlist_model.reload_current_path(clear_cache=True) def on_edit_xml_clicked(self): ''' Opens an XML editor to edit the launch file. ''' selected = self._pathItemsFromIndexes( self.ui_file_view.selectionModel().selectedIndexes(), False) for item in selected: path = self.launchlist_model.expand_item(item.path, item.id) if path is not None: self.edit_signal.emit(path) def on_new_xml_clicked(self): ''' Creates a new launch file. ''' # get new file from open dialog, use last path if one exists if not self.launchlist_model.is_in_root: items = self.launchlist_model.add_new_item("new.launch", PathItem.LAUNCH_FILE) if items: index = self.launchlist_proxy_model.mapFromSource( self.launchlist_model.index(1, 0)) self.ui_file_view.selectionModel().select( index, QItemSelectionModel.Select) self.ui_file_view.setCurrentIndex(index) self.ui_file_view.edit(index) def on_new_dir_clicked(self): ''' Creates a new directory. ''' # get new file from open dialog, use last path if one exists if not self.launchlist_model.is_in_root: items = self.launchlist_model.add_new_item("new", PathItem.FOLDER) if items: index = self.launchlist_proxy_model.mapFromSource( self.launchlist_model.index(1, 0)) self.ui_file_view.selectionModel().select( index, QItemSelectionModel.Select) self.ui_file_view.setCurrentIndex(index) self.ui_file_view.edit(index) def on_transfer_file_clicked(self): ''' Emit the signal to copy the selected file to a remote host. ''' selected = self._pathItemsFromIndexes( self.ui_file_view.selectionModel().selectedIndexes(), False) paths = list() for item in selected: path = self.launchlist_model.expand_item(item.path, item.id) if path is not None: paths.append(path) if paths: self.transfer_signal.emit(paths) def on_save_profile_clicked(self): # save the profile _netloc, path = nmdurl.split(self.launchlist_model.current_path, with_scheme=True) self.save_profile_signal.emit(path) def on_load_xml_clicked(self): ''' Tries to load the selected launch file. The button is only enabled and this method is called, if the button was enabled by on_launch_selection_clicked() ''' selected = self._pathItemsFromIndexes( self.ui_file_view.selectionModel().selectedIndexes(), False) for item in selected: path = self.launchlist_model.expand_item(item.path, item.id) if path is not None: nm.settings().launch_history_add(item.path) self.load_signal.emit(path, {}, None) def _pathItemsFromIndexes(self, indexes, recursive=True): result = [] for index in indexes: if index.column() == 0: model_index = self.launchlist_proxy_model.mapToSource(index) item = self.launchlist_model.itemFromIndex(model_index) if item is not None and isinstance(item, PathItem): result.append(item) return result def keyPressEvent(self, event): ''' Defines some of shortcuts for navigation/management in launch list view or topics view. ''' key_mod = QApplication.keyboardModifiers() if not self.ui_file_view.state() == QAbstractItemView.EditingState: # remove history file from list by pressing DEL if event == QKeySequence.Delete or (event.key() == Qt.Key_Delete and key_mod & Qt.ShiftModifier): selected = self._pathItemsFromIndexes( self.ui_file_view.selectionModel().selectedIndexes(), False) for item in selected: if item in nm.settings().launch_history: nm.settings().launch_history_remove(item.path) self.launchlist_model.reload_current_path() elif not self.launchlist_model.is_in_root: if key_mod & Qt.ShiftModifier: rem_uri, rem_path = nmdurl.split(item.path) host = rem_uri.split(':') result = MessageBox.question( self, "Delete Question", "Delete %s\n@ %s" % (rem_path, host[0]), buttons=MessageBox.No | MessageBox.Yes) if result == MessageBox.Yes: try: nm.nmd().file.delete(item.path) self.launchlist_model.reload_current_path( clear_cache=True) except Exception as e: rospy.logwarn("Error while delete %s: %s" % (item.path, utf8(e))) MessageBox.warning( self, "Delete error", 'Error while delete:\n%s' % item.name, "%s" % utf8(e)) else: MessageBox.information( self, "Delete Info", "Use Shift+Del to delete files or directories", buttons=MessageBox.Ok) elif not key_mod and event.key( ) == Qt.Key_F4 and self.ui_button_edit.isEnabled(): # open selected launch file in xml editor by F4 self.on_edit_xml_clicked() elif event == QKeySequence.Find: # set focus to filter box for packages self.ui_search_line.setFocus(Qt.ActiveWindowFocusReason) elif event == QKeySequence.Paste: # paste files from clipboard self.launchlist_model.paste_from_clipboard() elif event == QKeySequence.Copy: # copy the selected items as file paths into clipboard selected = self.ui_file_view.selectionModel().selectedIndexes() indexes = [] for s in selected: indexes.append(self.launchlist_proxy_model.mapToSource(s)) self.launchlist_model.copy_to_clipboard(indexes) if self.ui_search_line.hasFocus() and event.key() == Qt.Key_Escape: # cancel package filtering on pressing ESC self.launchlist_model.reload_current_path() self.ui_search_line.setText('') self.ui_file_view.setFocus(Qt.ActiveWindowFocusReason) QDockWidget.keyReleaseEvent(self, event)
def __init__(self, parent=None): ''' Creates the window, connects the signals and init the class. ''' QDockWidget.__init__(self, parent) # initialize parameter self.__current_path = os.path.expanduser('~') # load the UI file ui_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'ui', 'LaunchFilesDockWidget.ui') loadUi(ui_file, self, custom_widgets={'EnhancedLineEdit': EnhancedLineEdit}) self.hostLabel.setVisible(False) self.ui_button_progress_cancel_cfg.setIcon( nm.settings().icon('crystal_clear_button_close.png')) self.ui_button_reload.setIcon( nm.settings().icon('oxygen_view_refresh.png')) self.ui_button_edit.setIcon( nm.settings().icon('crystal_clear_edit_launch.png')) self.ui_button_new.setIcon(nm.settings().icon('crystal_clear_add.png')) self.ui_button_transfer.setIcon( nm.settings().icon('crystal_clear_launch_file_transfer.png')) self.ui_button_save_profile.setIcon( nm.settings().icon('crystal_clear_profile_new.png')) self.ui_button_load.setIcon( nm.settings().icon('crystal_clear_launch_file.png')) self._current_search = '' pal = self.palette() self._default_color = pal.color(QPalette.Window) # initialize the progress queue self.progress_queue = ProgressQueue(self.ui_frame_progress_cfg, self.ui_bar_progress_cfg, self.ui_button_progress_cancel_cfg, 'Launch File') # initialize the view for the launch files self.launchlist_model = LaunchListModel( progress_queue=self.progress_queue, viewobj=self.ui_file_view) self.launchlist_proxy_model = QSortFilterProxyModel(self) self.launchlist_proxy_model.setSourceModel(self.launchlist_model) self.name_delegate = HTMLDelegate(check_for_ros_names=False, palette=self.palette()) self.ui_file_view.setItemDelegateForColumn(0, self.name_delegate) self.ui_file_view.setModel(self.launchlist_proxy_model) self.ui_file_view.setAlternatingRowColors(True) self.ui_file_view.activated.connect(self.on_launch_selection_activated) self.ui_file_view.setDragDropMode(QAbstractItemView.DragOnly) self.ui_file_view.setDragEnabled(True) sm = self.ui_file_view.selectionModel() sm.selectionChanged.connect(self.on_ui_file_view_selection_changed) self.launchlist_model.pathlist_handled.connect( self.on_pathlist_handled) self.launchlist_model.error_on_path.connect(self.on_error_on_path) self.ui_search_line.refresh_signal.connect(self.set_package_filter) self.ui_search_line.stop_signal.connect(self.stop) # connect to the button signals self.ui_button_reload.clicked.connect(self.on_reload_clicked) self.ui_button_edit.clicked.connect(self.on_edit_xml_clicked) #self.ui_button_new.clicked.connect(self.on_new_xml_clicked) self.ui_button_transfer.clicked.connect(self.on_transfer_file_clicked) self.ui_button_save_profile.clicked.connect( self.on_save_profile_clicked) self.ui_button_load.clicked.connect(self.on_load_xml_clicked) # add menu to create fiel or directory self._menu_add = QMenu() create_file_action = QAction( nm.settings().icon('crystal_clear_launch_file_new.png'), "create file", self, statusTip="", triggered=self.on_new_xml_clicked) create_dir_action = QAction( nm.settings().icon('crystal_clear_folder.png'), "create directory", self, statusTip="", triggered=self.on_new_dir_clicked) self._menu_add.addAction(create_file_action) self._menu_add.addAction(create_dir_action) self.ui_button_new.setMenu(self._menu_add) self._masteruri2name = {} self._reload_timer = None self._first_path = self.launchlist_model.current_path
def setFilterRegExp(self, string): self.invalidateFilter() QSortFilterProxyModel.setFilterRegExp(self, string)
class SettingsWidget(QDockWidget): ''' Settings widget to handle the settings changes. The changes will direct change the settings of the GUI. ''' def __init__(self, parent=None): ''' Creates the window, connects the signals and init the class. ''' QDockWidget.__init__(self, parent) # load the UI file settings_dock_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'SettingsDockWidget.ui') loadUi(settings_dock_file, self) # initialize the settings view model self.settings_model = SettingsModel() self.settings_proxyModel = QSortFilterProxyModel(self) self.settings_proxyModel.setSourceModel(self.settings_model) self.settingsTreeView.setModel(self.settings_proxyModel) self.settingsTreeView.setAlternatingRowColors(True) for i, (_, width) in enumerate(SettingsModel.header): self.settingsTreeView.setColumnWidth(i, width) self.item_delegate = ItemDelegate() self.item_delegate.settings_path_changed_signal.connect(self.reload_settings) self.settingsTreeView.setItemDelegateForColumn(1, self.item_delegate) self.reload_settings() def reload_settings(self): ''' Load the current settings data into the model. The settings itself will not be loaded. ''' settings = {'Default user:'******'value': nm.settings().default_user, 'settings': nm.settings(), 'attrname': 'default_user', 'value_default': nm.settings().USER_DEFAULT, 'tooltip': '<p>The user used for ssh connection to remote hosts</p>' },), 'Launch history length:': ({'value': nm.settings().launch_history_length, 'settings': nm.settings(), 'attrname': 'launch_history_length', 'value_default': nm.settings().LAUNCH_HISTORY_LENGTH, 'value_min': 0, 'value_max': 25, 'tooltip': '<p>The count of recent ' 'loaded launch files displayed in the root ' 'of the <span style=" font-weight:600;">launch ' 'files</span> view.</p>' },), 'Param history length:': ({'value': nm.settings().param_history_length, 'settings': nm.settings(), 'attrname': 'param_history_length', 'value_default': nm.settings().PARAM_HISTORY_LENGTH, 'value_min': 0, 'value_max': 25, 'tooltip': '<p>The count of parameters stored which ' 'are entered in a parameter dialog (Launch file arguments, ' 'paramter server, publishing to a topic, service call)</p>' },), 'Settings path:': ({'value': nm.settings().cfg_path, 'settings': nm.settings(), 'attrname': 'cfg_path', 'edit_type': SettingsValueItem.EDIT_TYPE_FOLDER, 'value_default': nm.settings().CFG_PATH, 'tooltip': '' },), 'Robot icon path:': ({'value': nm.settings().robots_path, 'settings': nm.settings(), 'attrname': 'robots_path', 'edit_type': SettingsValueItem.EDIT_TYPE_FOLDER, 'value_default': nm.settings().ROBOTS_DIR, 'tooltip': '<p>The path to the folder with robot images ' '(<span style=" font-weight:600;">.png</span>).' 'The images with robot name will be displayed in the ' 'info bar.</p>' },), 'Show files extensions:': ({'value': ', '.join(nm.settings().launch_view_file_ext), 'settings': nm.settings(), 'attrname': 'launch_view_file_ext', 'value_default': ', '.join(nm.settings().LAUNCH_VIEW_EXT), 'tooltip': '<p>Files that are displayed next to Launch ' 'files in the <span style="font-weight:600;">' 'launch files</span> view</p>' },), 'Store window layout:': ({'value': nm.settings().store_geometry, 'settings': nm.settings(), 'attrname': 'store_geometry', 'value_default': nm.settings().STORE_GEOMETRY, 'tooltip': '' },), 'Max time difference:': ({'value': nm.settings().max_timediff, 'settings': nm.settings(), 'attrname': 'max_timediff', 'value_default': nm.settings().MAX_TIMEDIFF, 'tooltip': '<p>Shows a warning if the time difference to ' 'remote host is greater than this value</p>' },), 'Autoupdate:': ({'value': nm.settings().autoupdate, 'settings': nm.settings(), 'attrname': 'autoupdate', 'value_default': nm.settings().AUTOUPDATE, 'tooltip': '<p>By default node manager updates the current ' 'state on changes. You can deactivate this behavior to ' 'reduce the network load. If autoupdate is deactivated ' 'you must refresh the state manually.</p>' },), 'Start sync with discovery:': ({'value': nm.settings().start_sync_with_discovery, 'settings': nm.settings(), 'attrname': 'start_sync_with_discovery', 'value_default': nm.settings().START_SYNC_WITH_DISCOVERY, 'tooltip': "<p>Sets 'start sync' in 'Start' master discovery " "dialog to True, if this option is set to true.</p>" },), 'Confirm exit when closing:': ({'value': nm.settings().confirm_exit_when_closing, 'settings': nm.settings(), 'attrname': 'confirm_exit_when_closing', 'value_default': nm.settings().CONFIRM_EXIT_WHEN_CLOSING, 'tooltip': "<p>Shows on closing of node_manager a dialog to stop " "all ROS nodes if this option is set to true.</p>" },), 'Highlight xml blocks:': ({'value': nm.settings().highlight_xml_blocks, 'settings': nm.settings(), 'attrname': 'highlight_xml_blocks', 'value_default': nm.settings().HIGHLIGHT_XML_BLOCKS, 'tooltip': "<p>Highlights the current selected XML block, while " "editing ROS launch file.</p>" },), 'Colorize hosts:': ({'value': nm.settings().colorize_hosts, 'settings': nm.settings(), 'attrname': 'colorize_hosts', 'value_default': nm.settings().COLORIZE_HOSTS, 'tooltip': "<p>Determine automatic a default color for each host if True. " "Manually setted color will be prefered. You can select the color by " "double-click on hostname in description panel. To remove a setted color " "delete it manually from $HOME/.ros/node_manager/settings.ini</p>" },), 'Transpose pub/sub description:': ({'value': nm.settings().transpose_pub_sub_descr, 'settings': nm.settings(), 'attrname': 'transpose_pub_sub_descr', 'value_default': nm.settings().TRANSPOSE_PUB_SUB_DESCR, 'tooltip': "<p>Transpose publisher/subscriber in description dock.</p>" },) } self.settings_model.init_settings(settings) # self.settingsTreeView.setSortingEnabled(True) self.settingsTreeView.sortByColumn(0, Qt.AscendingOrder) self.settingsTreeView.expandAll()