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

Source Code for Module node_manager_fkie.main_window

   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  import os 
  34  import time 
  35  import uuid 
  36  import xmlrpclib 
  37  import getpass 
  38  
 
  39  from datetime import datetime 
  40  
 
  41  from python_qt_binding import QtGui 
  42  from python_qt_binding import QtCore 
  43  #from python_qt_binding import QtUiTools
 
  44  from python_qt_binding import loadUi 
  45  
 
  46  import roslib; roslib.load_manifest('node_manager_fkie') 
  47  import rospy 
  48  
 
  49  import gui_resources 
  50  from .discovery_listener import MasterListService, MasterStateTopic, MasterStatisticTopic, OwnMasterMonitoring 
  51  from .update_handler import UpdateHandler 
  52  from .master_view_proxy import MasterViewProxy 
  53  from .launch_config import LaunchConfig#, LaunchConfigException 
  54  from .capability_table import CapabilityTable 
  55  from .xml_editor import XmlEditor 
  56  from .detailed_msg_box import WarningMessageBox 
  57  from .network_discovery_dialog import NetworkDiscoveryDialog 
  58  from .parameter_dialog import ParameterDialog 
  59  from .progress_queue import ProgressQueue#, ProgressThread 
  60  from .screen_handler import ScreenHandler 
  61  from .sync_dialog import SyncDialog 
  62  from .common import masteruri_from_ros, package_name 
  63  from .select_dialog import SelectDialog 
  64  from .master_list_model import MasterModel, MasterSyncItem 
  65  from .log_widget import LogWidget 
  66  from .launch_files_widget import LaunchFilesWidget 
  67  from .settings_widget import SettingsWidget 
  68  from .menu_rqt import MenuRqt 
  69  
 
  70  import node_manager_fkie as nm 
  71  
 
  72  from multimaster_msgs_fkie.msg import LinkState, LinkStatesStamped, MasterState#, ROSMaster, SyncMasterInfo, SyncTopicInfo 
  73  from master_discovery_fkie.common import resolve_url 
  74  #from master_discovery_fkie.srv import DiscoverMasters, GetSyncInfo
 
  75  
 
  76  
 
77 -class MainWindow(QtGui.QMainWindow):
78 ''' 79 The class to create the main window of the application. 80 ''' 81 DELAYED_NEXT_REQ_ON_ERR = 5.0 82
83 - def __init__(self, files=[], restricted_to_one_master=False, parent=None):
84 ''' 85 Creates the window, connects the signals and init the class. 86 ''' 87 QtGui.QMainWindow.__init__(self) 88 restricted_to_one_master = False 89 self._finished = False 90 self._history_selected_robot = '' 91 self.__icons = {'empty' : (QtGui.QIcon(), ''), 92 'default_pc' : (QtGui.QIcon(':/icons/crystal_clear_miscellaneous.png'), ':/icons/crystal_clear_miscellaneous.png'), 93 'log_warning' : (QtGui.QIcon(':/icons/crystal_clear_warning.png'), ':/icons/crystal_clear_warning.png') 94 } # (masnter name : (QIcon, path)) 95 self.__current_icon = None 96 self.__current_master_label_name = None 97 self._changed_files = dict() 98 self._changed_files_param = dict() 99 #self.setAttribute(QtCore.Qt.WA_AlwaysShowToolTips, True) 100 # setup main window frame 101 self.setObjectName('MainWindow') 102 # self = mainWindow = QtGui.QMainWindow() 103 # self = mainWindow = loader.load(":/forms/MainWindow.ui") 104 ui_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'MainWindow.ui') 105 #/home/tiderko/ros/src/multimaster_fkie/node_manager_fkie/src/node_manager_fkie/ 106 loadUi(ui_file, self) 107 self.setObjectName('MainUI') 108 self.user_frame.setVisible(False) 109 self._add_user_to_combo(getpass.getuser()) 110 self.userComboBox.editTextChanged.connect(self.on_user_changed) 111 self.masterInfoFrame.setEnabled(False) 112 self.infoButton.clicked.connect(self.on_info_clicked) 113 self.refreshHostButton.clicked.connect(self.on_refresh_master_clicked) 114 self.runButton.clicked.connect(self.on_run_node_clicked) 115 self.syncButton.released.connect(self.on_sync_dialog_released) 116 117 menu_rqt = MenuRqt(self.rqtButton) 118 menu_rqt.start_rqt_plugin_signal.connect(self.on_rqt_plugin_start) 119 120 # setup settings widget 121 self.settings_dock = SettingsWidget(self) 122 self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.settings_dock) 123 # setup logger widget 124 self.log_dock = LogWidget(self) 125 self.log_dock.added_signal.connect(self._on_log_added) 126 self.log_dock.cleared_signal.connect(self._on_log_cleared) 127 self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.log_dock) 128 self.logButton.clicked.connect(self._on_log_button_clicked) 129 # setup the launch files view 130 self.launch_dock = LaunchFilesWidget(self) 131 self.launch_dock.load_signal.connect(self.on_load_launch_file) 132 self.launch_dock.load_as_default_signal.connect(self.on_load_launch_as_default) 133 self.launch_dock.edit_signal.connect(self.on_launch_edit) 134 self.launch_dock.transfer_signal.connect(self.on_launch_transfer) 135 self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.launch_dock) 136 137 self.mIcon = QtGui.QIcon(":/icons/crystal_clear_prop_run.png") 138 self.setWindowIcon(self.mIcon) 139 self.setWindowTitle("Node Manager") 140 # self.setCentralWidget(mainWindow) 141 142 # init the stack layout which contains the information about different ros master 143 self.stackedLayout = QtGui.QStackedLayout() 144 self.stackedLayout.setObjectName('stackedLayout') 145 emptyWidget = QtGui.QWidget() 146 emptyWidget.setObjectName('emptyWidget') 147 self.stackedLayout.addWidget(emptyWidget) 148 self.tabWidget.currentChanged.connect(self.on_currentChanged_tab) 149 self.tabLayout = QtGui.QVBoxLayout(self.tabPlace) 150 self.tabLayout.setObjectName("tabLayout") 151 self.tabLayout.setContentsMargins(0, 0, 0, 0) 152 self.tabLayout.addLayout(self.stackedLayout) 153 154 # initialize the progress queue 155 self._progress_queue = ProgressQueue(self.progressFrame, self.progressBar, self.progressCancelButton) 156 self._progress_queue_sync = ProgressQueue(self.progressFrame_sync, self.progressBar_sync, self.progressCancelButton_sync) 157 158 # initialize the view for the discovered ROS master 159 self.master_model = MasterModel(self.getMasteruri()) 160 self.masterTableView.setModel(self.master_model) 161 # self.masterTableView.setAlternatingRowColors(True) 162 self.masterTableView.clicked.connect(self.on_master_table_clicked) 163 self.masterTableView.pressed.connect(self.on_master_table_pressed) 164 self.masterTableView.activated.connect(self.on_master_table_activated) 165 sm = self.masterTableView.selectionModel() 166 sm.currentRowChanged.connect(self.on_masterTableView_selection_changed) 167 for i, (_, width) in enumerate(MasterModel.header):#_:=name 168 self.masterTableView.setColumnWidth(i, width) 169 self.refreshAllButton.clicked.connect(self.on_all_master_refresh_clicked) 170 self.discoveryButton.clicked.connect(self.on_discover_network_clicked) 171 self.startRobotButton.clicked.connect(self.on_start_robot_clicked) 172 173 # stores the widget to a 174 self.masters = dict() # masteruri : MasterViewProxy 175 self.currentMaster = None # MasterViewProxy 176 self._close_on_exit = True 177 178 nm.file_watcher().file_changed.connect(self.on_configfile_changed) 179 nm.file_watcher_param().file_changed.connect(self.on_configparamfile_changed) 180 self.__in_question = set() 181 182 ############################################################################ 183 self.capabilitiesTable = CapabilityTable(self.capabilities_tab) 184 self.capabilitiesTable.setObjectName("capabilitiesTable") 185 self.capabilitiesTable.start_nodes_signal.connect(self.on_start_nodes) 186 self.capabilitiesTable.stop_nodes_signal.connect(self.on_stop_nodes) 187 self.capabilitiesTable.description_requested_signal.connect(self.on_description_update_cap) 188 self.capabilities_tab.layout().addWidget(self.capabilitiesTable) 189 190 self.descriptionTextEdit.setOpenLinks(False) 191 self.descriptionTextEdit.anchorClicked.connect(self.on_description_anchorClicked) 192 self._shortcut_copy = QtGui.QShortcut(QtGui.QKeySequence(self.tr("Ctrl+Shift+C", "copy selected description")), self.descriptionTextEdit) 193 self._shortcut_copy.activated.connect(self.descriptionTextEdit.copy) 194 195 self.tabifyDockWidget(self.launch_dock, self.descriptionDock) 196 self.tabifyDockWidget(self.launch_dock, self.settings_dock) 197 self.tabifyDockWidget(self.launch_dock, self.helpDock) 198 self.launch_dock.raise_() 199 self.helpDock.setWindowIcon(QtGui.QIcon(':icons/crystal_clear_helpcenter.png')) 200 201 flags = self.windowFlags() 202 self.setWindowFlags(flags | QtCore.Qt.WindowContextHelpButtonHint) 203 204 self.default_load_launch = os.path.abspath(resolve_url(files[0])) if files else '' 205 if self.default_load_launch: 206 if os.path.isdir(self.default_load_launch): 207 self.launch_dock.launchlist_model.setPath(self.default_load_launch) 208 elif os.path.isfile(self.default_load_launch): 209 self.launch_dock.launchlist_model.setPath(os.path.dirname(self.default_load_launch)) 210 211 self._discover_dialog = None 212 self.restricted_to_one_master = restricted_to_one_master 213 if restricted_to_one_master: 214 self.syncButton.setEnabled(False) 215 self.refreshAllButton.setEnabled(False) 216 self.discoveryButton.setEnabled(False) 217 self.startRobotButton.setEnabled(False) 218 219 self._sync_dialog = SyncDialog() 220 221 self.editor_dialogs = dict() # [file] = XmlEditor 222 '''@ivar: stores the open XmlEditor ''' 223 224 self.simTimeLabel.setVisible(False) 225 self.launchServerLabel.setVisible(False) 226 227 # since the is_local method is threaded for host names, call it to cache the localhost 228 nm.is_local("localhost") 229 230 # set the help text 231 try: 232 from docutils import examples 233 with file(nm.settings().HELP_FILE) as f: 234 self.textBrowser.setText(examples.html_body(unicode(f.read()))) 235 except: 236 import traceback 237 msg = "Error while generate help: %s"%traceback.format_exc(2) 238 rospy.logwarn(msg) 239 self.textBrowser.setText(msg) 240 241 try: 242 ScreenHandler.testScreen() 243 except Exception as e: 244 rospy.logerr("No SCREEN available! You can't launch nodes.") 245 # WarningMessageBox(QtGui.QMessageBox.Warning, "No SCREEN", 246 # "No SCREEN available! You can't launch nodes.", 247 # '%s'%e).exec_() 248 249 self.imageLabel.mouseDoubleClickEvent = self.image_mouseDoubleClickEvent 250 251 try: 252 self.readSettings() 253 self.launch_dock.raise_() 254 except Exception as e: 255 rospy.logwarn("Error while read settings: %s"%e) 256 # setup the hide button, which hides the docks on left side 257 docks = self._dock_widget_in(QtCore.Qt.LeftDockWidgetArea, only_visible=True) 258 if not docks: 259 self.hideDocksButton.toggle() 260 self.on_hide_docks_toggled(True) 261 self.hideDocksButton.clicked.connect(self.on_hide_docks_toggled) 262 263 # ============================= 264 # Initialize the update handler 265 # ============================= 266 267 # initialize the class to get the state of discovering of other ROS master 268 self._update_handler = UpdateHandler() 269 self._update_handler.master_info_signal.connect(self.on_master_info_retrieved) 270 self._update_handler.master_errors_signal.connect(self.on_master_errors_retrieved) 271 self._update_handler.error_signal.connect(self.on_master_info_error) 272 273 # this monitor class is used, if no master_discovery node is running to get the state of the local ROS master 274 self.own_master_monitor = OwnMasterMonitoring() 275 self.own_master_monitor.init(22622) 276 self.own_master_monitor.state_signal.connect(self.on_master_state_changed) 277 self.own_master_monitor.err_signal.connect(self.on_master_monitor_err) 278 279 # get the name of the service and topic of the discovery node. The name are determine by the message type of those topics 280 self.masterlist_service = masterlist_service = MasterListService() 281 masterlist_service.masterlist_signal.connect(self.on_master_list_retrieved) 282 masterlist_service.masterlist_err_signal.connect(self.on_master_list_err_retrieved) 283 self.state_topic = MasterStateTopic() 284 self.state_topic.state_signal.connect(self.on_master_state_changed) 285 self.stats_topic = MasterStatisticTopic() 286 self.stats_topic.stats_signal.connect(self.on_conn_stats_updated) 287 288 # timer to update the showed update time of the ros state 289 self.master_timecheck_timer = QtCore.QTimer() 290 self.master_timecheck_timer.timeout.connect(self.on_master_timecheck) 291 self.master_timecheck_timer.start(1000) 292 self._refresh_time = time.time() 293 self._last_time_view_update = time.time() 294 295 self._con_tries = dict() 296 self._subscribe()
297
298 - def _dock_widget_in(self, area=QtCore.Qt.LeftDockWidgetArea, only_visible=False):
299 result = [] 300 docks = [self.launch_dock, self.descriptionDock, self.helpDock, self.networkDock] 301 for dock in docks: 302 if self.dockWidgetArea(dock) == area: 303 if not only_visible or (only_visible and dock.isVisibleTo(self)): 304 result.append(dock) 305 return result
306
307 - def _on_log_button_clicked(self):
308 self.log_dock.setVisible(not self.log_dock.isVisible())
309
310 - def _on_log_added(self, info, warn, err, fatal):
311 self.logButton.setEnabled(True)
312
313 - def _on_log_cleared(self):
314 self.logButton.setIcon(self.__icons['log_warning'][0]) 315 self.logButton.setText('') 316 self.logButton.setEnabled(False)
317
318 - def on_hide_docks_toggled(self, checked):
319 if self.dockWidgetArea(self.launch_dock) == QtCore.Qt.LeftDockWidgetArea: 320 self.launch_dock.setVisible(not checked) 321 if self.dockWidgetArea(self.descriptionDock) == QtCore.Qt.LeftDockWidgetArea: 322 self.descriptionDock.setVisible(not checked) 323 if self.dockWidgetArea(self.helpDock) == QtCore.Qt.LeftDockWidgetArea: 324 self.helpDock.setVisible(not checked) 325 if self.dockWidgetArea(self.networkDock) == QtCore.Qt.LeftDockWidgetArea: 326 self.networkDock.setVisible(not checked) 327 if self.dockWidgetArea(self.settings_dock) == QtCore.Qt.LeftDockWidgetArea: 328 self.settings_dock.setVisible(not checked) 329 self.hideDocksButton.setArrowType(QtCore.Qt.RightArrow if checked else QtCore.Qt.LeftArrow)
330
331 - def on_currentChanged_tab(self, index):
332 pass
333 # if index == self.tabWidget.widget(0): 334 # self.networkDock.show() 335 # self.launch_dock.show() 336 # else: 337 # self.networkDock.hide() 338 # self.launch_dock.hide() 339
340 - def readSettings(self):
341 if nm.settings().store_geometry: 342 settings = nm.settings().qsettings(nm.settings().CFG_GUI_FILE) 343 self._history_selected_robot = settings.value("selected_robot", '') 344 settings.beginGroup("mainwindow") 345 maximized = settings.value("maximized", 'false') == 'true' 346 if maximized: 347 self.showMaximized() 348 else: 349 self.resize(settings.value("size", QtCore.QSize(1024, 720))) 350 self.move(settings.value("pos", QtCore.QPoint(0, 0))) 351 try: 352 self.restoreState(settings.value("window_state")) 353 except: 354 pass 355 settings.endGroup()
356
357 - def storeSetting(self):
358 if nm.settings().store_geometry: 359 settings = nm.settings().qsettings(nm.settings().CFG_GUI_FILE) 360 settings.beginGroup("mainwindow") 361 settings.setValue("size", self.size()) 362 settings.setValue("pos", self.pos()) 363 settings.setValue("maximized", self.isMaximized()) 364 settings.setValue("window_state", self.saveState()) 365 settings.endGroup()
366
367 - def closeEvent(self, event):
368 # ask to close nodes on exit 369 if self._close_on_exit: 370 masters2stop, self._close_on_exit = SelectDialog.getValue('Stop nodes?', "Select masters where to stop:", self.masters.keys(), False, False, '', self, select_if_single=False) 371 if self._close_on_exit: 372 self._on_finish = True 373 for uri in masters2stop: 374 try: 375 m = self.masters[uri] 376 if not m is None: 377 m.stop_nodes_by_name(m.getRunningNodesIfLocal()) 378 except Exception as e: 379 rospy.logwarn("Error while stop nodes on %s: %s"%(uri, e)) 380 QtCore.QTimer.singleShot(200, self._test_for_finish) 381 else: 382 self._close_on_exit = True 383 event.ignore() 384 else: 385 try: 386 self.storeSetting() 387 except Exception as e: 388 rospy.logwarn("Error while store settings: %s"%e) 389 self.finish() 390 QtGui.QMainWindow.closeEvent(self, event)
391
392 - def _test_for_finish(self):
393 # this method test on exit for running process queues with stopping jobs 394 for uri, m in self.masters.items(): 395 if m.in_process(): 396 QtCore.QTimer.singleShot(200, self._test_for_finish) 397 return 398 self._close_on_exit = False 399 self.close()
400
401 - def finish(self):
402 if not self._finished: 403 self._finished = True 404 print "Mainwindow finish..." 405 self._progress_queue.stop() 406 self._progress_queue_sync.stop() 407 self._update_handler.stop() 408 self.state_topic.stop() 409 self.stats_topic.stop() 410 for _, master in self.masters.iteritems(): 411 master.stop() 412 self.own_master_monitor.stop() 413 self.master_timecheck_timer.stop() 414 self.launch_dock.stop() 415 self.log_dock.stop() 416 print "Mainwindow finished!"
417
418 - def getMasteruri(self):
419 ''' 420 Requests the ROS master URI from the ROS master through the RPC interface and 421 returns it. The 'materuri' attribute will be set to the requested value. 422 @return: ROS master URI 423 @rtype: C{str} or C{None} 424 ''' 425 if not hasattr(self, 'materuri') or self.materuri is None: 426 masteruri = masteruri_from_ros() 427 master = xmlrpclib.ServerProxy(masteruri) 428 _, _, self.materuri = master.getUri(rospy.get_name())#_:=code, message 429 nm.is_local(nm.nameres().getHostname(self.materuri)) 430 return self.materuri
431
432 - def removeMaster(self, masteruri):
433 ''' 434 Removed master with given master URI from the list. 435 @param masteruri: the URI of the ROS master 436 @type masteruri: C{str} 437 ''' 438 if masteruri in self.masters: 439 if not self.currentMaster is None and self.currentMaster.masteruri == masteruri: 440 self.setCurrentMaster(None) 441 self.masters[masteruri].stop() 442 self.masters[masteruri].updateHostRequest.disconnect() 443 self.masters[masteruri].host_description_updated.disconnect() 444 self.masters[masteruri].capabilities_update_signal.disconnect() 445 self.masters[masteruri].remove_config_signal.disconnect() 446 self.masters[masteruri].description_signal.disconnect() 447 self.masters[masteruri].request_xml_editor.disconnect() 448 self.masters[masteruri].stop_nodes_signal.disconnect() 449 self.masters[masteruri].robot_icon_updated.disconnect() 450 self.stackedLayout.removeWidget(self.masters[masteruri]) 451 self.tabPlace.layout().removeWidget(self.masters[masteruri]) 452 for cfg in self.masters[masteruri].default_cfgs: 453 self.capabilitiesTable.removeConfig(cfg) 454 self.masters[masteruri].setParent(None) 455 del self.masters[masteruri]
456
457 - def getMaster(self, masteruri, create_new=True):
458 ''' 459 @return: the Widget which represents the master of given ROS master URI. If no 460 Widget for given URI is available a new one will be created. 461 @rtype: L{MasterViewProxy} 462 ''' 463 if not masteruri in self.masters: 464 if not create_new: 465 return None 466 self.masters[masteruri] = MasterViewProxy(masteruri, self) 467 self.masters[masteruri].updateHostRequest.connect(self.on_host_update_request) 468 self.masters[masteruri].host_description_updated.connect(self.on_host_description_updated) 469 self.masters[masteruri].capabilities_update_signal.connect(self.on_capabilities_update) 470 self.masters[masteruri].remove_config_signal.connect(self.on_remove_config) 471 self.masters[masteruri].description_signal.connect(self.on_description_update) 472 self.masters[masteruri].request_xml_editor.connect(self.on_launch_edit) 473 self.masters[masteruri].stop_nodes_signal.connect(self.on_stop_nodes) 474 self.masters[masteruri].robot_icon_updated.connect(self._on_robot_icon_changed) 475 self.stackedLayout.addWidget(self.masters[masteruri]) 476 if masteruri == self.getMasteruri(): 477 if self.default_load_launch: 478 try: 479 if os.path.isfile(self.default_load_launch): 480 args = list() 481 args.append('_package:=%s'%(package_name(os.path.dirname(self.default_load_launch))[0])) 482 args.append('_launch_file:="%s"'%os.path.basename(self.default_load_launch)) 483 host = '%s'%nm.nameres().address(masteruri) 484 node_name = roslib.names.SEP.join(['%s'%(nm.nameres().mastername(masteruri)), 485 os.path.basename(self.default_load_launch).replace('.launch',''), 486 'default_cfg']) 487 self.launch_dock.progress_queue.add2queue('%s'%uuid.uuid4(), 488 'start default config @%s'%host, 489 nm.starter().runNodeWithoutConfig, 490 ('%s'%(nm.nameres().mastername(masteruri)), 'default_cfg_fkie', 491 'default_cfg', node_name, 492 args, masteruri, False, 493 self.masters[masteruri].current_user)) 494 self.launch_dock.progress_queue.start() 495 except Exception as e: 496 WarningMessageBox(QtGui.QMessageBox.Warning, "Load default configuration", 497 'Load default configuration %s failed!'%self.default_load_launch, 498 '%s'%e).exec_() 499 return self.masters[masteruri]
500
501 - def on_host_update_request(self, host):
502 for key, value in self.masters.items(): 503 if nm.nameres().getHostname(key) == host and not value.master_state is None: 504 self._update_handler.requestMasterInfo(value.master_state.uri, value.master_state.monitoruri)
505
506 - def on_host_description_updated(self, masteruri, host, descr):
507 # self.master_model.updateDescription(nm.nameres().mastername(masteruri, host), descr) 508 pass
509
510 - def on_capabilities_update(self, masteruri, address, config_node, descriptions):
511 for d in descriptions: 512 self.capabilitiesTable.updateCapabilities(masteruri, config_node, d) 513 if not masteruri is None: 514 master = self.getMaster(masteruri) 515 self.capabilitiesTable.updateState(masteruri, master.master_info)
516
517 - def on_remove_config(self, cfg):
518 self.capabilitiesTable.removeConfig(cfg)
519 520 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 521 #%%%%%%%%%%%%% Handling of local monitoring %%%%%%%% 522 #%%%%%%%%%%%%% (Backup, if no master_discovery node is running) %%%%%%%% 523 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 524
525 - def _subscribe(self):
526 ''' 527 Try to subscribe to the topics of the master_discovery node. If it fails, the 528 own local monitoring of the ROS master state will be enabled. 529 ''' 530 if not self.restricted_to_one_master: 531 try: 532 result_1 = self.state_topic.registerByROS(self.getMasteruri(), False) 533 result_2 = self.stats_topic.registerByROS(self.getMasteruri(), False) 534 self.masterlist_service.retrieveMasterList(self.getMasteruri(), False) 535 if not result_1 or not result_2: 536 self._setLocalMonitoring(True) 537 except: 538 pass 539 else: 540 self._setLocalMonitoring(True)
541
542 - def _setLocalMonitoring(self, on):
543 ''' 544 Enables the local monitoring of the ROS master state and disables the view of 545 the discoved ROS master. 546 @param on: the enable / disable the local monitoring 547 @type on: C{boolean} 548 ''' 549 self.masterTableView.setEnabled(not on) 550 self.refreshAllButton.setEnabled(not on) 551 self.own_master_monitor.pause(not on) 552 if on: 553 self.masterTableView.setToolTip("use 'Start' button to enable the master discovering") 554 else: 555 self.masterTableView.setToolTip('') 556 if on: 557 # remove discovered ROS master and set the local master to selected 558 for uri in self.masters.keys(): 559 master = self.masters[uri] 560 if nm.is_local(nm.nameres().getHostname(uri)) or uri == self.getMasteruri(): 561 if not self._history_selected_robot or master.mastername == self._history_selected_robot: 562 self.setCurrentMaster(master) 563 else: 564 if not master.master_state is None: 565 self.master_model.removeMaster(master.master_state.name) 566 self.removeMaster(uri)
567 # self.masterTableView.doItemsLayout() 568 569 570
571 - def on_master_list_err_retrieved(self, masteruri, error):
572 ''' 573 The callback method connected to the signal, which is emitted on an error 574 while call the service to determine the discovered ROS master. On the error 575 the local monitoring will be enabled. 576 ''' 577 self._setLocalMonitoring(True)
578
579 - def hasDiscoveryService(self, minfo):
580 ''' 581 Test whether the new retrieved MasterInfo contains the master_discovery node. 582 This is identified by a name of the contained 'list_masters' service. 583 @param minfo: the ROS master Info 584 @type minfo: L{master_discovery_fkie.MasterInfo} 585 ''' 586 # use no discovery services, if roscore is running on a remote host 587 if self.restricted_to_one_master: 588 return False 589 for service in minfo.services.keys(): 590 if service.endswith('list_masters'): 591 return True 592 return False
593 594 595 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 596 #%%%%%%%%%%%%% Handling of received ROS master state messages %%%%%%%% 597 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 598
599 - def on_master_list_retrieved(self, masteruri, servic_name, master_list):
600 ''' 601 Handle the retrieved list with ROS master. 602 1. update the ROS Network view 603 @param master_list: a list with ROS masters 604 @type master_list: C{[L{master_discovery_fkie.msg.MasterState}]} 605 ''' 606 self._setLocalMonitoring(False) 607 self._con_tries[masteruri] = 0 608 # remove ROS master which are not in the new list 609 new_uris = [m.uri for m in master_list if not m.uri is None] 610 for uri in self.masters.keys(): 611 if uri not in new_uris: 612 master = self.masters[uri] 613 if not (nm.is_local(nm.nameres().getHostname(uri)) or uri == self.getMasteruri()): 614 if not master.master_state is None: 615 self.master_model.removeMaster(master.master_state.name) 616 self.removeMaster(uri) 617 # add or update master 618 for m in master_list: 619 if not m.uri is None: 620 host = nm.nameres().getHostname(m.uri) 621 nm.nameres().addMasterEntry(m.uri, m.name, host, host) 622 m.name = nm.nameres().mastername(m.uri) 623 master = self.getMaster(m.uri) 624 master.master_state = m 625 master.force_next_update() 626 self._assigne_icon(m.name) 627 self.master_model.updateMaster(m) 628 self._update_handler.requestMasterInfo(m.uri, m.monitoruri)
629
630 - def on_master_state_changed(self, msg):
631 ''' 632 Handle the received master state message. 633 1. update the ROS Network view 634 2. enable local master monitoring, if all masters are removed (the local master too) 635 @param msg: the ROS message with new master state 636 @type msg: L{master_discovery_fkie.msg.MasterState} 637 ''' 638 #'print "*on_master_state_changed" 639 # do not update while closing 640 if hasattr(self, "_on_finish"): 641 rospy.logdebug("ignore changes on %s, because currently on closing...", msg.master.uri) 642 return; 643 host=nm.nameres().getHostname(msg.master.uri) 644 if msg.state == MasterState.STATE_CHANGED: 645 nm.nameres().addMasterEntry(msg.master.uri, msg.master.name, host, host) 646 msg.master.name = nm.nameres().mastername(msg.master.uri) 647 self.getMaster(msg.master.uri).master_state = msg.master 648 self._assigne_icon(msg.master.name) 649 self.master_model.updateMaster(msg.master) 650 # self.masterTableView.doItemsLayout() 651 if nm.settings().autoupdate: 652 self._update_handler.requestMasterInfo(msg.master.uri, msg.master.monitoruri) 653 else: 654 rospy.loginfo("Autoupdate disabled, the data will not be updated for %s"%msg.master.uri) 655 if msg.state == MasterState.STATE_NEW: 656 # if new master with uri of the local master is received update the master list 657 if msg.master.uri == self.getMasteruri(): 658 self.masterlist_service.retrieveMasterList(msg.master.uri, False) 659 nm.nameres().addMasterEntry(msg.master.uri, msg.master.name, host, host) 660 msg.master.name = nm.nameres().mastername(msg.master.uri) 661 self.getMaster(msg.master.uri).master_state = msg.master 662 self._assigne_icon(msg.master.name) 663 self.master_model.updateMaster(msg.master) 664 # self.masterTableView.doItemsLayout() 665 if nm.settings().autoupdate: 666 self._update_handler.requestMasterInfo(msg.master.uri, msg.master.monitoruri) 667 else: 668 rospy.loginfo("Autoupdate disabled, the data will not be updated for %s"%msg.master.uri) 669 if msg.state == MasterState.STATE_REMOVED: 670 if msg.master.uri == self.getMasteruri(): 671 # switch to locale monitoring, if the local master discovering was removed 672 self._setLocalMonitoring(True) 673 else: 674 nm.nameres().removeMasterEntry(msg.master.uri) 675 self.master_model.removeMaster(msg.master.name) 676 # self.masterTableView.doItemsLayout() 677 self.removeMaster(msg.master.uri)
678 # if len(self.masters) == 0: 679 # self._setLocalMonitoring(True) 680 #'print "**on_master_state_changed" 681
682 - def _assigne_icon(self, name, path=None):
683 ''' 684 Sets the new icon to the given robot. If the path is `None` set search for 685 .png file with robot name. 686 :param name: robot name 687 :type name: str 688 :param path: path of the icon (Default: None) 689 :type path: str 690 ''' 691 icon_path = path if path else nm.settings().robot_image_file(name) 692 if not self.__icons.has_key(name) or self.__icons[name][1] != path: 693 if QtCore.QFile.exists(icon_path): 694 self.__icons[name] = (QtGui.QIcon(icon_path), icon_path) 695 elif self.__icons.has_key(name): 696 del self.__icons[name]
697
698 - def on_master_monitor_err(self, msg):
699 self._con_tries[self.getMasteruri()] += 1
700
701 - def on_master_info_retrieved(self, minfo):
702 ''' 703 Integrate the received master info. 704 @param minfo: the ROS master Info 705 @type minfo: L{master_discovery_fkie.MasterInfo} 706 ''' 707 rospy.logdebug("MASTERINFO from %s (%s) received", minfo.mastername, minfo.masteruri) 708 self._con_tries[minfo.masteruri] = 0 709 # cputimes_m = os.times() 710 # cputime_init_m = cputimes_m[0] + cputimes_m[1] 711 if minfo.masteruri in self.masters: 712 for _, master in self.masters.items():#_:=uri 713 try: 714 # check for running discovery service 715 new_info = master.master_info is None or master.master_info.timestamp < minfo.timestamp 716 # cputimes = os.times() 717 # cputime_init = cputimes[0] + cputimes[1] 718 master.master_info = minfo 719 # cputimes = os.times() 720 # cputime = cputimes[0] + cputimes[1] - cputime_init 721 # print master.master_state.name, cputime 722 if not master.master_info is None: 723 if self._history_selected_robot == minfo.mastername and self._history_selected_robot == master.mastername and self.currentMaster != master: 724 if not self.currentMaster is None and not self.currentMaster.is_local: 725 self.setCurrentMaster(master) 726 elif nm.is_local(nm.nameres().getHostname(master.master_info.masteruri)) or self.restricted_to_one_master: 727 if new_info: 728 has_discovery_service = self.hasDiscoveryService(minfo) 729 if (not self.own_master_monitor.isPaused() or not self.masterTableView.isEnabled()) and has_discovery_service: 730 self._subscribe() 731 if self.currentMaster is None and (not self._history_selected_robot or self._history_selected_robot == minfo.mastername): 732 self.setCurrentMaster(master) 733 734 # update the list view, whether master is synchronized or not 735 if master.master_info.masteruri == minfo.masteruri: 736 self.master_model.setChecked(master.master_state.name, not minfo.getNodeEndsWith('master_sync') is None) 737 self.capabilitiesTable.updateState(minfo.masteruri, minfo) 738 except Exception, e: 739 rospy.logwarn("Error while process received master info from %s: %s", minfo.masteruri, str(e)) 740 # update the duplicate nodes 741 self.updateDuplicateNodes() 742 # update the buttons, whether master is synchronized or not 743 if not self.currentMaster is None and not self.currentMaster.master_info is None and not self.restricted_to_one_master: 744 self.syncButton.setEnabled(True) 745 self.syncButton.setChecked(not self.currentMaster.master_info.getNodeEndsWith('master_sync') is None) 746 else: 747 self.masterlist_service.retrieveMasterList(minfo.masteruri, False)
748 # cputimes_m = os.times() 749 # cputime_m = cputimes_m[0] + cputimes_m[1] - cputime_init_m 750 # print "ALL:", cputime_m 751
752 - def on_master_errors_retrieved(self, masteruri, error_list):
753 self.master_model.updateMasterErrors(nm.nameres().mastername(masteruri), error_list)
754
755 - def on_master_info_error(self, masteruri, error):
756 if not self._con_tries.has_key(masteruri): 757 self._con_tries[masteruri] = 0 758 self._con_tries[masteruri] += 1 759 if masteruri == self.getMasteruri(): 760 rospy.logwarn("Error while connect to local master_discovery %s: %s", masteruri, error) 761 # switch to local monitoring after 3 timeouts 762 if self._con_tries[masteruri] > 2: 763 self._setLocalMonitoring(True) 764 master = self.getMaster(masteruri, False) 765 if master and not master.master_state is None: 766 self._update_handler.requestMasterInfo(master.master_state.uri, master.master_state.monitoruri, self.DELAYED_NEXT_REQ_ON_ERR)
767
768 - def on_conn_stats_updated(self, stats):
769 ''' 770 Handle the retrieved connection statistics. 771 1. update the ROS Network view 772 @param stats: a list with connection statistics 773 @type stats: C{[L{master_discovery_fkie.msg.LinkState}]} 774 ''' 775 #'print "+on_conn_stats_updated" 776 for stat in stats.links: 777 self.master_model.updateMasterStat(stat.destination, stat.quality)
778 #'print "++on_conn_stats_updated" 779 780 781 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 782 #%%%%%%%%%%%%% Handling of master info frame %%%%%%%% 783 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 784
785 - def on_info_clicked(self):
786 text = ''.join(['<dl>']) 787 text = ''.join([text, '<dt><b>Maintainer</b>: ', 'Alexander Tiderko ', '<font color=gray>alexander.tiderko@gmail.com</font>', '</dt>']) 788 text = ''.join([text, '<dt><b>Author</b>: ', 'Alexander Tiderko, Timo Roehling', '</dt>']) 789 text = ''.join([text, '<dt><b>License</b>: ', 'BSD, some icons are licensed under the GNU Lesser General Public License (LGPL) or Creative Commons Attribution-Noncommercial 3.0 License', '</dt>']) 790 text = ''.join([text, '</dl>']) 791 text = ''.join([text, '<dt><b>Version</b>: ', nm.__version__, ' (', nm.__date__,')', '</dt>']) 792 QtGui.QMessageBox.about(self, 'About Node Manager', text)
793
794 - def on_refresh_master_clicked(self):
795 if not self.currentMaster is None: 796 rospy.loginfo("Request an update from %s", str(self.currentMaster.master_state.monitoruri)) 797 if not self.currentMaster.master_info is None: 798 check_ts = self.currentMaster.master_info.check_ts 799 self.currentMaster.master_info.timestamp = self.currentMaster.master_info.timestamp - 1.0 800 self.currentMaster.master_info.check_ts = check_ts 801 if not self.currentMaster.master_state is None: 802 self._update_handler.requestMasterInfo(self.currentMaster.master_state.uri, self.currentMaster.master_state.monitoruri) 803 self.currentMaster.force_next_update()
804 # self.currentMaster.remove_all_def_configs() 805
806 - def on_run_node_clicked(self):
807 ''' 808 Open a dialog to run a ROS node without a configuration 809 ''' 810 from run_dialog import RunDialog 811 if not self.currentMaster is None: 812 dia = RunDialog(nm.nameres().getHostname(self.currentMaster.masteruri), self.currentMaster.masteruri) 813 if dia.exec_(): 814 params = dia.run_params() 815 if params: 816 params = params + (False, self.currentMaster.current_user,) # autorequest must be false 817 try: 818 self._progress_queue.add2queue(str(uuid.uuid4()), 819 'run `%s` on %s'%(params[2], params[0]), 820 nm.starter().runNodeWithoutConfig, 821 params) 822 self._progress_queue.start() 823 except (Exception, nm.StartException), e: 824 rospy.logwarn("Error while run `%s` on %s: %s", params[2], params[0], str(e)) 825 WarningMessageBox(QtGui.QMessageBox.Warning, "Run error", 826 'Error while run node %s [%s]'%(params[2], params[1]), 827 str(e)).exec_()
828
829 - def on_rqt_plugin_start(self, name, plugin):
830 if not self.currentMaster is None: 831 try: 832 args = [] 833 if plugin: 834 args = ['-s', plugin] 835 node_name = 'rqt_%s_%s'%(name.lower().replace(' ', '_'), 836 self.currentMaster.master_state.name.replace('-', '_')) 837 self.currentMaster._progress_queue.add2queue(str(uuid.uuid4()), 838 'start logger level', 839 nm.starter().runNodeWithoutConfig, 840 ('localhost', 'rqt_gui', 'rqt_gui', 841 node_name, args, 842 '%s'%self.currentMaster.master_state.uri, 843 False)) 844 except (Exception, nm.StartException), e: 845 import traceback 846 print traceback.format_exc(1) 847 rospy.logwarn("Error while start %s: %s"%(name, e)) 848 WarningMessageBox(QtGui.QMessageBox.Warning, "Start error", 849 'Error while start %s'%name, 850 '%s'%e).exec_() 851 self.currentMaster._progress_queue.start()
852
853 - def on_sync_dialog_released(self, released=False, masteruri=None, external_call=False):
854 self.syncButton.setEnabled(False) 855 master = self.currentMaster 856 sync_node = None 857 if not masteruri is None: 858 master = self.getMaster(masteruri, False) 859 if master is not None and master.master_info is not None: 860 sync_node = master.master_info.getNodeEndsWith('master_sync') 861 if master is not None and (sync_node is None or external_call): 862 self._sync_dialog.resize(350,190) 863 if self._sync_dialog.exec_(): 864 try: 865 host = nm.nameres().getHostname(master.masteruri) 866 if not self._sync_dialog.interface_filename is None: 867 # copy the interface file to remote machine 868 self._progress_queue_sync.add2queue(str(uuid.uuid4()), 869 'Transfer sync interface %s'%host, 870 nm.starter().transfer_files, 871 ("%s"%host, self._sync_dialog.interface_filename, False, master.current_user)) 872 self._progress_queue_sync.add2queue(str(uuid.uuid4()), 873 'Start sync on %s'%host, 874 nm.starter().runNodeWithoutConfig, 875 ("%s"%host, 'master_sync_fkie', 'master_sync', 'master_sync', self._sync_dialog.sync_args, "%s"%master.masteruri, False, master.current_user)) 876 self._progress_queue_sync.start() 877 except: 878 import traceback 879 WarningMessageBox(QtGui.QMessageBox.Warning, "Start sync error", 880 "Error while start sync node", 881 str(traceback.format_exc(1))).exec_() 882 else: 883 self.syncButton.setChecked(False) 884 elif sync_node is not None: 885 master.stop_nodes([sync_node]) 886 self.syncButton.setEnabled(True)
887
888 - def on_sync_released(self, external_call=False):
889 ''' 890 Enable or disable the synchronization of the master cores 891 ''' 892 key_mod = QtGui.QApplication.keyboardModifiers() 893 if (key_mod & QtCore.Qt.ShiftModifier or key_mod & QtCore.Qt.ControlModifier): 894 if external_call: 895 self.on_sync_dialog_released(external_call=external_call) 896 # else: 897 # self.syncButton.showMenu() 898 if not self.currentMaster.master_info is None: 899 node = self.currentMaster.master_info.getNodeEndsWith('master_sync') 900 self.syncButton.setChecked(not node is None) 901 else: 902 self.syncButton.setEnabled(False) 903 if not self.currentMaster is None: 904 if self.syncButton.isChecked(): 905 # ask the user to start the master_sync with loaded launch file 906 if not self.currentMaster.master_info is None: 907 node = self.currentMaster.getNode('/master_sync') 908 if node and node[0].has_configs(): 909 def_cfg_info = '\nNote: default_cfg parameter will be changed!' if node[0].has_default_cfgs(node[0].cfgs) else '' 910 ret = QtGui.QMessageBox.question(self, 'Start synchronization','Start the synchronization using loaded configuration?\n\n `No` starts the master_sync with default parameter.%s'%def_cfg_info, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) 911 if ret == QtGui.QMessageBox.Yes: 912 self.currentMaster.start_nodes([node[0]]) 913 return 914 915 # start the master sync with default settings 916 sync_args = [] 917 sync_args.append(''.join(['_interface_url:=', "'.'"])) 918 sync_args.append(''.join(['_sync_topics_on_demand:=', 'False'])) 919 sync_args.append(''.join(['_ignore_hosts:=', '[]'])) 920 sync_args.append(''.join(['_sync_hosts:=', '[]'])) 921 sync_args.append(''.join(['_ignore_nodes:=', '[]'])) 922 sync_args.append(''.join(['_sync_nodes:=', '[]'])) 923 sync_args.append(''.join(['_ignore_topics:=', '[]'])) 924 sync_args.append(''.join(['_sync_topics:=', '[]'])) 925 sync_args.append(''.join(['_ignore_services:=', '[]'])) 926 sync_args.append(''.join(['_sync_services:=', '[]'])) 927 sync_args.append(''.join(['_sync_remote_nodes:=', 'False'])) 928 929 try: 930 host = nm.nameres().getHostname(self.currentMaster.masteruri) 931 self._progress_queue_sync.add2queue(str(uuid.uuid4()), 932 'start sync on '+str(host), 933 nm.starter().runNodeWithoutConfig, 934 (str(host), 'master_sync_fkie', 'master_sync', 'master_sync', sync_args, str(self.currentMaster.masteruri), False, self.currentMaster.current_user)) 935 self._progress_queue_sync.start() 936 except: 937 pass 938 elif not self.currentMaster.master_info is None: 939 node = self.currentMaster.master_info.getNodeEndsWith('master_sync') 940 if not node is None: 941 self.currentMaster.stop_nodes([node]) 942 self.syncButton.setEnabled(True)
943
944 - def on_master_timecheck(self):
945 # HACK: sometimes the local monitoring will not be activated. This is the detection. 946 if len(self.masters) < 2 and self.currentMaster is None: 947 self._subscribe() 948 return 949 # update the info panel of the robot. If the node manager is not selected the updates are rarer. 950 current_time = time.time() 951 if self.isActiveWindow() or current_time - self._last_time_view_update > 5: 952 self._last_time_view_update = current_time 953 if not self.currentMaster is None and not self.currentMaster.master_state is None: 954 master = self.getMaster(self.currentMaster.master_state.uri) 955 name = master.master_state.name 956 masteruri = master.master_state.uri 957 if self.restricted_to_one_master: 958 name = ''.join([name, ' <span style=" color:red;">(restricted)</span>']) 959 if not self.masternameLabel.toolTip(): 960 self.masternameLabel.setToolTip('The multicore options are disabled, because the roscore is running on remote host!') 961 if not master.master_info is None: 962 self.showMasterName(masteruri, name, self.timestampStr(master.master_info.check_ts), master.master_state.online) 963 pass 964 elif not master.master_state is None: 965 text = 'Try to get info!!!' 966 if not nm.settings().autoupdate: 967 text = 'Press F5 or click on reload to get info' 968 self.showMasterName(masteruri, name, text, master.master_state.online) 969 else: 970 self.showMasterName('', 'No robot selected', None, False) 971 if (current_time - self._refresh_time > 30.0): 972 masteruri = self.getMasteruri() 973 if not masteruri is None: 974 master = self.getMaster(masteruri) 975 if not master is None and not master.master_state is None and nm.settings().autoupdate: 976 self._update_handler.requestMasterInfo(master.master_state.uri, master.master_state.monitoruri) 977 self._refresh_time = current_time
978 979
980 - def showMasterName(self, masteruri, name, timestamp, online=True):
981 ''' 982 Update the view of the info frame. 983 ''' 984 con_err = '' 985 try: 986 tries = self._con_tries[masteruri] 987 if tries > 1: 988 con_err = '<span style=" color:red;">connection problems (%s tries)! </span>'%str(tries) 989 except: 990 pass 991 if self.__current_master_label_name != name: 992 self.__current_master_label_name = name 993 self.masternameLabel.setText('<span style=" font-size:14pt; font-weight:600;">%s</span>'%name) 994 ts = 'updated: %s'%str(timestamp) if not timestamp is None else '' 995 if not nm.settings().autoupdate: 996 ts = '%s<span style=" color:orange;"> AU off</span>'%ts 997 self.masterInfoLabel.setText('<span style=" font-size:8pt;">%s%s</span>'%(con_err, ts)) 998 999 # load the robot image, if one exists 1000 if self.masternameLabel.isEnabled(): 1001 if self.__icons.has_key(name): 1002 if self.__icons[name][0] != self.__current_icon: 1003 icon = self.__icons[name][0] 1004 self.__current_icon = icon 1005 self.imageLabel.setPixmap(icon.pixmap(self.nameFrame.size())) 1006 self.imageLabel.setToolTip(''.join(['<html><head></head><body><img src="', self.__icons[name][1], '" alt="', name,'"></body></html>'])) 1007 elif self.__icons['default_pc'][0] != self.__current_icon: 1008 icon = self.__icons['default_pc'][0] 1009 self.__current_icon = icon 1010 self.imageLabel.setPixmap(icon.pixmap(self.nameFrame.size())) 1011 self.imageLabel.setToolTip('') 1012 # set sim_time info 1013 master = self.getMaster(masteruri, False) 1014 sim_time_enabled = self.masternameLabel.isEnabled() and not master is None and master.use_sim_time 1015 self.simTimeLabel.setVisible(bool(sim_time_enabled)) 1016 launch_server_enabled = self.masternameLabel.isEnabled() and (not master is None) and master.has_launch_server() 1017 self.launchServerLabel.setVisible(launch_server_enabled) 1018 self.masternameLabel.setEnabled(online) 1019 self.masterInfoFrame.setEnabled((not timestamp is None)) 1020 # update warning symbol / text 1021 if self.logButton.isEnabled(): 1022 if self.logButton.text(): 1023 self.logButton.setIcon(self.__icons['log_warning'][0]) 1024 self.logButton.setText('') 1025 else: 1026 self.logButton.setText('%d'%self.log_dock.count()) 1027 self.logButton.setIcon(self.__icons['empty'][0])
1028 1029
1030 - def timestampStr(self, timestamp):
1031 dt = datetime.fromtimestamp(timestamp) 1032 diff = time.time()-timestamp 1033 diff_dt = datetime.fromtimestamp(diff) 1034 before = '0 sec' 1035 if (diff < 60): 1036 before = diff_dt.strftime('%S sec') 1037 elif (diff < 3600): 1038 before = diff_dt.strftime('%M:%S min') 1039 elif (diff < 86400): 1040 before = diff_dt.strftime('%H:%M:%S std') 1041 else: 1042 before = diff_dt.strftime('%d Day(s) %H:%M:%S') 1043 return '%s (%s)'%(dt.strftime('%H:%M:%S'), before)
1044
1045 - def updateDuplicateNodes(self):
1046 # update the duplicate nodes 1047 running_nodes = dict() 1048 for _, m in self.masters.items(): 1049 if not m.master_state is None and m.master_state.online: 1050 running_nodes.update(m.getRunningNodesIfLocal()) 1051 for _, m in self.masters.items(): 1052 if not m.master_state is None: 1053 m.markNodesAsDuplicateOf(running_nodes)
1054 1055 1056 1057 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1058 #%%%%%%%%%%%%% Handling of master list view %%%%%%%% 1059 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1060
1061 - def on_master_table_pressed(self, selected):
1062 pass
1063
1064 - def on_master_table_clicked(self, selected):
1065 ''' 1066 On click on the sync item, the master_sync node will be started or stopped, 1067 depending on run state. 1068 ''' 1069 item = self.master_model.itemFromIndex(selected) 1070 if isinstance(item, MasterSyncItem): 1071 if MasterSyncItem.START_SYNC != item.synchronized: 1072 self.syncButton.setChecked(item.synchronized != MasterSyncItem.SYNC) 1073 item.synchronized = MasterSyncItem.START_SYNC 1074 self.on_sync_released(True)
1075
1076 - def on_master_table_activated(self, selected):
1077 item = self.master_model.itemFromIndex(selected) 1078 QtGui.QMessageBox.information(self, item.name, item.toolTip())
1079
1080 - def on_master_selection_changed(self, selected):
1081 ''' 1082 If a master was selected, set the corresponding Widget of the stacked layout 1083 to the current widget and shows the state of the selected master. 1084 ''' 1085 # si = self.masterTableView.selectedIndexes() 1086 # for index in si: 1087 # if index.row() == selected.row(): 1088 item = self.master_model.itemFromIndex(selected) 1089 if not item is None: 1090 self._history_selected_robot = item.master.name 1091 self.setCurrentMaster(item.master.uri) 1092 if not self.currentMaster.master_info is None and not self.restricted_to_one_master: 1093 node = self.currentMaster.master_info.getNodeEndsWith('master_sync') 1094 self.syncButton.setEnabled(True) 1095 self.syncButton.setChecked(not node is None) 1096 else: 1097 self.syncButton.setEnabled(False) 1098 return 1099 self.launch_dock.raise_()
1100
1101 - def setCurrentMaster(self, master):
1102 ''' 1103 Changes the view of the master. 1104 :param master: the MasterViewProxy object or masteruri 1105 :type master: MasterViewProxy or str 1106 ''' 1107 show_user_field = False 1108 if isinstance(master, MasterViewProxy): 1109 self.currentMaster = master 1110 self.stackedLayout.setCurrentWidget(master) 1111 show_user_field = not master.is_local 1112 self._add_user_to_combo(self.currentMaster.current_user) 1113 self.userComboBox.setEditText(self.currentMaster.current_user) 1114 elif master is None: 1115 self.currentMaster = None 1116 self.stackedLayout.setCurrentIndex(0) 1117 else: # it's masteruri 1118 self.currentMaster = self.getMaster(master) 1119 if not self.currentMaster is None: 1120 self.stackedLayout.setCurrentWidget(self.currentMaster) 1121 show_user_field = not self.currentMaster.is_local 1122 self._add_user_to_combo(self.currentMaster.current_user) 1123 self.userComboBox.setEditText(self.currentMaster.current_user) 1124 else: 1125 self.stackedLayout.setCurrentIndex(0) 1126 self.user_frame.setVisible(show_user_field) 1127 self.on_master_timecheck()
1128
1129 - def _add_user_to_combo(self, user):
1130 for i in range(self.userComboBox.count()): 1131 if user.lower() == self.userComboBox.itemText(i).lower(): 1132 return 1133 self.userComboBox.addItem(user)
1134
1135 - def on_user_changed(self, user):
1136 if not self.currentMaster is None: 1137 self.currentMaster.current_user = user
1138
1139 - def on_masterTableView_selection_changed(self, selected, deselected):
1140 ''' 1141 On selection of a master list. 1142 ''' 1143 if selected.isValid(): 1144 self.on_master_selection_changed(selected)
1145
1147 ''' 1148 Retrieves from the master_discovery node the list of all discovered ROS 1149 master and get their current state. 1150 ''' 1151 # set the timestamp of the current master info back 1152 for _, m in self.masters.items(): 1153 if not m.master_info is None: 1154 check_ts = m.master_info.check_ts 1155 m.master_info.timestamp = m.master_info.timestamp - 1.0 1156 m.master_info.check_ts = check_ts 1157 self.masterlist_service.refresh(self.getMasteruri(), False)
1158
1160 try: 1161 self._discover_dialog.raise_() 1162 except: 1163 self._discover_dialog = NetworkDiscoveryDialog('226.0.0.0', 11511, 100, self) 1164 self._discover_dialog.network_join_request.connect(self._join_network) 1165 self._discover_dialog.show()
1166
1167 - def on_start_robot_clicked(self):
1168 ''' 1169 Tries to start the master_discovery node on the machine requested by a dialog. 1170 ''' 1171 # get the history list 1172 user_list = [self.userComboBox.itemText(i) for i in reversed(range(self.userComboBox.count()))] 1173 user_list.insert(0, 'last used') 1174 params_optional = {'Discovery type': ('string', ['master_discovery', 'zeroconf']), 1175 'ROS Master Name' : ('string', 'autodetect'), 1176 'ROS Master URI' : ('string', 'ROS_MASTER_URI'), 1177 'Robot hosts' : ('string', ''), 1178 'Username' : ('string', user_list), 1179 'MCast Group' : ('string', '226.0.0.0'), 1180 'Heartbeat [Hz]' : ('float', 0.5) 1181 } 1182 params = {'Host' : ('string', 'localhost'), 1183 'Network(0..99)' : ('int', '0'), 1184 'Optional Parameter' : ('list', params_optional) } 1185 dia = ParameterDialog(params, sidebar_var='Host') 1186 dia.setFilterVisible(False) 1187 dia.setWindowTitle('Start discovery') 1188 dia.resize(450,300) 1189 dia.setFocusField('Host') 1190 if dia.exec_(): 1191 try: 1192 params = dia.getKeywords() 1193 hostnames = params['Host'] if isinstance(params['Host'], list) else [params['Host']] 1194 port = params['Network(0..99)'] 1195 discovery_type = params['Optional Parameter']['Discovery type'] 1196 mastername = 'autodetect' 1197 masteruri = 'ROS_MASTER_URI' 1198 if len(hostnames) < 2: 1199 mastername = params['Optional Parameter']['ROS Master Name'] 1200 masteruri = params['Optional Parameter']['ROS Master URI'] 1201 robot_hosts = params['Optional Parameter']['Robot hosts'] 1202 username = params['Optional Parameter']['Username'] 1203 mcast_group = params['Optional Parameter']['MCast Group'] 1204 heartbeat_hz = params['Optional Parameter']['Heartbeat [Hz]'] 1205 if robot_hosts: 1206 robot_hosts = robot_hosts.replace(' ', '') 1207 robot_hosts = robot_hosts.replace('[', '') 1208 robot_hosts = robot_hosts.replace(']', '') 1209 for hostname in hostnames: 1210 try: 1211 args = [] 1212 if not port is None and port and int(port) < 100 and int(port) >= 0: 1213 args.append('_mcast_port:=%s'%(11511 + int(port))) 1214 else: 1215 args.append('_mcast_port:=%s'%(11511)) 1216 if not mastername == 'autodetect': 1217 args.append('_name:=%s'%(mastername)) 1218 args.append('_mcast_group:=%s'%mcast_group) 1219 args.append('_robot_hosts:=[%s]'%robot_hosts) 1220 args.append('_heartbeat_hz:=%s'%heartbeat_hz) 1221 #TODO: remove the name parameter from the ROS parameter server 1222 usr = username 1223 if username == 'last used': 1224 usr = nm.settings().host_user(hostname) 1225 self._progress_queue.add2queue(str(uuid.uuid4()), 1226 'start discovering on %s'%hostname, 1227 nm.starter().runNodeWithoutConfig, 1228 (str(hostname), 'master_discovery_fkie', str(discovery_type), str(discovery_type), args, (None if masteruri == 'ROS_MASTER_URI' else str(masteruri)), False, usr)) 1229 1230 except (Exception, nm.StartException), e: 1231 import traceback 1232 print traceback.format_exc(1) 1233 rospy.logwarn("Error while start master_discovery for %s: %s"%(str(hostname), e)) 1234 WarningMessageBox(QtGui.QMessageBox.Warning, "Start error", 1235 'Error while start master_discovery', 1236 str(e)).exec_() 1237 self._progress_queue.start() 1238 except Exception, e: 1239 WarningMessageBox(QtGui.QMessageBox.Warning, "Start error", 1240 'Error while parse parameter', 1241 str(e)).exec_()
1242
1243 - def _join_network(self, network):
1244 try: 1245 hostname = 'localhost' 1246 args = [] 1247 if network < 100 and network >= 0: 1248 args.append(''.join(['_mcast_port:=', str(11511 + int(network))])) 1249 self._progress_queue.add2queue(str(uuid.uuid4()), 1250 'start discovering on '+str(hostname), 1251 nm.starter().runNodeWithoutConfig, 1252 (str(hostname), 'master_discovery_fkie', 'master_discovery', 'master_discovery', args, None, False)) 1253 self._progress_queue.start() 1254 except (Exception, nm.StartException), e: 1255 rospy.logwarn("Error while start master_discovery for %s: %s", str(hostname), str(e)) 1256 WarningMessageBox(QtGui.QMessageBox.Warning, "Start error", 1257 'Error while start master_discovery', 1258 str(e)).exec_()
1259 1260 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1261 #%%%%%%%%%%%%% Handling of the launch view signals %%%%%%%% 1262 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1263
1264 - def on_load_launch_file(self, path):
1265 ''' 1266 Load the launch file. A ROS master must be selected first. 1267 :param path: the path of the launch file. 1268 :type path: str 1269 ''' 1270 rospy.loginfo("LOAD launch: %s"%path) 1271 master_proxy = self.stackedLayout.currentWidget() 1272 if isinstance(master_proxy, MasterViewProxy): 1273 # cursor = self.cursor() 1274 # self.setCursor(QtCore.Qt.WaitCursor) 1275 try: 1276 master_proxy.launchfiles = path 1277 except Exception, e: 1278 import traceback 1279 print traceback.format_exc(1) 1280 WarningMessageBox(QtGui.QMessageBox.Warning, "Loading launch file", path, '%s'%e).exec_() 1281 # self.setCursor(cursor) 1282 else: 1283 QtGui.QMessageBox.information(self, "Load of launch file", 1284 "Select a master first!", )
1285
1286 - def on_load_launch_as_default(self, path, host=None):
1287 ''' 1288 Load the launch file as default configuration. A ROS master must be selected first. 1289 :param path: the path of the launch file. 1290 :type path: str 1291 :param host: The host name, where the configuration start. 1292 :type host: str (Default: None) 1293 ''' 1294 rospy.loginfo("LOAD launch as default: %s"%path) 1295 master_proxy = self.stackedLayout.currentWidget() 1296 if isinstance(master_proxy, MasterViewProxy): 1297 args = list() 1298 args.append('_package:=%s'%(package_name(os.path.dirname(path))[0])) 1299 args.append('_launch_file:="%s"'%os.path.basename(path)) 1300 try: 1301 # test for requerid args 1302 launchConfig = LaunchConfig(path) 1303 req_args = launchConfig.getArgs() 1304 if req_args: 1305 params = dict() 1306 arg_dict = launchConfig.argvToDict(req_args) 1307 for arg in arg_dict.keys(): 1308 params[arg] = ('string', [arg_dict[arg]]) 1309 inputDia = ParameterDialog(params) 1310 inputDia.setFilterVisible(False) 1311 inputDia.setWindowTitle('Enter the argv for %s'%path) 1312 if inputDia.exec_(): 1313 params = inputDia.getKeywords() 1314 args.extend(launchConfig.resolveArgs([''.join([p, ":='", v, "'"]) for p,v in params.items() if v])) 1315 else: 1316 return 1317 except: 1318 import traceback 1319 rospy.logwarn('Error while load %s as default: %s'%(path, traceback.format_exc(1))) 1320 hostname = host if host else nm.nameres().address(master_proxy.masteruri) 1321 name_file_prefix = os.path.basename(path).replace('.launch','').replace(' ', '_') 1322 node_name = roslib.names.SEP.join([hostname, 1323 name_file_prefix, 1324 'default_cfg']) 1325 self.launch_dock.progress_queue.add2queue('%s'%uuid.uuid4(), 1326 'start default config %s'%hostname, 1327 nm.starter().runNodeWithoutConfig, 1328 ('%s'%hostname, 'default_cfg_fkie', 'default_cfg', 1329 node_name, args, master_proxy.masteruri, False, 1330 master_proxy.current_user)) 1331 self.launch_dock.progress_queue.start() 1332 else: 1333 QtGui.QMessageBox.information(self, "Load of launch file", 1334 "Select a master first!", )
1335 1336
1337 - def on_launch_edit(self, files, search_text='', trynr=1):
1338 ''' 1339 Opens the given files in an editor. If the first file is already open, select 1340 the editor. If search text is given, search for the text in files an goto the 1341 line. 1342 :param file: A list with paths 1343 :type file: list of strings 1344 :param search_text: A string to search in file 1345 :type search_text: str 1346 ''' 1347 if files: 1348 path = files[0] 1349 if self.editor_dialogs.has_key(path): 1350 last_path = files[-1] 1351 try: 1352 self.editor_dialogs[path].on_load_request(last_path, search_text) 1353 self.editor_dialogs[path].raise_() 1354 self.editor_dialogs[path].activateWindow() 1355 except: 1356 if trynr > 1: 1357 raise 1358 del self.editor_dialogs[path] 1359 self.on_launch_edit(files, search_text, 2) 1360 else: 1361 editor = XmlEditor(files, search_text, self) 1362 self.editor_dialogs[path] = editor 1363 editor.finished_signal.connect(self._editor_dialog_closed) 1364 editor.show()
1365
1366 - def _editor_dialog_closed(self, files):
1367 ''' 1368 If a editor dialog is closed, remove it from the list... 1369 ''' 1370 if self.editor_dialogs.has_key(files[0]): 1371 del self.editor_dialogs[files[0]]
1372
1373 - def on_launch_transfer(self, files):
1374 ''' 1375 Copies the selected file to a remote host 1376 :param file: A list with paths 1377 :type file: list of strings 1378 ''' 1379 if files: 1380 host = 'localhost' 1381 username = nm.settings().default_user 1382 if not self.currentMaster is None: 1383 host = nm.nameres().getHostname(self.currentMaster.masteruri) 1384 username = self.currentMaster.current_user 1385 params = {'Host' : ('string', host), 1386 'recursive' : ('bool', 'False'), 1387 'Username' : ('string', '%s'%username) } 1388 dia = ParameterDialog(params) 1389 dia.setFilterVisible(False) 1390 dia.setWindowTitle('Transfer file') 1391 dia.resize(350,120) 1392 dia.setFocusField('Host') 1393 if dia.exec_(): 1394 try: 1395 params = dia.getKeywords() 1396 host = params['Host'] 1397 recursive = params['recursive'] 1398 username = params['Username'] 1399 for path in files: 1400 rospy.loginfo("TRANSFER to %s@%s: %s"%(username, host, path)) 1401 self.launch_dock.progress_queue.add2queue('%s'%uuid.uuid4(), 1402 'transfer files to %s'%host, 1403 nm.starter().transfer_files, 1404 ('%s'%host, path, False, username)) 1405 if recursive: 1406 for f in LaunchConfig.getIncludedFiles(path): 1407 self.launch_dock.progress_queue.add2queue(str(uuid.uuid4()), 1408 'transfer files to %s'%host, 1409 nm.starter().transfer_files, 1410 ('%s'%host, f, False, username)) 1411 self.launch_dock.progress_queue.start() 1412 except Exception, e: 1413 WarningMessageBox(QtGui.QMessageBox.Warning, "Transfer error", 1414 'Error while transfer files', '%s'%e).exec_()
1415
1416 - def _reload_globals_at_next_start(self, launch_file):
1417 if not self.currentMaster is None: 1418 self.currentMaster.reload_global_parameter_at_next_start(launch_file)
1419 1420 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1421 #%%%%%%%%%%%%% Change file detection %%%%%%%%%%%%%%%%%%% 1422 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1423
1424 - def on_configfile_changed(self, changed, affected):
1425 ''' 1426 Signal hander to handle the changes of a loaded configuration file 1427 @param changed: the changed file 1428 @type changed: C{str} 1429 @param affected: the list of tuples with masteruri and launchfile, which are affected by file change 1430 @type affected: list 1431 ''' 1432 # create a list of launch files and masters, which are affected by the changed file 1433 # and are not currently in question 1434 if self.isActiveWindow(): 1435 self._changed_files[changed] = affected 1436 self._check_for_changed_files() 1437 else: 1438 self._changed_files[changed] = affected
1439
1440 - def _check_for_changed_files(self):
1441 ''' 1442 Check the dictinary with changed files and notify the masters about changes. 1443 ''' 1444 new_affected = list() 1445 for _, affected in self._changed_files.items():#:=changed 1446 for (muri, lfile) in affected: 1447 if not (muri, lfile) in self.__in_question: 1448 self.__in_question.add((muri, lfile)) 1449 new_affected.append((muri, lfile)) 1450 # if there are no question to reload the launch file -> ask 1451 if new_affected: 1452 choices = dict() 1453 for (muri, lfile) in new_affected: 1454 master = self.getMaster(muri) 1455 if not master is None: 1456 master.launchfile = lfile 1457 choices[''.join([os.path.basename(lfile), ' [', master.mastername, ']'])] = (master, lfile) 1458 cfgs, _ = SelectDialog.getValue('Reload configurations?', 1459 '<b>%s</b> was changed.<br>Select affected configurations to reload:'%', '.join([os.path.basename(f) for f in self._changed_files.keys()]), choices.keys(), 1460 False, True, 1461 ':/icons/crystal_clear_launch_file.png', 1462 self) 1463 for (muri, lfile) in new_affected: 1464 self.__in_question.remove((muri, lfile)) 1465 for c in cfgs: 1466 choices[c][0].launchfiles = choices[c][1] 1467 self._changed_files.clear()
1468
1469 - def on_configparamfile_changed(self, changed, affected):
1470 ''' 1471 Signal handler to handle the changes of a configuration file referenced by a parameter value 1472 @param changed: the changed file 1473 @type changed: C{str} 1474 @param affected: the list of tuples with masteruri and launchfile, which are affected by file change 1475 @type affected: list 1476 ''' 1477 # create a list of launch files and masters, which are affected by the changed file 1478 # and are not currently in question 1479 if self.isActiveWindow(): 1480 self._changed_files_param[changed] = affected 1481 self._check_for_changed_files_param() 1482 else: 1483 self._changed_files_param[changed] = affected
1484
1486 ''' 1487 Check the dictinary with changed files and notify about the transfer of changed file. 1488 ''' 1489 new_affected = list() 1490 for changed, affected in self._changed_files_param.items(): 1491 for (muri, lfile) in affected: 1492 if not (muri, changed) in self.__in_question: 1493 self.__in_question.add((muri, changed)) 1494 new_affected.append((muri, changed)) 1495 # if there are no question to reload the launch file -> ask 1496 if new_affected: 1497 choices = dict() 1498 for (muri, lfile) in new_affected: 1499 master = self.getMaster(muri) 1500 if not master is None: 1501 master.launchfile = lfile 1502 choices[''.join([os.path.basename(lfile), ' [', master.mastername, ']'])] = (master, lfile) 1503 cfgs, _ = SelectDialog.getValue('Transfer configurations?', 1504 'Configuration files referenced by parameter are changed.<br>Select affected configurations for copy to remote host: (don\'t forget to restart the nodes!)', 1505 choices.keys(), False, True, 1506 ':/icons/crystal_clear_launch_file_transfer.png', 1507 self) 1508 for (muri, lfile) in new_affected: 1509 self.__in_question.remove((muri, lfile)) 1510 for c in cfgs: 1511 host = '%s'%nm.nameres().getHostname(choices[c][0].masteruri) 1512 username = choices[c][0].current_user 1513 self.launch_dock.progress_queue.add2queue(str(uuid.uuid4()), 1514 'transfer files to %s'%host, 1515 nm.starter().transfer_files, 1516 (host, choices[c][1], False, username)) 1517 self.launch_dock.progress_queue.start() 1518 self._changed_files_param.clear()
1519
1520 - def changeEvent(self, event):
1521 ''' 1522 Check for changed files, if the main gui is activated. 1523 ''' 1524 QtGui.QMainWindow.changeEvent(self, event) 1525 self._check_for_changed_files() 1526 self._check_for_changed_files_param()
1527 1528 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1529 #%%%%%%%%%%%%% Capabilities handling %%%%%%%%%%%%%%%%%%% 1530 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1531
1532 - def on_start_nodes(self, masteruri, cfg, nodes):
1533 if not masteruri is None: 1534 master = self.getMaster(masteruri) 1535 master.start_nodes_by_name(nodes, (cfg, ''))
1536
1537 - def on_stop_nodes(self, masteruri, nodes):
1538 if not masteruri is None: 1539 master = self.getMaster(masteruri) 1540 master.stop_nodes_by_name(nodes)
1541
1542 - def on_description_update(self, title, text):
1543 if (self.sender() == self.currentMaster or not isinstance(self.sender(), MasterViewProxy)) and (not self.descriptionTextEdit.hasFocus() or self._accept_next_update): 1544 self._accept_next_update = False 1545 self.descriptionDock.setWindowTitle(title) 1546 self.descriptionTextEdit.setText(text) 1547 if text and not (self.launch_dock.hasFocus() or self.launch_dock.xmlFileView.hasFocus()): 1548 self.descriptionDock.raise_() 1549 else: 1550 self.launch_dock.raise_()
1551
1552 - def on_description_update_cap(self, title, text):
1553 self.descriptionDock.setWindowTitle(title) 1554 self.descriptionTextEdit.setText(text)
1555
1556 - def on_description_anchorClicked(self, url):
1557 self._accept_next_update = True 1558 if url.toString().startswith('open_sync_dialog://'): 1559 self.on_sync_dialog_released(False, str(url.encodedPath()).replace('open_sync_dialog', 'http'), True) 1560 elif url.toString().startswith('show_all_screens://'): 1561 master = self.getMaster(str(url.encodedPath()).replace('show_all_screens', 'http'), False) 1562 if not master is None: 1563 master.on_show_all_screens() 1564 elif url.toString().startswith('remove_all_launch_server://'): 1565 master = self.getMaster(str(url.encodedPath()).replace('remove_all_launch_server', 'http'), False) 1566 if not master is None: 1567 master.on_remove_all_launch_server() 1568 elif url.toString().startswith('node://'): 1569 if not self.currentMaster is None: 1570 self.currentMaster.on_node_selection_changed(None, None, True, url.encodedPath()) 1571 elif url.toString().startswith('topic://'): 1572 if not self.currentMaster is None: 1573 self.currentMaster.on_topic_selection_changed(None, None, True, url.encodedPath()) 1574 elif url.toString().startswith('topicecho://'): 1575 if not self.currentMaster is None: 1576 self.currentMaster.show_topic_output(url.encodedPath(), False) 1577 elif url.toString().startswith('topichz://'): 1578 if not self.currentMaster is None: 1579 self.currentMaster.show_topic_output(url.encodedPath(), True) 1580 elif url.toString().startswith('topichzssh://'): 1581 if not self.currentMaster is None: 1582 self.currentMaster.show_topic_output(url.encodedPath(), True, use_ssh=True) 1583 elif url.toString().startswith('topicpub://'): 1584 if not self.currentMaster is None: 1585 self.currentMaster.start_publisher(url.encodedPath()) 1586 elif url.toString().startswith('topicrepub://'): 1587 if not self.currentMaster is None: 1588 self.currentMaster.start_publisher(url.encodedPath(), True) 1589 elif url.toString().startswith('topicstop://'): 1590 if not self.currentMaster is None: 1591 self.currentMaster.on_topic_pub_stop_clicked(url.encodedPath()) 1592 elif url.toString().startswith('service://'): 1593 if not self.currentMaster is None: 1594 self.currentMaster.on_service_selection_changed(None, None, True, url.encodedPath()) 1595 elif url.toString().startswith('servicecall://'): 1596 if not self.currentMaster is None: 1597 self.currentMaster.service_call(url.encodedPath()) 1598 elif url.toString().startswith('unregister_node://'): 1599 if not self.currentMaster is None: 1600 self.currentMaster.on_unregister_nodes() 1601 elif url.toString().startswith('start_node://'): 1602 if not self.currentMaster is None: 1603 self.currentMaster.on_start_clicked() 1604 elif url.toString().startswith('restart_node://'): 1605 if not self.currentMaster is None: 1606 self.currentMaster.on_force_start_nodes() 1607 elif url.toString().startswith('start_node_at_host://'): 1608 if not self.currentMaster is None: 1609 self.currentMaster.on_start_nodes_at_host() 1610 elif url.toString().startswith('kill_node://'): 1611 if not self.currentMaster is None: 1612 self.currentMaster.on_kill_nodes() 1613 elif url.toString().startswith('kill_screen://'): 1614 if not self.currentMaster is None: 1615 self.currentMaster.on_kill_screens() 1616 elif url.toString().startswith('copy_log_path://'): 1617 if not self.currentMaster is None: 1618 self.currentMaster.on_log_path_copy() 1619 elif url.toString().startswith('launch://'): 1620 self.on_launch_edit([str(url.encodedPath())], '') 1621 elif url.toString().startswith('reload_globals://'): 1622 self._reload_globals_at_next_start(str(url.encodedPath()).replace('reload_globals://', '')) 1623 else: 1624 QtGui.QDesktopServices.openUrl(url)
1625
1626 - def keyReleaseEvent(self, event):
1627 ''' 1628 Defines some of shortcuts for navigation/management in launch 1629 list view or topics view. 1630 ''' 1631 key_mod = QtGui.QApplication.keyboardModifiers() 1632 if not self.currentMaster is None and self.currentMaster.masterTab.nodeTreeView.hasFocus(): 1633 if event.key() == QtCore.Qt.Key_F4 and not key_mod: 1634 if self.currentMaster.masterTab.editConfigButton.isEnabled(): 1635 self.currentMaster.on_edit_config_clicked() 1636 elif self.currentMaster.masterTab.editRosParamButton.isEnabled(): 1637 self.currentMaster.on_edit_rosparam_clicked() 1638 elif event.key() == QtCore.Qt.Key_F3 and not key_mod and self.currentMaster.masterTab.ioButton.isEnabled(): 1639 self.currentMaster.on_io_clicked() 1640 QtGui.QMainWindow.keyReleaseEvent(self, event)
1641
1642 - def image_mouseDoubleClickEvent(self, event):
1643 ''' 1644 Set the robot image 1645 ''' 1646 if self.currentMaster: 1647 try: 1648 if not os.path.isdir(nm.settings().ROBOTS_DIR): 1649 os.makedirs(nm.settings().ROBOTS_DIR) 1650 (fileName, _) = QtGui.QFileDialog.getOpenFileName(self, 1651 "Set robot image", 1652 nm.settings().ROBOTS_DIR, 1653 "Image files (*.bmp *.gif *.jpg *.jpeg *.png *.pbm *.xbm);;All files (*)") 1654 if fileName and self.__current_master_label_name: 1655 p = QtGui.QPixmap(fileName) 1656 p.save(nm.settings().robot_image_file(self.__current_master_label_name)) 1657 if self.__icons.has_key(self.__current_master_label_name): 1658 del self.__icons[self.__current_master_label_name] 1659 self._assigne_icon(self.__current_master_label_name) 1660 except Exception as e: 1661 WarningMessageBox(QtGui.QMessageBox.Warning, "Error", 1662 ''.join(['Set robot image for ', str(self.__current_master_label_name), ' failed!']), 1663 str(e)).exec_() 1664 rospy.logwarn("Error while set robot image for %s: %s", str(self.__current_master_label_name), str(e))
1665
1666 - def _on_robot_icon_changed(self, masteruri, path):
1667 ''' 1668 One of the robot icons was chagned. Update the icon. 1669 ''' 1670 master = self.getMaster(masteruri, False) 1671 if master: 1672 self._assigne_icon(master.mastername, resolve_url(path))
1673