Home | Trees | Indices | Help |
---|
|
1 # Software License Agreement (BSD License) 2 # 3 # Copyright (c) 2012, Fraunhofer FKIE/US, Alexander Tiderko 4 # All rights reserved. 5 # 6 # Redistribution and use in source and binary forms, with or without 7 # modification, are permitted provided that the following conditions 8 # are met: 9 # 10 # * Redistributions of source code must retain the above copyright 11 # notice, this list of conditions and the following disclaimer. 12 # * Redistributions in binary form must reproduce the above 13 # copyright notice, this list of conditions and the following 14 # disclaimer in the documentation and/or other materials provided 15 # with the distribution. 16 # * Neither the name of Fraunhofer nor the names of its 17 # contributors may be used to endorse or promote products derived 18 # from this software without specific prior written permission. 19 # 20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 # POSSIBILITY OF SUCH DAMAGE. 32 from python_qt_binding import loadUi 33 from python_qt_binding.QtCore import QSize, Qt, Signal 34 try: 35 from python_qt_binding.QtGui import QSortFilterProxyModel 36 from python_qt_binding.QtGui import QAbstractItemDelegate, QStyledItemDelegate, QHBoxLayout 37 from python_qt_binding.QtGui import QCheckBox, QComboBox, QDockWidget, QFileDialog, QLineEdit, QSpinBox, QDoubleSpinBox, QPushButton, QWidget 38 from python_qt_binding.QtGui import QStyleOptionViewItemV4 as QStyleOptionViewItem 39 except: 40 from python_qt_binding.QtWidgets import QAbstractItemDelegate, QStyledItemDelegate, QHBoxLayout 41 from python_qt_binding.QtWidgets import QCheckBox, QComboBox, QDockWidget, QFileDialog, QLineEdit, QSpinBox, QDoubleSpinBox, QPushButton, QWidget 42 from python_qt_binding.QtWidgets import QStyleOptionViewItem 43 from python_qt_binding.QtCore import QSortFilterProxyModel 44 import os 45 46 import node_manager_fkie as nm 47 48 from .settings_model import SettingsModel, SettingsValueItem 49 5052 ''' 53 Settings widget to handle the settings changes. The changes will direct change 54 the settings of the GUI. 55 ''' 56207 20858 ''' 59 Creates the window, connects the signals and init the class. 60 ''' 61 QDockWidget.__init__(self, parent) 62 # load the UI file 63 settings_dock_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'SettingsDockWidget.ui') 64 loadUi(settings_dock_file, self) 65 # initialize the settings view model 66 self.settings_model = SettingsModel() 67 self.settings_proxyModel = QSortFilterProxyModel(self) 68 self.settings_proxyModel.setSourceModel(self.settings_model) 69 self.settingsTreeView.setModel(self.settings_proxyModel) 70 self.settingsTreeView.setAlternatingRowColors(True) 71 for i, (_, width) in enumerate(SettingsModel.header): 72 self.settingsTreeView.setColumnWidth(i, width) 73 self.item_delegate = ItemDelegate() 74 self.item_delegate.settings_path_changed_signal.connect(self.reload_settings) 75 self.settingsTreeView.setItemDelegateForColumn(1, self.item_delegate) 76 self.reload_settings()7779 ''' 80 Load the current settings data into the model. The settings itself will not 81 be loaded. 82 ''' 83 settings = {'Default user:': ({'value': nm.settings().default_user, 84 'settings': nm.settings(), 85 'attrname': 'default_user', 86 'value_default': nm.settings().USER_DEFAULT, 87 'tooltip': '<p>The user used for ssh connection to remote hosts</p>' 88 },), 89 'Launch history length:': ({'value': nm.settings().launch_history_length, 90 'settings': nm.settings(), 91 'attrname': 'launch_history_length', 92 'value_default': nm.settings().LAUNCH_HISTORY_LENGTH, 93 'value_min': 0, 94 'value_max': 25, 95 'tooltip': '<p>The count of recent ' 96 'loaded launch files displayed in the root ' 97 'of the <span style=" font-weight:600;">launch ' 98 'files</span> view.</p>' 99 },), 100 'Param history length:': ({'value': nm.settings().param_history_length, 101 'settings': nm.settings(), 102 'attrname': 'param_history_length', 103 'value_default': nm.settings().PARAM_HISTORY_LENGTH, 104 'value_min': 0, 105 'value_max': 25, 106 'tooltip': '<p>The count of parameters stored which ' 107 'are entered in a parameter dialog (Launch file arguments, ' 108 'paramter server, publishing to a topic, service call)</p>' 109 },), 110 111 'Settings path:': ({'value': nm.settings().cfg_path, 112 'settings': nm.settings(), 113 'attrname': 'cfg_path', 114 'edit_type': SettingsValueItem.EDIT_TYPE_FOLDER, 115 'value_default': nm.settings().CFG_PATH, 116 'tooltip': '' 117 },), 118 'Robot icon path:': ({'value': nm.settings().robots_path, 119 'settings': nm.settings(), 120 'attrname': 'robots_path', 121 'edit_type': SettingsValueItem.EDIT_TYPE_FOLDER, 122 'value_default': nm.settings().ROBOTS_DIR, 123 'tooltip': '<p>The path to the folder with robot images ' 124 '(<span style=" font-weight:600;">.png</span>).' 125 'The images with robot name will be displayed in the ' 126 'info bar.</p>' 127 },), 128 'Show files extensions:': ({'value': ', '.join(nm.settings().launch_view_file_ext), 129 'settings': nm.settings(), 130 'attrname': 'launch_view_file_ext', 131 'value_default': ', '.join(nm.settings().LAUNCH_VIEW_EXT), 132 'tooltip': '<p>Files that are displayed next to Launch ' 133 'files in the <span style="font-weight:600;">' 134 'launch files</span> view</p>' 135 },), 136 'Store window layout:': ({'value': nm.settings().store_geometry, 137 'settings': nm.settings(), 138 'attrname': 'store_geometry', 139 'value_default': nm.settings().STORE_GEOMETRY, 140 'tooltip': '' 141 },), 142 'Max time difference:': ({'value': nm.settings().max_timediff, 143 'settings': nm.settings(), 144 'attrname': 'max_timediff', 145 'value_default': nm.settings().MAX_TIMEDIFF, 146 'value_step': 0.1, 147 'tooltip': '<p>Shows a warning if the time difference to ' 148 'remote host is greater than this value</p>' 149 },), 150 'Autoupdate:': ({'value': nm.settings().autoupdate, 151 'settings': nm.settings(), 152 'attrname': 'autoupdate', 153 'value_default': nm.settings().AUTOUPDATE, 154 'tooltip': '<p>By default node manager updates the current ' 155 'state on changes. You can deactivate this behavior to ' 156 'reduce the network load. If autoupdate is deactivated ' 157 'you must refresh the state manually.</p>' 158 },), 159 'Start sync with discovery:': ({'value': nm.settings().start_sync_with_discovery, 160 'settings': nm.settings(), 161 'attrname': 'start_sync_with_discovery', 162 'value_default': nm.settings().START_SYNC_WITH_DISCOVERY, 163 'tooltip': "<p>Sets 'start sync' in 'Start' master discovery " 164 "dialog to True, if this option is set to true.</p>" 165 },), 166 'Confirm exit when closing:': ({'value': nm.settings().confirm_exit_when_closing, 167 'settings': nm.settings(), 168 'attrname': 'confirm_exit_when_closing', 169 'value_default': nm.settings().CONFIRM_EXIT_WHEN_CLOSING, 170 'tooltip': "<p>Shows on closing of node_manager a dialog to stop " 171 "all ROS nodes if this option is set to true.</p>" 172 },), 173 'Highlight xml blocks:': ({'value': nm.settings().highlight_xml_blocks, 174 'settings': nm.settings(), 175 'attrname': 'highlight_xml_blocks', 176 'value_default': nm.settings().HIGHLIGHT_XML_BLOCKS, 177 'tooltip': "<p>Highlights the current selected XML block, while " 178 "editing ROS launch file.</p>" 179 },), 180 'Colorize hosts:': ({'value': nm.settings().colorize_hosts, 181 'settings': nm.settings(), 182 'attrname': 'colorize_hosts', 183 'value_default': nm.settings().COLORIZE_HOSTS, 184 'tooltip': "<p>Determine automatic a default color for each host if True. " 185 "Manually setted color will be prefered. You can select the color by " 186 "double-click on hostname in description panel. To remove a setted color " 187 "delete it manually from $HOME/.ros/node_manager/settings.ini</p>" 188 },), 189 'Show domain suffix:': ({'value': nm.settings().show_domain_suffix, 190 'settings': nm.settings(), 191 'attrname': 'show_domain_suffix', 192 'value_default': nm.settings().SHOW_DOMAIN_SUFFIX, 193 'tooltip': "<p>Shows the domain suffix of the host in the host description" 194 " panel and node tree view.</p>" 195 },), 196 'Transpose pub/sub description:': ({'value': nm.settings().transpose_pub_sub_descr, 197 'settings': nm.settings(), 198 'attrname': 'transpose_pub_sub_descr', 199 'value_default': nm.settings().TRANSPOSE_PUB_SUB_DESCR, 200 'tooltip': "<p>Transpose publisher/subscriber in description dock.</p>" 201 },) 202 } 203 self.settings_model.init_settings(settings) 204 # self.settingsTreeView.setSortingEnabled(True) 205 self.settingsTreeView.sortByColumn(0, Qt.AscendingOrder) 206 self.settingsTreeView.expandAll()210 ''' 211 This ItemDelegate provides editors for different setting types in settings view. 212 ''' 213 214 settings_path_changed_signal = Signal() 215 216 reload_settings = False 217307 308219 ''' 220 Creates a editor in the TreeView depending on type of the settings data. 221 ''' 222 item = self._itemFromIndex(index) 223 if item.edit_type() == SettingsValueItem.EDIT_TYPE_AUTODETECT: 224 if isinstance(item.value(), bool): 225 box = QCheckBox(parent) 226 box.setFocusPolicy(Qt.StrongFocus) 227 box.setAutoFillBackground(True) 228 box.stateChanged.connect(self.edit_finished) 229 return box 230 elif isinstance(item.value(), int): 231 box = QSpinBox(parent) 232 box.setValue(item.value()) 233 if not item.value_min() is None: 234 box.setMinimum(item.value_min()) 235 if not item.value_max() is None: 236 box.setMaximum(item.value_max()) 237 if not item.value_step() is None: 238 box.setSingleStep(item.value_step()) 239 return box 240 elif isinstance(item.value(), float): 241 box = QDoubleSpinBox(parent) 242 box.setValue(item.value()) 243 if not item.value_min() is None: 244 box.setMinimum(item.value_min()) 245 if not item.value_max() is None: 246 box.setMaximum(item.value_max()) 247 if not item.value_step() is None: 248 box.setSingleStep(item.value_step()) 249 box.setDecimals(3) 250 return box 251 elif item.edit_type() == SettingsValueItem.EDIT_TYPE_FOLDER: 252 editor = PathEditor(item.value(), parent) 253 editor.editing_finished_signal.connect(self.edit_finished) 254 return editor 255 elif item.edit_type() == SettingsValueItem.EDIT_TYPE_LIST: 256 box = QComboBox(parent) 257 box.addItems(item.value_list()) 258 index = box.findText(item.value()) 259 if index >= 0: 260 box.setCurrentIndex(index) 261 box.setEditable(False) 262 return box 263 return QStyledItemDelegate.createEditor(self, parent, option, index)264 265 # def setEditorData(self, editor, index): 266 # print "setEditorData" 267 # QStyledItemDelegate.setEditorData(self, editor, index) 268 269 # def updateEditorGeometry(self, editor, option, index): 270 # print "updateEditorGeometry", option.rect.width() 271 # editor.setMaximumSize(option.rect.width(), option.rect.height()) 272 # QStyledItemDelegate.updateEditorGeometry(self, editor, option, index) 273275 if isinstance(editor, PathEditor): 276 cfg_path = nm.settings().cfg_path 277 model.setData(index, editor.path) 278 self.reload_settings = (cfg_path != nm.settings().cfg_path) 279 else: 280 QStyledItemDelegate.setModelData(self, editor, model, index)281283 ''' 284 Determines and returns the size of the text after the format. 285 @see: U{http://www.pyside.org/docs/pyside/PySide/QtGui/QAbstractItemDelegate.html#PySide.QtGui.QAbstractItemDelegate} 286 ''' 287 options = QStyleOptionViewItem(option) 288 self.initStyleOption(options, index) 289 return QSize(options.rect.width(), 25)290292 editor = self.sender() 293 # The commitData signal must be emitted when we've finished editing 294 # and need to write our changed back to the model. 295 self.commitData.emit(editor) 296 self.closeEditor.emit(editor, QAbstractItemDelegate.NoHint) 297 if self.reload_settings: 298 self.reload_settings = False 299 self.settings_path_changed_signal.emit()300310 ''' 311 This is a path editor used as ItemDeligate in settings view. This editor 312 provides an additional button for directory selection dialog. 313 ''' 314 315 editing_finished_signal = Signal() 316354318 QWidget.__init__(self, parent) 319 self.path = path 320 self._layout = QHBoxLayout(self) 321 self._layout.setContentsMargins(0, 0, 0, 0) 322 self._layout.setSpacing(0) 323 self._button = QPushButton('...') 324 self._button.setMaximumSize(QSize(24, 20)) 325 self._button.clicked.connect(self._on_path_select_clicked) 326 self._layout.addWidget(self._button) 327 self._lineedit = QLineEdit(path) 328 self._lineedit.returnPressed.connect(self._on_editing_finished) 329 self._layout.addWidget(self._lineedit) 330 self.setLayout(self._layout) 331 self.setFocusProxy(self._button) 332 self.setAutoFillBackground(True)333335 # Workaround for QFileDialog.getExistingDirectory because it do not 336 # select the configuration folder in the dialog 337 self.dialog = QFileDialog(self, caption='Select a new settings folder') 338 self.dialog.setOption(QFileDialog.HideNameFilterDetails, True) 339 self.dialog.setFileMode(QFileDialog.Directory) 340 self.dialog.setDirectory(self.path) 341 if self.dialog.exec_(): 342 fileNames = self.dialog.selectedFiles() 343 path = fileNames[0] 344 if os.path.isfile(path): 345 path = os.path.basename(path) 346 self._lineedit.setText(path) 347 self.path = dir 348 self.editing_finished_signal.emit()349351 if self._lineedit.text(): 352 self.path = self._lineedit.text() 353 self.editing_finished_signal.emit()
Home | Trees | Indices | Help |
---|
Generated by Epydoc 3.0.1 on Thu Apr 27 02:41:56 2017 | http://epydoc.sourceforge.net |