Package node_manager_fkie :: Module sync_dialog
[frames] | no frames]

Source Code for Module node_manager_fkie.sync_dialog

  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   
 33  from python_qt_binding import QtGui 
 34  from python_qt_binding import QtCore 
 35   
 36  import os 
 37  import threading 
 38   
 39  import node_manager_fkie as nm 
 40  from common import is_package 
 41  from detailed_msg_box import WarningMessageBox 
 42  from xml_editor import Editor 
43 44 -class SyncHighlighter(QtGui.QSyntaxHighlighter):
45 ''' 46 Enabled the syntax highlightning for the sync interface. 47 ''' 48
49 - def __init__(self, parent=None):
50 QtGui.QSyntaxHighlighter.__init__(self, parent) 51 self.rules = [] 52 self.commentStart = QtCore.QRegExp("#") 53 self.commentEnd = QtCore.QRegExp("\n") 54 self.commentFormat = QtGui.QTextCharFormat() 55 self.commentFormat.setFontItalic(True) 56 self.commentFormat.setForeground(QtCore.Qt.darkGray) 57 f = QtGui.QTextCharFormat() 58 r = QtCore.QRegExp() 59 r.setMinimal(True) 60 f.setFontWeight(QtGui.QFont.Normal) 61 f.setForeground (QtCore.Qt.darkBlue) 62 tagList = ["\\bignore_hosts\\b", "\\bsync_hosts\\b", 63 "\\bignore_nodes\\b", "\\bsync_nodes\\b", 64 "\\bignore_topics\\b", "\\bignore_publishers\\b", 65 "\\bignore_topics\\b", "\\bsync_topics\\b", 66 "\\bignore_subscribers\\b", "\\bsync_services\\b", 67 "\\bsync_topics_on_demand\\b", "\\bsync_remote_nodes\\b"] 68 for tag in tagList: 69 r.setPattern(tag) 70 self.rules.append((QtCore.QRegExp(r), QtGui.QTextCharFormat(f))) 71 72 f.setForeground(QtCore.Qt.darkGreen) 73 f.setFontWeight(QtGui.QFont.Bold) 74 attrList = ["\\b\\*|\\*\\B|\\/\\*"] 75 for attr in attrList: 76 r.setPattern(attr) 77 self.rules.append((QtCore.QRegExp(r), QtGui.QTextCharFormat(f)))
78 79 # f.setForeground(QtCore.Qt.red) 80 # f.setFontWeight(QtGui.QFont.Bold) 81 # attrList = ["\\s\\*"] 82 # for attr in attrList: 83 # r.setPattern(attr) 84 # self.rules.append((QtCore.QRegExp(r), QtGui.QTextCharFormat(f))) 85 86
87 - def highlightBlock(self, text):
88 for pattern, myformat in self.rules: 89 index = pattern.indexIn(text) 90 while index >= 0: 91 length = pattern.matchedLength() 92 self.setFormat(index, length, myformat) 93 index = pattern.indexIn(text, index + length) 94 95 self.setCurrentBlockState(0) 96 startIndex = 0 97 if self.previousBlockState() != 1: 98 startIndex = self.commentStart.indexIn(text) 99 if startIndex >= 0: 100 commentLength = len(text) - startIndex 101 self.setFormat(startIndex, commentLength, self.commentFormat)
102
103 104 -class SyncDialog(QtGui.QDialog):
105 ''' 106 A dialog to set the sync options. 107 ''' 108
109 - def __init__(self, parent=None):
110 QtGui.QDialog.__init__(self, parent) 111 # self.host = host 112 self.setWindowIcon(QtGui.QIcon(":/icons/irondevil_sync.png")) 113 self.setWindowTitle('Sync') 114 self.verticalLayout = QtGui.QVBoxLayout(self) 115 self.verticalLayout.setObjectName("verticalLayout") 116 self.resize(350,190) 117 118 self.toolButton_SyncAll = QtGui.QToolButton(self) 119 sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) 120 sizePolicy.setHorizontalStretch(0) 121 sizePolicy.setVerticalStretch(2) 122 sizePolicy.setHeightForWidth(self.toolButton_SyncAll.sizePolicy().hasHeightForWidth()) 123 self.toolButton_SyncAll.setSizePolicy(sizePolicy) 124 self.toolButton_SyncAll.setObjectName("toolButton_SyncAll") 125 self.verticalLayout.addWidget(self.toolButton_SyncAll) 126 self.toolButton_SyncAll.setText(QtGui.QApplication.translate("Form", "Sync All", None, QtGui.QApplication.UnicodeUTF8)) 127 self.toolButton_SyncAll.clicked.connect(self._on_sync_all_clicked) 128 129 # self.toolButton_SyncAllAnyMsg = QtGui.QToolButton(self) 130 # sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) 131 # sizePolicy.setHorizontalStretch(0) 132 # sizePolicy.setVerticalStretch(1) 133 # sizePolicy.setHeightForWidth(self.toolButton_SyncAllAnyMsg.sizePolicy().hasHeightForWidth()) 134 # self.toolButton_SyncAllAnyMsg.setSizePolicy(sizePolicy) 135 # self.toolButton_SyncAllAnyMsg.setObjectName("toolButton_SyncAllAnyMsg") 136 # self.verticalLayout.addWidget(self.toolButton_SyncAllAnyMsg) 137 # self.toolButton_SyncAllAnyMsg.setText(QtGui.QApplication.translate("Form", "Sync all (+AnyMsg)", None, QtGui.QApplication.UnicodeUTF8)) 138 # self.toolButton_SyncAllAnyMsg.clicked.connect(self._on_sync_all_anymsg_clicked) 139 140 self.toolButton_SyncTopicOnDemand = QtGui.QToolButton(self) 141 sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) 142 sizePolicy.setHorizontalStretch(0) 143 sizePolicy.setVerticalStretch(1) 144 sizePolicy.setHeightForWidth(self.toolButton_SyncTopicOnDemand.sizePolicy().hasHeightForWidth()) 145 self.toolButton_SyncTopicOnDemand.setSizePolicy(sizePolicy) 146 self.toolButton_SyncTopicOnDemand.setObjectName("toolButton_SyncTopicOnDemand") 147 self.verticalLayout.addWidget(self.toolButton_SyncTopicOnDemand) 148 self.toolButton_SyncTopicOnDemand.setText(QtGui.QApplication.translate("Form", "Sync only topics on demand", None, QtGui.QApplication.UnicodeUTF8)) 149 self.toolButton_SyncTopicOnDemand.clicked.connect(self._on_sync_topics_on_demand_clicked) 150 151 self.toolButton_SelectInterface = QtGui.QToolButton(self) 152 sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) 153 sizePolicy.setHorizontalStretch(0) 154 sizePolicy.setVerticalStretch(1) 155 sizePolicy.setHeightForWidth(self.toolButton_SelectInterface.sizePolicy().hasHeightForWidth()) 156 self.toolButton_SelectInterface.setSizePolicy(sizePolicy) 157 self.toolButton_SelectInterface.setObjectName("toolButton_SelectInterface") 158 self.verticalLayout.addWidget(self.toolButton_SelectInterface) 159 self.toolButton_SelectInterface.setText(QtGui.QApplication.translate("Form", "Select an interface", None, QtGui.QApplication.UnicodeUTF8)) 160 self.toolButton_SelectInterface.clicked.connect(self._on_select_interface_clicked) 161 162 self.interface_field = QtGui.QComboBox(self) 163 self.interface_field.setInsertPolicy(QtGui.QComboBox.InsertAlphabetically) 164 self.interface_field.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)) 165 self.interface_field.setEditable(True) 166 self.interface_field.setVisible(False) 167 self.interface_field.setObjectName("interface_field") 168 self.verticalLayout.addWidget(self.interface_field) 169 self.interface_field.currentIndexChanged[str].connect(self._on_interface_selected) 170 171 self.toolButton_EditInterface = QtGui.QToolButton(self) 172 sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) 173 sizePolicy.setHorizontalStretch(0) 174 sizePolicy.setVerticalStretch(1) 175 sizePolicy.setHeightForWidth(self.toolButton_EditInterface.sizePolicy().hasHeightForWidth()) 176 self.toolButton_EditInterface.setSizePolicy(sizePolicy) 177 self.toolButton_EditInterface.setObjectName("toolButton_EditInterface") 178 self.verticalLayout.addWidget(self.toolButton_EditInterface) 179 self.toolButton_EditInterface.setText(QtGui.QApplication.translate("Form", "Edit selected interface", None, QtGui.QApplication.UnicodeUTF8)) 180 self.toolButton_EditInterface.clicked.connect(self._on_edit_interface_clicked) 181 self.toolButton_EditInterface.setVisible(False) 182 183 self.toolButton_CreateInterface = QtGui.QToolButton(self) 184 sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) 185 sizePolicy.setHorizontalStretch(0) 186 sizePolicy.setVerticalStretch(1) 187 sizePolicy.setHeightForWidth(self.toolButton_CreateInterface.sizePolicy().hasHeightForWidth()) 188 self.toolButton_CreateInterface.setSizePolicy(sizePolicy) 189 self.toolButton_CreateInterface.setObjectName("toolButton_CreateInterface") 190 self.verticalLayout.addWidget(self.toolButton_CreateInterface) 191 self.toolButton_CreateInterface.setText(QtGui.QApplication.translate("Form", "Create an interface", None, QtGui.QApplication.UnicodeUTF8)) 192 self.toolButton_CreateInterface.clicked.connect(self._on_create_interface_clicked) 193 self.toolButton_CreateInterface.setVisible(False) 194 195 self.textedit = Editor('', self) 196 self.hl = SyncHighlighter(self.textedit.document()) 197 sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) 198 self.textedit.setSizePolicy(sizePolicy) 199 self.textedit.setObjectName("syncedit") 200 self.verticalLayout.addWidget(self.textedit) 201 self.textedit.setVisible(False) 202 203 204 self._fill_interface_thread = None 205 self._interfaces_files = None 206 self._sync_args = [] 207 self._interface_filename = None 208 209 self.buttonBox = QtGui.QDialogButtonBox(self) 210 self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel) 211 self.buttonBox.setOrientation(QtCore.Qt.Horizontal) 212 self.buttonBox.setObjectName("buttonBox") 213 self.verticalLayout.addWidget(self.buttonBox) 214 QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), self.accept) 215 QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), self.reject) 216 217 self._new_iface = True
218 219 @property
220 - def sync_args(self):
221 return self._sync_args
222 223 @property
224 - def interface_filename(self):
225 return self._interface_filename
226 227
228 - def _on_sync_all_clicked(self):
229 self.setResult(QtGui.QDialog.Accepted) 230 self._sync_args = [] 231 self._sync_args.append(''.join(['_interface_url:=', "'.'"])) 232 self._sync_args.append(''.join(['_sync_topics_on_demand:=', 'False'])) 233 self._sync_args.append(''.join(['_ignore_hosts:=', '[]'])) 234 self._sync_args.append(''.join(['_sync_hosts:=', '[]'])) 235 self._sync_args.append(''.join(['_ignore_nodes:=', '[]'])) 236 self._sync_args.append(''.join(['_sync_nodes:=', '[]'])) 237 self._sync_args.append(''.join(['_ignore_topics:=', '[]'])) 238 self._sync_args.append(''.join(['_ignore_publishers:=', '[]'])) 239 self._sync_args.append(''.join(['_ignore_subscribers:=', '[]'])) 240 self._sync_args.append(''.join(['_sync_topics:=', '[]'])) 241 self._sync_args.append(''.join(['_ignore_services:=', '[]'])) 242 self._sync_args.append(''.join(['_sync_services:=', '[]'])) 243 self._sync_args.append(''.join(['_sync_remote_nodes:=', 'False'])) 244 self._interface_filename = None 245 self.accept()
246 247 # def _on_sync_all_anymsg_clicked(self): 248 # self._sync_args = [] 249 # self._sync_args.append(''.join(['_interface_url:=', "'.'"])) 250 # self._sync_args.append(''.join(['_sync_topics_on_demand:=', 'True'])) 251 # self._sync_args.append(''.join(['_ignore_hosts:=', '[]'])) 252 # self._sync_args.append(''.join(['_sync_hosts:=', '[]'])) 253 # self._sync_args.append(''.join(['_ignore_nodes:=', '[]'])) 254 # self._sync_args.append(''.join(['_sync_nodes:=', '[]'])) 255 # self._sync_args.append(''.join(['_ignore_topics:=', '[]'])) 256 # self._sync_args.append(''.join(['_sync_topics:=', '[/*]'])) 257 # self._sync_args.append(''.join(['_ignore_services:=', '[]'])) 258 # self._sync_args.append(''.join(['_sync_services:=', '[]'])) 259 # self._sync_args.append(''.join(['_sync_remote_nodes:=', 'False'])) 260 # self._interface_filename = None 261 # self.accept() 262
264 self._sync_args = [] 265 self._sync_args.append(''.join(['_interface_url:=', "'.'"])) 266 self._sync_args.append(''.join(['_sync_topics_on_demand:=', 'True'])) 267 self._sync_args.append(''.join(['_ignore_hosts:=', '[]'])) 268 self._sync_args.append(''.join(['_sync_hosts:=', '[]'])) 269 self._sync_args.append(''.join(['_ignore_nodes:=', '[]'])) 270 self._sync_args.append(''.join(['_sync_nodes:=', '[]'])) 271 self._sync_args.append(''.join(['_ignore_topics:=', '[]'])) 272 self._sync_args.append(''.join(['_ignore_publishers:=', '[]'])) 273 self._sync_args.append(''.join(['_ignore_subscribers:=', '[]'])) 274 self._sync_args.append(''.join(['_sync_topics:=', '[/only_on_demand]'])) 275 self._sync_args.append(''.join(['_ignore_services:=', '[/*]'])) 276 self._sync_args.append(''.join(['_sync_services:=', '[]'])) 277 self._sync_args.append(''.join(['_sync_remote_nodes:=', 'False'])) 278 self._interface_filename = None 279 self.accept()
280
282 self.toolButton_SyncAll.setVisible(False) 283 # self.toolButton_SyncAllAnyMsg.setVisible(False) 284 self.toolButton_SyncTopicOnDemand.setVisible(False) 285 self.toolButton_SelectInterface.setVisible(False) 286 self.interface_field.setVisible(True) 287 self.toolButton_CreateInterface.setVisible(True) 288 self.toolButton_EditInterface.setVisible(True) 289 self.toolButton_EditInterface.setEnabled(False) 290 self.textedit.setVisible(False) 291 # # fill the interfaces 292 if self._interfaces_files is None: 293 self.interface_field.addItems(['interface searching...']) 294 self.interface_field.setCurrentIndex(0) 295 self._fill_interface_thread = threading.Thread(target=self._fill_interfaces) 296 self._fill_interface_thread.start() 297 else: 298 self.toolButton_EditInterface.setEnabled(self._interfaces_files.has_key(self.interface_field.currentText())) 299 self.buttonBox.clear() 300 self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok|QtGui.QDialogButtonBox.Cancel) 301 self.interface_field.setFocus(QtCore.Qt.TabFocusReason) 302 self.resize(350,80)
303
304 - def _fill_interfaces(self):
305 if self._interfaces_files is None: 306 self.root_paths = [os.path.normpath(p) for p in os.getenv("ROS_PACKAGE_PATH").split(':')] 307 self._interfaces_files = {} 308 for p in self.root_paths: 309 ret = self._getInterfaces(p) 310 self._interfaces_files = dict(ret.items() + self._interfaces_files.items()) 311 self.interface_field.clear() 312 self.interface_field.clearEditText() 313 self.interface_field.addItems(self._interfaces_files.keys())
314
315 - def _on_interface_selected(self, interface):
316 if self._interfaces_files and self._interfaces_files.has_key(interface): 317 self._sync_args = [] 318 self._sync_args.append(''.join(['_interface_url:=', interface])) 319 self.toolButton_EditInterface.setEnabled(True) 320 else: 321 self.toolButton_EditInterface.setEnabled(False)
322
323 - def accept(self):
324 if self.textedit.isVisible(): 325 try: 326 tmp_file = os.path.join(nm.screen().LOG_PATH, 'tmp_sync_interface.sync') 327 with open(tmp_file, 'w+') as f: 328 f.write(self.textedit.toPlainText()) 329 from master_discovery_fkie.common import read_interface 330 read_interface(tmp_file) 331 if not self._new_iface and self._interfaces_files.has_key(self.interface_field.currentText()): 332 fileName = self._interfaces_files[self.interface_field.currentText()] 333 else: 334 fileName, _ = QtGui.QFileDialog.getSaveFileName(self, 'Save sync interface', '/home', "Sync Files (*.sync)") 335 if fileName: 336 with open(fileName, 'w+') as f: 337 self._interface_filename = fileName 338 f.write(self.textedit.toPlainText()) 339 if self._new_iface: 340 self.interface_field.clear() 341 self._interfaces_files = None 342 self._on_select_interface_clicked() 343 # QtGui.QDialog.accept(self) 344 # self.resetView() 345 except Exception as e: 346 WarningMessageBox(QtGui.QMessageBox.Warning, "Create sync interface", 347 "Error while create interface", 348 str(e)).exec_() 349 elif self.interface_field.isVisible(): 350 interface = self.interface_field.currentText() 351 if self._interfaces_files and self._interfaces_files.has_key(interface): 352 self._interface_filename = self._interfaces_files[interface] 353 self._sync_args = [] 354 self._sync_args.append(''.join(['_interface_url:=', interface])) 355 QtGui.QDialog.accept(self) 356 self.resetView() 357 else: 358 QtGui.QDialog.accept(self) 359 self.resetView()
360 361
362 - def reject(self):
363 if self.textedit.isVisible(): 364 self._on_select_interface_clicked() 365 else: 366 QtGui.QDialog.reject(self) 367 self.resetView()
368
370 self._new_iface = True 371 self.interface_field.setVisible(False) 372 self.toolButton_CreateInterface.setVisible(False) 373 self.toolButton_EditInterface.setVisible(False) 374 self.textedit.setVisible(True) 375 self.textedit.setText("# The ignore_* lists will be processed first.\n" 376 "# For ignore/sync nodes, topics or services\n" 377 "# use follow declaration:\n" 378 "#{param name}: \n" 379 "# - {ros name}\n" 380 "# or for selected hosts:\n" 381 "# - {host name}:\n" 382 "# - {ros name}\n\n" 383 "# you can use follow wildcard: '*', but not as a first character\n" 384 "ignore_hosts:\n" 385 "sync_hosts:\n\n" 386 "ignore_nodes:\n" 387 "sync_nodes:\n\n" 388 "ignore_topics:\n" 389 "ignore_publishers:\n" 390 "ignore_subscribers:\n" 391 "sync_topics:\n\n" 392 "ignore_services:\n" 393 " - /*get_loggers\n" 394 " - /*set_logger_level\n" 395 "sync_services:\n\n" 396 "# If sync_topics_on_demand is True the local subscribed and published topics\n" 397 "# are synchronized with remote even if they are not in the sync_* list.\n" 398 "sync_topics_on_demand: False\n\n" 399 "# The nodes which are running not at the same host as the ROS master are not\n" 400 "# synchronized by default. Use sync_remote_nodes to sync these nodes also.\n" 401 "sync_remote_nodes: False\n\n" 402 ) 403 self.resize(350,300)
404
406 self._new_iface = False 407 self.interface_field.setVisible(False) 408 self.toolButton_CreateInterface.setVisible(False) 409 self.toolButton_EditInterface.setVisible(False) 410 self.textedit.setVisible(True) 411 if self._interfaces_files.has_key(self.interface_field.currentText()): 412 try: 413 with open(self._interfaces_files[self.interface_field.currentText()], 'rw') as f: 414 iface = f.read() 415 self.textedit.setText(iface) 416 except Exception as e: 417 WarningMessageBox(QtGui.QMessageBox.Warning, "Edit sync interface", 418 "Error while open interface", 419 str(e)).exec_() 420 self.resize(350,300)
421
422 - def resetView(self):
423 self.toolButton_SyncAll.setVisible(True) 424 # self.toolButton_SyncAllAnyMsg.setVisible(True) 425 self.toolButton_SyncTopicOnDemand.setVisible(True) 426 self.toolButton_SelectInterface.setVisible(True) 427 self.interface_field.setVisible(False) 428 self.toolButton_CreateInterface.setVisible(False) 429 self.toolButton_EditInterface.setVisible(False) 430 self.textedit.setVisible(False) 431 self.buttonBox.clear() 432 self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel) 433 self.resize(350,160)
434
435 - def _getInterfaces(self, path, package=None):
436 result = {} 437 if os.path.isdir(path): 438 fileList = os.listdir(path) 439 # set package, if it is currently None and one found 440 if not package: 441 # detect package 442 if is_package(fileList): 443 package = os.path.basename(path) 444 for f in fileList: 445 ret = self._getInterfaces(os.path.join(path, f), package) 446 result = dict(ret.items() + result.items()) 447 elif package and os.path.isfile(path) and path.endswith('.sync'): 448 # create a selection for binaries 449 return {''.join(['pkg://', package, '///', os.path.basename(path)]) : path} 450 return result
451