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