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

Source Code for Module node_manager_fkie.master_view_proxy

   1  # Software License Agreement (BSD License)
 
   2  #
 
   3  # Copyright (c) 2012, Fraunhofer FKIE/US, Alexander Tiderko
 
   4  # All rights reserved.
 
   5  #
 
   6  # Redistribution and use in source and binary forms, with or without
 
   7  # modification, are permitted provided that the following conditions
 
   8  # are met:
 
   9  #
 
  10  #  * Redistributions of source code must retain the above copyright
 
  11  #    notice, this list of conditions and the following disclaimer.
 
  12  #  * Redistributions in binary form must reproduce the above
 
  13  #    copyright notice, this list of conditions and the following
 
  14  #    disclaimer in the documentation and/or other materials provided
 
  15  #    with the distribution.
 
  16  #  * Neither the name of Fraunhofer nor the names of its
 
  17  #    contributors may be used to endorse or promote products derived
 
  18  #    from this software without specific prior written permission.
 
  19  #
 
  20  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 
  21  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 
  22  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 
  23  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 
  24  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 
  25  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 
  26  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 
  27  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 
  28  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 
  29  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 
  30  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 
  31  # POSSIBILITY OF SUCH DAMAGE.
 
  32  
 
  33  from PySide import QtGui 
  34  from PySide import QtCore 
  35  from PySide import QtUiTools 
  36  
 
  37  import os 
  38  import sys 
  39  import socket 
  40  import xmlrpclib 
  41  import threading 
  42  import time 
  43  from urlparse import urlparse 
  44  
 
  45  import rospy 
  46  import roslib 
  47  
 
  48  import node_manager_fkie as nm 
  49  from html_delegate import HTMLDelegate 
  50  from topic_list_model import TopicModel, TopicItem 
  51  from node_tree_model import NodeTreeModel, NodeItem, GroupItem, HostItem 
  52  from service_list_model import ServiceModel, ServiceItem 
  53  from parameter_list_model import ParameterModel, ParameterValueItem 
  54  from default_cfg_handler import DefaultConfigHandler 
  55  from launch_config import LaunchConfig, LaunchConfigException 
  56  from master_discovery_fkie.master_info import NodeInfo  
  57  from parameter_dialog import ParameterDialog, MasterParameterDialog, ServiceDialog 
  58  from select_dialog import SelectDialog 
  59  from echo_dialog import EchoDialog 
  60  from parameter_handler import ParameterHandler 
  61  from detailed_msg_box import WarningMessageBox, DetailedError 
  62  from progress_queue import ProgressQueue, ProgressThread 
63 64 65 66 67 -class MasterViewProxy(QtGui.QWidget):
68 ''' 69 This class stores the informations about a ROS master and shows it on request. 70 ''' 71 72 updateHostRequest = QtCore.Signal(str) 73 host_description_updated = QtCore.Signal(str, str, str) 74 '''@ivar: the signal is emitted on description changes and contains the 75 ROS Master URI, host address and description a parameter.''' 76 77 capabilities_update_signal = QtCore.Signal(str, str, str, list) 78 '''@ivar: the signal is emitted if a description with capabilities is received 79 and has the ROS master URI, host address, the name of the default_cfg node and a list with 80 descriptions (L{default_cfg_fkie.Description}) as parameter.''' 81 remove_config_signal = QtCore.Signal(str) 82 '''@ivar: the signal is emitted if a default_cfg was removed''' 83 84 description_signal = QtCore.Signal(str, str) 85 '''@ivar: the signal is emitted to show a description (title, description)''' 86 87 request_xml_editor = QtCore.Signal(list, str) 88 '''@ivar: the signal to open a xml editor dialog (list with files, search text)''' 89
90 - def __init__(self, masteruri, parent=None):
91 ''' 92 Creates a new master. 93 @param masteruri: the URI of the ROS master 94 @type masteruri: C{str} 95 ''' 96 QtGui.QWidget.__init__(self, parent) 97 self.setObjectName(' - '.join(['MasterViewProxy', masteruri])) 98 self.masteruri = masteruri 99 self.mastername = masteruri 100 try: 101 o = urlparse(self.master.uri) 102 self.mastername = o.hostname 103 except: 104 pass 105 self._tmpObjects = [] 106 self.__master_state = None 107 self.__master_info = None 108 self.__configs = dict() # [file name] = LaunchConfig 109 # self.rosconfigs = dict() # [launch file path] = LaunchConfig() 110 self.__in_question = [] # stores the changed files, until the user is interacted 111 # self.__uses_confgs = dict() # stores the decisions of the user for used configuration to start of node 112 '''@ivar: stored the question dialogs for changed files ''' 113 self._stop_ignores = ['rosout', rospy.get_name(), 'node_manager', 'master_discovery', 'master_sync', 'default_cfg', 'zeroconf'] 114 ''' @todo: detect the names of master_discovery and master_sync ndoes''' 115 116 self.__echo_topics_dialogs = dict() # [topic name] = EchoDialog 117 '''@ivar: stores the open EchoDialogs ''' 118 self.__last_info_text = None 119 120 self.default_cfg_handler = DefaultConfigHandler() 121 self.default_cfg_handler.node_list_signal.connect(self.on_default_cfg_nodes_retrieved) 122 self.default_cfg_handler.description_signal.connect(self.on_default_cfg_descr_retrieved) 123 self.default_cfg_handler.err_signal.connect(self.on_default_cfg_err) 124 125 loader = QtUiTools.QUiLoader() 126 self.masterTab = loader.load(":/forms/MasterTab.ui") 127 tabLayout = QtGui.QVBoxLayout(self) 128 tabLayout.setContentsMargins(0, 0, 0, 0) 129 tabLayout.addWidget(self.masterTab) 130 self._progress_queue = ProgressQueue(self.masterTab.progressFrame, self.masterTab.progressBar, self.masterTab.progressCancelButton) 131 132 # setup the node view 133 self.node_tree_model = NodeTreeModel(nm.nameres().address(self.masteruri), self.masteruri) 134 self.node_tree_model.hostInserted.connect(self.on_host_inserted) 135 self.masterTab.nodeTreeView.setModel(self.node_tree_model) 136 for i, (name, width) in enumerate(NodeTreeModel.header): 137 self.masterTab.nodeTreeView.setColumnWidth(i, width) 138 self.nodeNameDelegate = HTMLDelegate() 139 self.masterTab.nodeTreeView.setItemDelegateForColumn(0, self.nodeNameDelegate) 140 self.masterTab.nodeTreeView.collapsed.connect(self.on_node_collapsed) 141 self.masterTab.nodeTreeView.expanded.connect(self.on_node_expanded) 142 sm = self.masterTab.nodeTreeView.selectionModel() 143 sm.selectionChanged.connect(self.on_node_selection_changed) 144 self.masterTab.nodeTreeView.activated.connect(self.on_node_activated) 145 # self.masterTab.nodeTreeView.setAcceptDrops(True) 146 # self.masterTab.nodeTreeWidget.setSortingEnabled(True) 147 148 # setup the topic view 149 self.topic_model = TopicModel() 150 self.topic_proxyModel = TopicsSortFilterProxyModel(self) 151 self.topic_proxyModel.setSourceModel(self.topic_model) 152 self.masterTab.topicsView.setModel(self.topic_proxyModel) 153 # self.masterTab.topicsView.setModel(self.topic_model) 154 for i, (name, width) in enumerate(TopicModel.header): 155 self.masterTab.topicsView.setColumnWidth(i, width) 156 self.topicNameDelegate = HTMLDelegate() 157 self.masterTab.topicsView.setItemDelegateForColumn(0, self.topicNameDelegate) 158 sm = self.masterTab.topicsView.selectionModel() 159 sm.selectionChanged.connect(self.on_topic_selection_changed) 160 self.masterTab.topicsView.activated.connect(self.on_topic_activated) 161 self.masterTab.topicsView.setSortingEnabled(True) 162 # self.topic_proxyModel.filterAcceptsRow = _filterTopicsAcceptsRow 163 164 # setup the service view 165 self.service_model = ServiceModel() 166 self.service_proxyModel = ServicesSortFilterProxyModel(self) 167 self.service_proxyModel.setSourceModel(self.service_model) 168 self.masterTab.servicesView.setModel(self.service_proxyModel) 169 # self.masterTab.servicesView.setModel(self.service_model) 170 for i, (name, width) in enumerate(ServiceModel.header): 171 self.masterTab.servicesView.setColumnWidth(i, width) 172 self.serviceNameDelegate = HTMLDelegate() 173 self.serviceTypeDelegate = HTMLDelegate() 174 self.masterTab.servicesView.setItemDelegateForColumn(0, self.serviceNameDelegate) 175 self.masterTab.servicesView.setItemDelegateForColumn(1, self.serviceTypeDelegate) 176 sm = self.masterTab.servicesView.selectionModel() 177 sm.selectionChanged.connect(self.on_service_selection_changed) 178 self.masterTab.servicesView.activated.connect(self.on_service_activated) 179 self.masterTab.servicesView.setSortingEnabled(True) 180 # self.service_proxyModel.filterAcceptsRow = _filterServiceAcceptsRow 181 182 # setup the parameter view 183 self.parameter_model = ParameterModel() 184 self.parameter_model.itemChanged.connect(self._on_parameter_item_changed) 185 self.parameter_proxyModel = ParameterSortFilterProxyModel(self) 186 self.parameter_proxyModel.setSourceModel(self.parameter_model) 187 self.masterTab.parameterView.setModel(self.parameter_proxyModel) 188 for i, (name, width) in enumerate(ParameterModel.header): 189 self.masterTab.parameterView.setColumnWidth(i, width) 190 self.parameterNameDelegate = HTMLDelegate() 191 self.masterTab.parameterView.setItemDelegateForColumn(0, self.parameterNameDelegate) 192 # self.parameterEditor = QtGui.QStyledItemDelegate() 193 # factory = QtGui.QItemEditorFactory.defaultFactory() 194 # self.parameterEditor.setItemEditorFactory(factory) 195 # self.masterTab.parameterView.setItemDelegateForColumn(1, self.parameterEditor) 196 sm = self.masterTab.parameterView.selectionModel() 197 sm.selectionChanged.connect(self.on_parameter_selection_changed) 198 self.masterTab.parameterView.setSortingEnabled(True) 199 200 # self.parameter_proxyModel.filterAcceptsRow = _filterParameterAcceptsRow 201 # self.masterTab.parameterView.activated.connect(self.on_service_activated) 202 203 # connect the buttons 204 self.masterTab.startButton.clicked.connect(self.on_start_clicked) 205 self.masterTab.stopButton.clicked.connect(self.on_stop_clicked) 206 # self.masterTab.stopContextButton.toggled.connect(self.on_stop_context_toggled) 207 self.masterTab.ioButton.clicked.connect(self.on_io_clicked) 208 self.masterTab.logButton.clicked.connect(self.on_log_clicked) 209 self.masterTab.logDeleteButton.clicked.connect(self.on_log_delete_clicked) 210 self.masterTab.dynamicConfigButton.clicked.connect(self.on_dynamic_config_clicked) 211 self.masterTab.editConfigButton.clicked.connect(self.on_edit_config_clicked) 212 self.masterTab.editRosParamButton.clicked.connect(self.on_edit_rosparam_clicked) 213 self.masterTab.closeCfgButton.clicked.connect(self.on_close_clicked) 214 215 self.masterTab.echoTopicButton.clicked.connect(self.on_topic_echo_clicked) 216 self.masterTab.hzTopicButton.clicked.connect(self.on_topic_hz_clicked) 217 self.masterTab.pubTopicButton.clicked.connect(self.on_topic_pub_clicked) 218 self.masterTab.pubStopTopicButton.clicked.connect(self.on_topic_pub_stop_clicked) 219 220 self.masterTab.callServiceButton.clicked.connect(self.on_service_call_clicked) 221 self.masterTab.topicFilterInput.textChanged.connect(self.on_topic_filter_changed) 222 self.masterTab.serviceFilterInput.textChanged.connect(self.on_service_filter_changed) 223 self.masterTab.parameterFilterInput.textChanged.connect(self.on_parameter_filter_changed) 224 self.masterTab.getParameterButton.clicked.connect(self.on_get_parameter_clicked) 225 self.masterTab.addParameterButton.clicked.connect(self.on_add_parameter_clicked) 226 self.masterTab.deleteParameterButton.clicked.connect(self.on_delete_parameter_clicked) 227 228 #create a handler to request the parameter 229 self.parameterHandler = ParameterHandler() 230 self.parameterHandler.parameter_list_signal.connect(self._on_param_list) 231 self.parameterHandler.parameter_values_signal.connect(self._on_param_values) 232 self.parameterHandler.delivery_result_signal.connect(self._on_delivered_values) 233 234 # creates a start menu 235 start_menu = QtGui.QMenu(self) 236 self.forceStartNodesAct = QtGui.QAction("&Force start node", self, statusTip="Force the start of selected node", triggered=self.on_force_start_nodes) 237 start_menu.addAction(self.forceStartNodesAct) 238 self.startNodesAtHostAct = QtGui.QAction("&Start node on host(Loaded config needed)", self, statusTip="Start node on other host", triggered=self.on_start_nodes_at_host) 239 start_menu.addAction(self.startNodesAtHostAct) 240 self.masterTab.startButton.setMenu(start_menu) 241 242 # creates a stop menu 243 stop_menu = QtGui.QMenu(self) 244 self.killNodesAct = QtGui.QAction("&Kill Node", self, shortcut=QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_Backspace), statusTip="Kill selected node", triggered=self.on_kill_nodes) 245 self.unregNodesAct = QtGui.QAction("&Unregister Nodes", self, shortcut=QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_Delete), statusTip="Removes the registration of selected nodes from ROS master", triggered=self.on_unregister_nodes) 246 stop_menu.addAction(self.killNodesAct) 247 stop_menu.addAction(self.unregNodesAct) 248 self.masterTab.stopButton.setMenu(stop_menu) 249 250 # creates a screen menu 251 screen_menu = QtGui.QMenu(self) 252 self.killScreensAct = QtGui.QAction("&Kill Screen", self, shortcut=QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Backspace), statusTip="Kill available screens", triggered=self.on_kill_screens) 253 screen_menu.addAction(self.killScreensAct) 254 self.showAllScreensAct = QtGui.QAction("&Show all available screens", self, shortcut=QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_S), statusTip="Shows all available screens", triggered=self.on_show_all_screens) 255 screen_menu.addAction(self.showAllScreensAct) 256 self.masterTab.ioButton.setMenu(screen_menu) 257 self.masterTab.ioButton.setEnabled(True) 258 259 # create log menu 260 log_menu = QtGui.QMenu(self) 261 self.logCopyPathAct = QtGui.QAction("&Copy log path to clipboard", self, statusTip="Copy log path to clipboard", triggered=self.on_log_path_copy) 262 # self.logShowSelectedAct = QtGui.QAction("&Select a log to show", self, statusTip="Select a log file to show (only local)", triggered=self.on_log_show_selected) 263 log_menu.addAction(self.logCopyPathAct) 264 # self.logCopyPathAct.setEnabled(nm.is_local(nm.nameres().getHostname(self.masteruri))) 265 # log_menu.addAction(self.logShowSelectedAct) 266 self.masterTab.logButton.setMenu(log_menu) 267 268 # set the shortcuts 269 self._shortcut1 = QtGui.QShortcut(QtGui.QKeySequence(self.tr("Alt+1", "Select first group")), self) 270 self._shortcut1.activated.connect(self.on_shortcut1_activated) 271 self._shortcut2 = QtGui.QShortcut(QtGui.QKeySequence(self.tr("Alt+2", "Select second group")), self) 272 self._shortcut2.activated.connect(self.on_shortcut2_activated) 273 self._shortcut3 = QtGui.QShortcut(QtGui.QKeySequence(self.tr("Alt+3", "Select third group")), self) 274 self._shortcut3.activated.connect(self.on_shortcut3_activated) 275 self._shortcut4 = QtGui.QShortcut(QtGui.QKeySequence(self.tr("Alt+4", "Select fourth group")), self) 276 self._shortcut4.activated.connect(self.on_shortcut4_activated) 277 self._shortcut5 = QtGui.QShortcut(QtGui.QKeySequence(self.tr("Alt+5", "Select fifth group")), self) 278 self._shortcut5.activated.connect(self.on_shortcut5_activated) 279 280 self._shortcut_collapse_all = QtGui.QShortcut(QtGui.QKeySequence(self.tr("Alt+C", "Collapse all groups")), self) 281 self._shortcut_collapse_all.activated.connect(self.on_shortcut_collapse_all) 282 self._shortcut_expand_all = QtGui.QShortcut(QtGui.QKeySequence(self.tr("Alt+E", "Expand all groups")), self) 283 self._shortcut_expand_all.activated.connect(self.masterTab.nodeTreeView.expandAll) 284 self._shortcut_run = QtGui.QShortcut(QtGui.QKeySequence(self.tr("Alt+R", "run selected nodes")), self) 285 self._shortcut_run.activated.connect(self.on_start_clicked) 286 self._shortcut_stop = QtGui.QShortcut(QtGui.QKeySequence(self.tr("Alt+S", "stop selected nodes")), self) 287 self._shortcut_stop.activated.connect(self.on_stop_clicked) 288 289 self._shortcut_copy = QtGui.QShortcut(QtGui.QKeySequence(self.tr("Ctrl+C", "copy selected nodes to clipboard")), self.masterTab.nodeTreeView) 290 self._shortcut_copy.activated.connect(self.on_copy_node_clicked) 291 self._shortcut_copy = QtGui.QShortcut(QtGui.QKeySequence(self.tr("Ctrl+C", "copy selected topics to clipboard")), self.masterTab.topicsView) 292 self._shortcut_copy.activated.connect(self.on_copy_topic_clicked) 293 self._shortcut_copy = QtGui.QShortcut(QtGui.QKeySequence(self.tr("Ctrl+C", "copy selected services to clipboard")), self.masterTab.servicesView) 294 self._shortcut_copy.activated.connect(self.on_copy_service_clicked) 295 self._shortcut_copy = QtGui.QShortcut(QtGui.QKeySequence(self.tr("Ctrl+C", "copy selected parameter to clipboard")), self.masterTab.parameterView) 296 self._shortcut_copy.activated.connect(self.on_copy_parameter_clicked)
297 298 # print "================ create", self.objectName() 299 # 300 # def __del__(self): 301 # for ps in self.__echo_topics_dialogs.values(): 302 # try: 303 # ps.terminate() 304 # except: 305 # pass 306 # print "*********** destroy", self.objectName() 307
308 - def stop(self):
309 print " Shutdown master", self.masteruri, "..." 310 self.default_cfg_handler.stop() 311 self._progress_queue.stop() 312 for ps in self.__echo_topics_dialogs.values(): 313 try: 314 ps.terminate() 315 except: 316 pass 317 print " Master", self.masteruri, " is down!"
318 319 @property
320 - def master_state(self):
321 return self.__master_state
322 323 @master_state.setter
324 - def master_state(self, master_state):
325 self.__master_state = master_state
326 327 @property
328 - def master_info(self):
329 return self.__master_info
330 331 @master_info.setter
332 - def master_info(self, master_info):
333 ''' 334 Sets the new master information. To determine whether a node is running the 335 PID and his URI are needed. The PID of remote nodes (host of the ROS master 336 and the node are different) will be not determine by discovering. Thus this 337 information must be obtain from other MasterInfo object and stored while 338 updating. 339 @param master_info: the mater information object 340 @type master_info: L{master_discovery_fkie.msg.MasterInfo} 341 ''' 342 try: 343 update_nodes = False 344 update_others = False 345 if (master_info.masteruri == self.masteruri): 346 self.mastername = master_info.mastername 347 # store process pid's of remote nodes 348 nodepids = [] 349 if not self.__master_info is None: 350 nodepids = [(n.name, n.pid, n.uri, n.masteruri) for nodename, n in self.__master_info.nodes.items() if not n.isLocal] 351 self.__master_info = master_info 352 # restore process pid's of remote nodes 353 if not self.__master_info is None: 354 for nodename, pid, uri, muri in nodepids: 355 node = self.__master_info.getNode(nodename) 356 if not node is None: 357 node.pid = pid 358 node.masteruri = muri 359 # request master info updates for new remote nodes 360 nodepids2 = [(n.name, n.pid, n.uri) for nodename, n in self.__master_info.nodes.items() if not n.isLocal] 361 node2update = list(set(nodepids2)-set(nodepids)) 362 hosts2update = list(set([nm.nameres().getHostname(uri) for nodename, pid, uri in node2update])) 363 for host in hosts2update: 364 self.updateHostRequest.emit(host) 365 update_nodes = True 366 update_others = True 367 else: 368 if not (self.__master_info is None or master_info is None): 369 for nodename, node in master_info.nodes.items(): 370 n = self.__master_info.getNode(nodename) 371 if not n is None and n.pid != node.pid and not n.isLocal: 372 update_nodes = True 373 n.pid = node.pid 374 for servicename, service in master_info.services.items(): 375 s = self.__master_info.getService(servicename) 376 if not s is None and s.uri != service.uri and not s.isLocal: 377 update_others = True 378 s.uri = service.uri 379 380 # cputimes = os.times() 381 # cputime_init = cputimes[0] + cputimes[1] 382 if update_nodes: 383 # print " updateRunningNodesInModel", time.time() 384 self.updateRunningNodesInModel(self.__master_info) 385 # print " updateRunningNodesInModel total", time.time() 386 if update_others: 387 # print " updateTopicsListModel", time.time(), "topic count:", len(self.__master_info.topics) 388 self.updateTopicsListModel(self.__master_info) 389 # print " updateServiceListModel", time.time(), "services count:", len(self.__master_info.services) 390 self.updateServiceListModel(self.__master_info) 391 # print " updateDefaultConfigs", time.time() 392 self.updateDefaultConfigs(self.__master_info) 393 # print " updateDefaultConfigs", time.time() 394 # cputimes = os.times() 395 # cputime = cputimes[0] + cputimes[1] - cputime_init 396 # print " update on ", self.__master_info.mastername if not self.__master_info is None else self.__master_state.name, cputime 397 self.on_node_selection_changed(None, None) 398 self.on_topic_selection_changed(None, None) 399 self.on_service_selection_changed(None, None) 400 except: 401 import traceback 402 print traceback.format_exc()
403
404 - def markNodesAsDuplicateOf(self, running_nodes):
405 ''' 406 Marks all nodes, which are not running and in a given list as a duplicates nodes. 407 @param running_nodes: The list with names of running nodes 408 @type running_nodes: C{[str]} 409 ''' 410 self.node_tree_model.markNodesAsDuplicateOf(running_nodes)
411
412 - def getRunningNodesIfSync(self):
413 ''' 414 Returns the list with all running nodes, which are registered by this ROS 415 master. Also the nodes, which are physically running on remote hosts. 416 @return running_nodes: The list with names of running nodes 417 @rtype running_nodes: C{[str]} 418 ''' 419 if not self.master_info is None and self.master_info.getNodeEndsWith('master_sync'): 420 return self.master_info.node_names 421 return []
422
423 - def getRunningNodesIfLocal(self):
424 ''' 425 Returns the list with all running nodes, which are running (has process) on this host. 426 The nodes registered on this ROS master, but running on remote hosts are not 427 returned. 428 @return running_nodes: The list with names of running nodes 429 @rtype running_nodes: C{[str]} 430 ''' 431 result = [] 432 if not self.master_info is None: 433 for name, node in self.master_info.nodes.items(): 434 if node.isLocal: 435 result.append(node.name) 436 return result
437 438
439 - def updateRunningNodesInModel(self, master_info):
440 ''' 441 Creates the dictionary with ExtendedNodeInfo objects and updates the nodes view. 442 @param master_info: the mater information object 443 @type master_info: L{master_discovery_fkie.msg.MasterInfo} 444 ''' 445 if not master_info is None: 446 self.node_tree_model.updateModelData(master_info.nodes) 447 self.updateButtons()
448
449 - def updateButtons(self):
450 ''' 451 Updates the enable state of the buttons depending of the selection and 452 running state of the selected node. 453 ''' 454 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes()) 455 has_running = False 456 has_stopped = False 457 has_invalid = False 458 for node in selectedNodes: 459 if not node.uri is None: 460 has_running = True 461 if node.pid is None: 462 has_invalid = True 463 else: 464 has_stopped = True 465 # self.masterTab.startButton.setEnabled(has_stopped or has_invalid) 466 # self.masterTab.stopButton.setEnabled(has_running or has_invalid) 467 self.masterTab.startButton.setEnabled(True) 468 self.masterTab.stopButton.setEnabled(True) 469 # self.masterTab.ioButton.setEnabled(has_running or has_stopped) 470 self.masterTab.logButton.setEnabled(True) 471 # self.masterTab.logButton.setEnabled(has_running or has_stopped) 472 self.masterTab.logDeleteButton.setEnabled(has_running or has_stopped) 473 # test for available dynamic reconfigure services 474 if not self.master_info is None: 475 dyncfgServices = [s[:-len('/set_parameters')] for s in self.master_info.services.keys() if (s.endswith('/set_parameters'))] 476 dyncfgNodes = [s for n in selectedNodes for s in dyncfgServices if s.startswith((n.name))] 477 self.masterTab.dynamicConfigButton.setEnabled(len(dyncfgNodes)) 478 # the configuration is only available, if only one node is selected 479 cfg_enable = False 480 if len(selectedNodes) >= 1: 481 cfg_enable = len(self._getCfgChoises(selectedNodes[0], True)) > 0 482 self.masterTab.editConfigButton.setEnabled(cfg_enable and len(selectedNodes) == 1) 483 self.startNodesAtHostAct.setEnabled(cfg_enable) 484 self.masterTab.editRosParamButton.setEnabled(len(selectedNodes) == 1) 485 self.masterTab.closeCfgButton.setEnabled(len(self.__configs) > 0)
486 487
488 - def updateTopicsListModel(self, master_info):
489 ''' 490 Updates the topic view based on the current master information. 491 @param master_info: the mater information object 492 @type master_info: L{master_discovery_fkie.msg.MasterInfo} 493 ''' 494 if not master_info is None: 495 self.topic_model.updateModelData(master_info.topics)
496
497 - def updateServiceListModel(self, master_info):
498 ''' 499 Updates the service view based on the current master information. 500 @param master_info: the mater information object 501 @type master_info: L{master_discovery_fkie.msg.MasterInfo} 502 ''' 503 if not master_info is None: 504 self.service_model.updateModelData(master_info.services)
505
506 - def hasLaunchfile(self, path):
507 ''' 508 @param path: the launch file 509 @type path: C{str} 510 @return: C{True} if the given launch file is open 511 @rtype: C{boolean} 512 ''' 513 return launchfiles.has_key(path)
514 515 @property
516 - def launchfiles(self):
517 ''' 518 Returns the copy of the dictionary with loaded launch files on this host 519 @rtype: C{dict(str(file) : L{LaunchConfig}, ...)} 520 ''' 521 result = dict() 522 for (c, cfg) in self.__configs.items(): 523 if not isinstance(c, tuple): 524 result[c] = cfg 525 return result
526 527 @launchfiles.setter
528 - def launchfiles(self, launchfile):
529 ''' 530 Loads the launch file. If this file is already loaded, it will be reloaded. 531 After successful load the node view will be updated. 532 @param launchfile: the launch file path 533 @type launchfile: C{str} 534 ''' 535 # QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.ExcludeUserInputEvents) 536 if self.__configs.has_key(launchfile): 537 # close the configuration 538 self.removeConfigFromModel(launchfile) 539 # # remove machine entries 540 # try: 541 # for name, machine in self.__configs[launchfile].Roscfg.machines.items(): 542 # if machine.name: 543 # nm.nameres().remove(name=machine.name, host=machine.address) 544 # except: 545 # import traceback 546 # print traceback.format_exc() 547 #load launch config 548 try: 549 # test for requerid args 550 launchConfig = LaunchConfig(launchfile, masteruri=self.masteruri) 551 req_args = launchConfig.getArgs() 552 loaded = False 553 if req_args: 554 params = dict() 555 arg_dict = launchConfig.argvToDict(req_args) 556 for arg in arg_dict.keys(): 557 params[arg] = ('string', [arg_dict[arg]]) 558 inputDia = ParameterDialog(params) 559 inputDia.setFilterVisible(False) 560 inputDia.setWindowTitle(''.join(['Enter the argv for ', launchfile])) 561 if inputDia.exec_(): 562 params = inputDia.getKeywords() 563 argv = [] 564 for p,v in params.items(): 565 if v: 566 argv.append(''.join([p, ':=', v])) 567 loaded = launchConfig.load(argv) 568 else: 569 return 570 if not loaded: 571 launchConfig.load(req_args) 572 self.__configs[launchfile] = launchConfig 573 #connect the signal, which is emitted on changes on configuration file 574 launchConfig.file_changed.connect(self.on_configfile_changed) 575 for name, machine in launchConfig.Roscfg.machines.items(): 576 if machine.name: 577 nm.nameres().addInfo(machine.name, machine.address, machine.address) 578 self.appendConfigToModel(launchfile, launchConfig.Roscfg) 579 self.masterTab.tabWidget.setCurrentIndex(0) 580 581 #get the descriptions of capabilities and hosts 582 try: 583 robot_descr = launchConfig.getRobotDescr() 584 capabilities = launchConfig.getCapabilitiesDesrc() 585 for (host, caps) in capabilities.items(): 586 if not host: 587 host = nm.nameres().mastername(self.masteruri) 588 host_addr = nm.nameres().address(host) 589 self.node_tree_model.addCapabilities(nm.nameres().masteruri(host), host_addr, launchfile, caps) 590 for (host, descr) in robot_descr.items(): 591 if not host: 592 host = nm.nameres().mastername(self.masteruri) 593 host_addr = nm.nameres().address(host) 594 tooltip = self.node_tree_model.updateHostDescription(nm.nameres().masteruri(host), host_addr, descr['type'], descr['name'], descr['description']) 595 self.host_description_updated.emit(self.masteruri, host_addr, tooltip) 596 except: 597 import traceback 598 print traceback.print_exc() 599 # by this call the name of the host will be updated if a new one is defined in the launch file 600 self.updateRunningNodesInModel(self.__master_info) 601 # print "MASTER:", launchConfig.Roscfg.master 602 # print "NODES_CORE:", launchConfig.Roscfg.nodes_core 603 # for n in launchConfig.Roscfg.nodes: 604 # n.__slots__ = [] 605 # print "NODES:", pickle.dumps(launchConfig.Roscfg.nodes) 606 # 607 # print "ROSLAUNCH_FILES:", launchConfig.Roscfg.roslaunch_files 608 # # list of resolved node names. This is so that we can check for naming collisions 609 # print "RESOLVED_NAMES:", launchConfig.Roscfg.resolved_node_names 610 # 611 # print "TESTS:", launchConfig.Roscfg.tests 612 # print "MACHINES:", launchConfig.Roscfg.machines 613 # print "PARAMS:", launchConfig.Roscfg.params 614 # print "CLEAR_PARAMS:", launchConfig.Roscfg.clear_params 615 # print "EXECS:", launchConfig.Roscfg.executables 616 # 617 # # for tools like roswtf 618 # print "ERRORS:", launchConfig.Roscfg.config_errors 619 # 620 # print "M:", launchConfig.Roscfg.m 621 except Exception, e: 622 import os 623 err_text = ''.join([os.path.basename(launchfile),' loading failed!']) 624 err_details = '\n\n'.join([err_text, str(e)]) 625 rospy.logwarn("Loading launch file: %s", err_details) 626 WarningMessageBox(QtGui.QMessageBox.Warning, "Loading launch file", err_text, err_details).exec_()
627
628 - def appendConfigToModel(self, launchfile, rosconfig):
629 ''' 630 Update the node view 631 @param launchfile: the launch file path 632 @type launchfile: C{str} 633 @param rosconfig: the configuration 634 @type rosconfig: L{LaunchConfig} 635 ''' 636 hosts = dict() # dict(addr : dict(node : [config]) ) 637 for n in rosconfig.nodes: 638 addr = nm.nameres().address(self.masteruri) 639 masteruri = self.masteruri 640 if n.machine_name and not n.machine_name == 'localhost': 641 if not rosconfig.machines.has_key(n.machine_name): 642 raise Exception(''.join(["ERROR: unknown machine [", n.machine_name,"]"])) 643 addr = rosconfig.machines[n.machine_name].address 644 masteruri = nm.nameres().masteruri(n.machine_name) 645 node = roslib.names.ns_join(n.namespace, n.name) 646 if not hosts.has_key((masteruri, addr)): 647 hosts[(masteruri, addr)] = dict() 648 hosts[(masteruri, addr)][node] = launchfile 649 # add the configurations for each host separately 650 for ((masteruri, addr), nodes) in hosts.items(): 651 self.node_tree_model.appendConfigNodes(masteruri, addr, nodes) 652 self.updateButtons()
653
654 - def removeConfigFromModel(self, launchfile):
655 ''' 656 Update the node view after removed configuration. 657 @param launchfile: the launch file path 658 @type launchfile: C{str} 659 ''' 660 if isinstance(launchfile, tuple): 661 self.remove_config_signal.emit(launchfile[0]) 662 self.node_tree_model.removeConfigNodes(launchfile) 663 self.updateButtons()
664
665 - def updateDefaultConfigs(self, master_info):
666 ''' 667 Updates the default configuration view based on the current master information. 668 @param master_info: the mater information object 669 @type master_info: L{master_discovery_fkie.msg.MasterInfo} 670 ''' 671 if self.__master_info is None: 672 return 673 default_cfgs = [] 674 for name in self.__master_info.service_names: 675 if name.endswith('list_nodes'): 676 srv = self.__master_info.getService(name) 677 default_cfgs.append((roslib.names.namespace(name).rstrip(roslib.names.SEP), srv.uri, srv.masteruri)) 678 # remove the node contained in default configuration form the view 679 removed = list(set([c for c in self.__configs.keys() if isinstance(c, tuple)]) - set(default_cfgs)) 680 if removed: 681 for r in removed: 682 host = nm.nameres().address(r[1]) 683 self.node_tree_model.removeConfigNodes(r) 684 service = self.__master_info.getService(roslib.names.ns_join(r[0], 'list_nodes')) 685 if r[2] == self.masteruri: 686 self.remove_config_signal.emit(r[0]) 687 del self.__configs[r] 688 if len(self.__configs) == 0: 689 address = nm.nameres().address(master_info.masteruri) 690 tooltip = self.node_tree_model.updateHostDescription(master_info.masteruri, address, '', '', '') 691 self.host_description_updated.emit(master_info.masteruri, address, tooltip) 692 # request the nodes of new default configurations 693 added = list(set(default_cfgs) - set(self.__configs.keys())) 694 for (name, uri, muri) in added: 695 self.default_cfg_handler.requestNodeList(uri, roslib.names.ns_join(name, 'list_nodes')) 696 #request the description 697 descr_service = self.__master_info.getService(roslib.names.ns_join(name, 'description')) 698 if not descr_service is None: 699 self.default_cfg_handler.requestDescriptionList(descr_service.uri, descr_service.name) 700 self.updateButtons()
701
702 - def on_default_cfg_nodes_retrieved(self, service_uri, config_name, nodes):
703 ''' 704 Handles the new list with nodes from default configuration service. 705 @param service_uri: the URI of the service provided the default configuration 706 @type service_uri: C{str} 707 @param config_name: the name of default configuration service 708 @type config_name: C{str} 709 @param nodes: the name of the nodes with name spaces 710 @type nodes: C{[str]} 711 ''' 712 # remove the current state 713 masteruri = self.masteruri 714 if not self.__master_info is None: 715 service = self.__master_info.getService(config_name) 716 if not service is None: 717 masteruri = service.masteruri 718 key = (roslib.names.namespace(config_name).rstrip(roslib.names.SEP), service_uri, masteruri) 719 # if self.__configs.has_key(key): 720 # self.node_tree_model.removeConfigNodes(key) 721 # add the new config 722 node_cfgs = dict() 723 for n in nodes: 724 node_cfgs[n] = key 725 host = nm.nameres().getHostname(service_uri) 726 host_addr = nm.nameres().address(host) 727 if host_addr is None: 728 host_addr = host 729 self.node_tree_model.appendConfigNodes(masteruri, host_addr, node_cfgs) 730 self.__configs[key] = nodes 731 self.updateButtons()
732
733 - def on_default_cfg_descr_retrieved(self, service_uri, config_name, items):
734 ''' 735 Handles the description list from default configuration service. 736 Emits a Qt signal L{host_description_updated} to notify about a new host 737 description and a Qt signal L{capabilities_update} to notify about a capabilities 738 update. 739 @param service_uri: the URI of the service provided the default configuration 740 @type service_uri: C{str} 741 @param config_name: the name of default configuration service 742 @type config_name: C{str} 743 @param items: list with descriptions 744 @type items: C{[L{default_cfg_fkie.Description}]} 745 ''' 746 if items: 747 masteruri = self.masteruri 748 if not self.__master_info is None: 749 service = self.__master_info.getService(config_name) 750 if not service is None: 751 masteruri = service.masteruri 752 key = (roslib.names.namespace(config_name).rstrip(roslib.names.SEP), service_uri, masteruri) 753 host = nm.nameres().getHostname(service_uri) 754 host_addr = nm.nameres().address(host) 755 #add capabilities 756 caps = dict() 757 for c in items[0].capabilities: 758 if not caps.has_key(c.namespace): 759 caps[c.namespace] = dict() 760 caps[c.namespace][c.name.decode(sys.getfilesystemencoding())] = { 'type' : c.type, 'images' : c.images, 'description' : c.description.replace("\\n ", "\n").decode(sys.getfilesystemencoding()), 'nodes' : list(c.nodes) } 761 if host_addr is None: 762 host_addr = nm.nameres().getHostname(key[1]) 763 self.node_tree_model.addCapabilities(masteruri, host_addr, key, caps) 764 # set host description 765 tooltip = self.node_tree_model.updateHostDescription(masteruri, host_addr, items[0].robot_type, items[0].robot_name.decode(sys.getfilesystemencoding()), items[0].robot_descr.decode(sys.getfilesystemencoding())) 766 self.host_description_updated.emit(masteruri, host_addr, tooltip) 767 self.capabilities_update_signal.emit(masteruri, host_addr, roslib.names.namespace(config_name).rstrip(roslib.names.SEP), items)
768
769 - def on_default_cfg_err(self, service_uri, service, msg):
770 ''' 771 Handles the error messages from default configuration service. 772 @param service_uri: the URI of the service provided the default configuration 773 @type service_uri: C{str} 774 @param service: the name of default configuration service 775 @type service: C{str} 776 @param msg: the error message 777 @type msg: C{str} 778 ''' 779 pass
780 # QtGui.QMessageBox.warning(self, 'Error while call %s'%service, 781 # str(msg), 782 # QtGui.QMessageBox.Ok) 783
784 - def on_configfile_changed(self, changed, config):
785 ''' 786 Signal hander to handle the changes of a loaded configuration file 787 @param changed: the changed file 788 @type changed: C{str} 789 @param config: the main configuration file to load 790 @type config: C{str} 791 ''' 792 name = self.masteruri 793 if not self.__master_info is None: 794 name = self.__master_info.mastername 795 if not config in self.__in_question: 796 self.__in_question.append(config) 797 ret = QtGui.QMessageBox.question(self, ''.join([name, ' - configuration update']), 798 ' '.join(['The configuration file', changed, 'was changed!\n\nReload the configuration', os.path.basename(config), 'for', name,'?']), 799 QtGui.QMessageBox.No | QtGui.QMessageBox.Yes, QtGui.QMessageBox.Yes) 800 self.__in_question.remove(config) 801 if ret == QtGui.QMessageBox.Yes: 802 self.launchfiles = config
803 804 805 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 806 #%%%%%%%%%%%%% Handling of the view activities %%%%%%%% 807 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 808
809 - def on_node_activated(self, index):
810 ''' 811 Depending of the state of the node, it will be run or the screen output will 812 be open. 813 @param index: The index of the activated node 814 @type index: L{PySide.QtCore.QModelIndex} 815 ''' 816 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes(), False) 817 if not selectedNodes: 818 return 819 has_running = False 820 has_stopped = False 821 has_invalid = False 822 for node in selectedNodes: 823 if not node.uri is None: 824 has_running = True 825 if node.pid is None: 826 has_invalid = True 827 else: 828 has_stopped = True 829 830 if has_stopped: 831 self.on_start_clicked() 832 elif has_running and not has_invalid: 833 self.on_io_clicked() 834 else: 835 self.on_log_clicked()
836
837 - def on_topic_activated(self, index):
838 ''' 839 @param index: The index of the activated topic 840 @type index: L{PySide.QtCore.QModelIndex} 841 ''' 842 self.on_topic_echo_clicked()
843
844 - def on_service_activated(self, index):
845 ''' 846 @param index: The index of the activated service 847 @type index: L{PySide.QtCore.QModelIndex} 848 ''' 849 self.on_service_call_clicked()
850
851 - def on_host_inserted(self, item):
852 if item.id == (self.masteruri, nm.nameres().getHostname(self.masteruri)): 853 index = self.node_tree_model.indexFromItem(item) 854 if index.isValid(): 855 self.masterTab.nodeTreeView.expand(index)
856 # self.masterTab.nodeTreeView.expandAll() 857
858 - def on_node_collapsed(self, index):
859 if not index.parent ().isValid(): 860 self.masterTab.nodeTreeView.selectionModel().clear()
861
862 - def on_node_expanded(self, index):
863 pass
864
865 - def _create_html_list(self, title, items):
866 result = '' 867 if items: 868 result = ''.join([result, '<b><u>', title,'</u></b>']) 869 if len(items) > 1: 870 result = ''.join([result, ' [', str(len(items)),']']) 871 result = ''.join([result, '<ul>']) 872 for i in items: 873 result = ''.join([result, '<li>', i, '</li>']) 874 result = ''.join([result, '</ul>']) 875 return result
876
877 - def on_node_selection_changed(self, selected, deselected):
878 ''' 879 updates the Buttons, create a description and emit L{description_signal} to 880 show the description of host, group or node. 881 ''' 882 if self.masterTab.tabWidget.tabText(self.masterTab.tabWidget.currentIndex()) != 'Nodes': 883 return 884 selectedHosts = self.hostsFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes()) 885 name = '' 886 text = '' 887 if len(selectedHosts) == 1: 888 host = selectedHosts[0] 889 name = ' - '.join([host.name, 'Robot']) 890 text = host.generateDescription() 891 else: 892 selectedGroups = self.groupsFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes()) 893 if len(selectedGroups) == 1: 894 group = selectedGroups[0] 895 name = ' - '.join([group.name, 'Group']) 896 text = group.generateDescription() 897 else: 898 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes()) 899 if len(selectedNodes) == 1: 900 # create description for a node 901 node = selectedNodes[0] 902 text = ''.join(['<h3>', node.name,'</h3>']) 903 text = ''.join([text, '<dl>']) 904 text = ''.join([text, '<dt><b>URI</b>: ', str(node.node_info.uri), '</dt>']) 905 text = ''.join([text, '<dt><b>PID</b>: ', str(node.node_info.pid), '</dt>']) 906 text = ''.join([text, '<dt><b>ORG.MASTERURI</b>: ', str(node.node_info.masteruri), '</dt>']) 907 text = ''.join([text, '</dl>']) 908 text = ''.join([text, self._create_html_list('Published Topics:', node.published)]) 909 text = ''.join([text, self._create_html_list('Subscribed Topics:', node.subscribed)]) 910 text = ''.join([text, self._create_html_list('Services:', node.services)]) 911 launches = [] 912 default_cfgs = [] 913 for c in node.cfgs: 914 if isinstance(c, tuple): 915 default_cfgs.append(c[0]) 916 else: 917 launches.append(c) 918 text = ''.join([text, self._create_html_list('Loaded Launch Files:', launches)]) 919 text = ''.join([text, self._create_html_list('Default Configurations:', default_cfgs)]) 920 text = ''.join(['<div>', text, '</div>']) 921 name = node.name 922 923 if self.__last_info_text != text: 924 self.__last_info_text = text 925 self.description_signal.emit(name, text) 926 self.updateButtons()
927
928 - def on_topic_selection_changed(self, selected, deselected):
929 ''' 930 updates the Buttons, create a description and emit L{description_signal} to 931 show the description of selected topic 932 ''' 933 if self.masterTab.tabWidget.tabText(self.masterTab.tabWidget.currentIndex()) != 'Topics': 934 return 935 selectedTopics = self.topicsFromIndexes(self.masterTab.topicsView.selectionModel().selectedIndexes()) 936 topics_selected = (len(selectedTopics) > 0) 937 self.masterTab.echoTopicButton.setEnabled(topics_selected) 938 self.masterTab.hzTopicButton.setEnabled(topics_selected) 939 self.masterTab.pubStopTopicButton.setEnabled(topics_selected) 940 if len(selectedTopics) == 1: 941 topic = selectedTopics[0] 942 text = ''.join(['<h3>', topic.name,'</h3>']) 943 text = ''.join([text, self._create_html_list('Publisher:', topic.publisherNodes)]) 944 text = ''.join([text, self._create_html_list('Subscriber:', topic.subscriberNodes)]) 945 text = ''.join([text, '<b><u>Type:</u></b> ', str(self._href_from_msgtype(topic.type))]) 946 text = ''.join([text, '<dl>']) 947 try: 948 mclass = roslib.message.get_message_class(topic.type) 949 if not mclass is None: 950 text = ''.join([text, '<ul>']) 951 for f in mclass.__slots__: 952 idx = mclass.__slots__.index(f) 953 idtype = mclass._slot_types[idx] 954 base_type = roslib.msgs.base_msg_type(idtype) 955 # primitive = "unknown" 956 # if base_type in roslib.msgs.PRIMITIVE_TYPES: 957 # primitive = "primitive" 958 # else: 959 # try: 960 # primitive =roslib.message.get_message_class(base_type) 961 ## primitive = "class", list_msg_class.__slots__ 962 # except ValueError: 963 # pass 964 text = ''.join([text, '<li>', str(f), ': ', str(idtype), '</li>']) 965 text = ''.join([text, '</ul>']) 966 except ValueError: 967 pass 968 text = ''.join([text, '</dl>']) 969 info_text = ''.join(['<div>', text, '</div>']) 970 if self.__last_info_text != info_text: 971 self.__last_info_text = info_text 972 self.description_signal.emit(topic.name, info_text)
973
974 - def _href_from_msgtype(self, type):
975 result = type 976 if type: 977 result = ''.join(['<a href="http://ros.org/doc/api/', type.replace('/', '/html/msg/'), '.html">', type, '</a>']) 978 return result
979
980 - def on_service_selection_changed(self, selected, deselected):
981 ''' 982 updates the Buttons, create a description and emit L{description_signal} to 983 show the description of selected service 984 ''' 985 if self.masterTab.tabWidget.tabText(self.masterTab.tabWidget.currentIndex()) != 'Services': 986 return 987 selectedServices = self.servicesFromIndexes(self.masterTab.servicesView.selectionModel().selectedIndexes()) 988 self.masterTab.callServiceButton.setEnabled(len(selectedServices) > 0) 989 if len(selectedServices) == 1: 990 service = selectedServices[0] 991 text = ''.join(['<h3>', service.name,'</h3>']) 992 text = ''.join([text, '<dl><dt><b>URI</b>: ', str(service.uri), '</dt></dl>']) 993 try: 994 service_class = service.get_service_class(nm.is_local(nm.nameres().getHostname(service.uri))) 995 text = ''.join([text, '<h4>', self._href_from_svrtype(service_class._type), '</h4>']) 996 text = ''.join([text, '<b><u>', 'Request', ':</u></b>']) 997 text = ''.join([text, '<dl><dt>', str(service_class._request_class.__slots__), '</dt></dl>']) 998 999 text = ''.join([text, '<b><u>', 'Response', ':</u></b>']) 1000 text = ''.join([text, '<dl><dt>', str(service_class._response_class.__slots__), '</dt></dl>']) 1001 except: 1002 pass 1003 info_text = ''.join(['<div>', text, '</div>']) 1004 if self.__last_info_text != info_text: 1005 self.__last_info_text = info_text 1006 self.description_signal.emit(service.name, info_text)
1007
1008 - def _href_from_svrtype(self, type):
1009 result = type 1010 if type: 1011 result = ''.join(['<a href="http://ros.org/doc/api/', type.replace('/', '/html/srv/'), '.html">', type, '</a>']) 1012 return result
1013
1014 - def on_parameter_selection_changed(self, selected, deselected):
1015 selectedParameter = self.parameterFromIndexes(self.masterTab.parameterView.selectionModel().selectedIndexes()) 1016 self.masterTab.deleteParameterButton.setEnabled(len(selectedParameter) > 0)
1017
1018 - def hostsFromIndexes(self, indexes, recursive=True):
1019 result = [] 1020 for index in indexes: 1021 if index.column() == 0: 1022 item = self.node_tree_model.itemFromIndex(index) 1023 if not item is None: 1024 if isinstance(item, HostItem): 1025 result.append(item) 1026 return result
1027
1028 - def groupsFromIndexes(self, indexes, recursive=True):
1029 result = [] 1030 for index in indexes: 1031 if index.column() == 0 and index.parent().isValid(): 1032 item = self.node_tree_model.itemFromIndex(index) 1033 if not item is None: 1034 if isinstance(item, GroupItem): 1035 result.append(item) 1036 return result
1037
1038 - def nodesFromIndexes(self, indexes, recursive=True):
1039 result = [] 1040 for index in indexes: 1041 if index.column() == 0: 1042 item = self.node_tree_model.itemFromIndex(index) 1043 res = self._nodesFromItems(item, recursive) 1044 for r in res: 1045 if not r in result: 1046 result.append(r) 1047 return result
1048
1049 - def _nodesFromItems(self, item, recursive):
1050 result = [] 1051 if not item is None: 1052 if isinstance(item, (GroupItem, HostItem)): 1053 if recursive: 1054 for j in range(item.rowCount()): 1055 result[len(result):] = self._nodesFromItems(item.child(j), recursive) 1056 elif isinstance(item, NodeItem): 1057 if not item in result: 1058 result.append(item) 1059 return result
1060
1061 - def topicsFromIndexes(self, indexes):
1062 result = [] 1063 for index in indexes: 1064 model_index = self.topic_proxyModel.mapToSource(index) 1065 item = self.topic_model.itemFromIndex(model_index) 1066 if not item is None and isinstance(item, TopicItem): 1067 result.append(item.topic) 1068 return result
1069
1070 - def servicesFromIndexes(self, indexes):
1071 result = [] 1072 for index in indexes: 1073 model_index = self.service_proxyModel.mapToSource(index) 1074 item = self.service_model.itemFromIndex(model_index) 1075 if not item is None and isinstance(item, ServiceItem): 1076 result.append(item.service) 1077 return result
1078
1079 - def parameterFromIndexes(self, indexes):
1080 result = [] 1081 for index in indexes: 1082 model_index = self.parameter_proxyModel.mapToSource(index) 1083 item = self.parameter_model.itemFromIndex(model_index) 1084 if not item is None and isinstance(item, ParameterValueItem): 1085 result.append((item.name, item.value)) 1086 return result
1087 1088 1089 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1090 #%%%%%%%%%%%%% Handling of the button activities %%%%%%%% 1091 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1092
1093 - def on_start_clicked(self):
1094 ''' 1095 Starts the selected nodes. If for a node more then one configuration is 1096 available, the selection dialog will be show. 1097 ''' 1098 key_mod = QtGui.QApplication.keyboardModifiers() 1099 if (key_mod & QtCore.Qt.ShiftModifier or key_mod & QtCore.Qt.ControlModifier): 1100 self.masterTab.startButton.showMenu() 1101 else: 1102 cursor = self.cursor() 1103 self.masterTab.startButton.setEnabled(False) 1104 self.setCursor(QtCore.Qt.WaitCursor) 1105 try: 1106 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes()) 1107 self.start_nodes(selectedNodes) 1108 finally: 1109 self.setCursor(cursor) 1110 self.masterTab.startButton.setEnabled(True)
1111
1112 - def start_node(self, node, force, config, force_host=None):
1113 1114 if node is None: 1115 raise DetailedError("Start error", 'None is not valid node name!') 1116 if node.pid is None or force: 1117 # start the node using launch configuration 1118 if config is None: 1119 raise DetailedError("Start error", 1120 ''.join(['Error while start ', node.name, ':\nNo configuration found!'])) 1121 if isinstance(config, LaunchConfig): 1122 try: 1123 nm.starter().runNode(node.name, config, force_host, self.masteruri) 1124 except socket.error as se: 1125 rospy.logwarn("Error while start '%s': %s\n\n Start canceled!", node.name, str(se)) 1126 raise DetailedError("Start error", 1127 ''.join(['Error while start ', node.name, '\n\nStart canceled!']), 1128 str(se)) 1129 return False 1130 except (Exception, nm.StartException) as e: 1131 print type(e) 1132 import traceback 1133 print traceback.format_exc() 1134 rospy.logwarn("Error while start '%s': %s", node.name, str(e)) 1135 raise DetailedError("Start error", ''.join(['Error while start ', node.name]), str(e)) 1136 elif isinstance(config, (str, unicode)): 1137 # start with default configuration 1138 from default_cfg_fkie.srv import Task 1139 try: 1140 nm.starter().callService(self.master_info.getService(config).uri, config, Task, [node.name]) 1141 except (Exception, nm.StartException) as e: 1142 socket_error = (str(e).find("timeout") or str(e).find("113")) 1143 rospy.logwarn("Error while call a service of node '%s': %s", node.name, str(e)) 1144 raise DetailedError("Service error", 1145 ''.join(['Error while call a service of node ', node.name, '[', self.master_info.getService(config).uri, ']']), 1146 str(e))
1147
1148 - def start_nodes(self, nodes, force=False, force_host=None):
1149 ''' 1150 Internal method to start a list with nodes 1151 @param nodes: the list with nodes to start 1152 @type nodes: C{[L{NodeItem}, ...]} 1153 @param force: force the start of the node, also if it is already started. 1154 @type force: C{bool} 1155 @param force_host: force the start of the node at specified host. 1156 @type force_host: C{str} 1157 ''' 1158 cfg_choices = dict() 1159 cfg_nodes = dict() 1160 for node in nodes: 1161 if node.pid is None or (not node.pid is None and force): 1162 # test for duplicate nodes 1163 if self.node_tree_model.isDuplicateNode(node.name): 1164 ret = QtGui.QMessageBox.question(self, 'Question', ''.join(['Some nodes, e.g. ', node.name, ' are already running on another host. If you start this node the other node will be terminated.\n Do you want proceed?']), QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) 1165 if ret == QtGui.QMessageBox.No: 1166 return 1167 # determine the used configuration 1168 choices = self._getCfgChoises(node) 1169 ch_keys = choices.keys() 1170 if ch_keys: 1171 ch_keys.sort() 1172 choises_str = str(ch_keys) 1173 if not choises_str in cfg_choices.keys(): 1174 choice = self._getUserCfgChoice(choices, node.name) 1175 if not choice is None: 1176 cfg_choices[choises_str] = choices[choice] 1177 cfg_nodes[node.name] = choices[choice] 1178 else: 1179 res = WarningMessageBox(QtGui.QMessageBox.Warning, "Start error", 1180 ''.join(['Error while start ', node.name, ':\nNo configuration selected!'])).exec_() 1181 else: 1182 cfg_nodes[node.name] = cfg_choices[choises_str] 1183 1184 # put into the queue and start 1185 for node in nodes: 1186 if node.name in cfg_nodes: 1187 self._progress_queue.add2queue(str(self._progress_queue.count()), 1188 'Start nodes', 1189 self.start_node, 1190 (node.node_info, force, cfg_nodes[node.node_info.name], force_host)) 1191 self._progress_queue.start()
1192
1193 - def start_nodes_by_name(self, nodes, cfg):
1194 ''' 1195 Start nodes given in a list by their names. 1196 @param nodes: a list with full node names 1197 @type nodes: C{[str]} 1198 ''' 1199 result = [] 1200 if not self.master_info is None: 1201 for n in nodes: 1202 node_info = NodeInfo(n, self.masteruri) 1203 node_item = NodeItem(node_info) 1204 node_item.addConfig(cfg) 1205 result.append(node_item) 1206 self.start_nodes(result)
1207
1208 - def on_force_start_nodes(self):
1209 ''' 1210 Starts the selected nodes (also if it already running). If for a node more then one configuration is 1211 available, the selection dialog will be show. 1212 ''' 1213 cursor = self.cursor() 1214 self.masterTab.startButton.setEnabled(False) 1215 self.setCursor(QtCore.Qt.WaitCursor) 1216 try: 1217 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes()) 1218 self.start_nodes(selectedNodes, True) 1219 finally: 1220 self.setCursor(cursor) 1221 self.masterTab.startButton.setEnabled(True)
1222
1223 - def on_start_nodes_at_host(self):
1224 ''' 1225 Starts the selected nodes on an another host. 1226 ''' 1227 cursor = self.cursor() 1228 self.masterTab.startButton.setEnabled(False) 1229 params = {'Host' : ('string', 'localhost') } 1230 dia = ParameterDialog(params) 1231 dia.setFilterVisible(False) 1232 dia.setWindowTitle('Start node on...') 1233 dia.resize(350,120) 1234 dia.setFocusField('host') 1235 progressDialog = None 1236 if dia.exec_(): 1237 try: 1238 params = dia.getKeywords() 1239 host = params['Host'] 1240 self.setCursor(QtCore.Qt.WaitCursor) 1241 try: 1242 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes()) 1243 self.start_nodes(selectedNodes, True, host) 1244 finally: 1245 self.setCursor(cursor) 1246 except Exception, e: 1247 WarningMessageBox(QtGui.QMessageBox.Warning, "Start error", 1248 'Error while parse parameter', 1249 str(e)).exec_() 1250 self.masterTab.startButton.setEnabled(True)
1251
1252 - def _getDefaultCfgChoises(self, node):
1253 result = {} 1254 for c in node.cfgs: 1255 if isinstance(c, tuple): 1256 result[' '.join(['[default]', c[0]])] = roslib.names.ns_join(c[0], 'run') 1257 return result
1258
1259 - def _getCfgChoises(self, node, ignore_defaults=False):
1260 result = {} 1261 for c in node.cfgs: 1262 if not isinstance(c, tuple): 1263 launch = self.launchfiles[c] 1264 result[''.join([str(launch.LaunchName), ' [', str(launch.PackageName), ']'])] = self.launchfiles[c] 1265 elif not ignore_defaults: 1266 result[' '.join(['[default]', c[0]])] = roslib.names.ns_join(c[0], 'run') 1267 return result
1268
1269 - def _getUserCfgChoice(self, choices, nodename):
1270 value = None 1271 # Open selection 1272 if len(choices) == 1: 1273 value = choices.keys()[0] 1274 elif len(choices) > 0: 1275 items = SelectDialog.getValue('Configuration selection', choices.keys(), True) 1276 if items: 1277 value = items[0] 1278 return value
1279
1280 - def on_stop_clicked(self):
1281 ''' 1282 Stops the selected and running nodes. If the node can't be stopped using his 1283 RPC interface, it will be unregistered from the ROS master using the masters 1284 RPC interface. 1285 ''' 1286 key_mod = QtGui.QApplication.keyboardModifiers() 1287 if (key_mod & QtCore.Qt.ShiftModifier or key_mod & QtCore.Qt.ControlModifier): 1288 self.masterTab.stopButton.showMenu() 1289 else: 1290 cursor = self.cursor() 1291 self.setCursor(QtCore.Qt.WaitCursor) 1292 try: 1293 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes()) 1294 self.stop_nodes(selectedNodes) 1295 finally: 1296 self.setCursor(cursor)
1297
1298 - def stop_node(self, node, force=False):
1299 if not node is None and not node.uri is None and (not self._is_in_ignore_list(node.name) or force): 1300 try: 1301 #'print "STOP set timeout", node 1302 socket.setdefaulttimeout(10) 1303 #'print "STOP create xmlrpc", node 1304 p = xmlrpclib.ServerProxy(node.uri) 1305 #'print "STOP send stop", node 1306 p.shutdown(rospy.get_name(), ''.join(['[node manager] request from ', self.mastername])) 1307 #'print "STOP stop finished", node 1308 except Exception, e: 1309 # import traceback 1310 # formatted_lines = traceback.format_exc().splitlines() 1311 rospy.logwarn("Error while stop node '%s': %s", str(node.name), str(e)) 1312 if str(e).find(' 111') == 1: 1313 # self.masterTab.stopButton.setEnabled(False) 1314 raise DetailedError("Stop error", 1315 ''.join(['Error while stop node ', node.name]), 1316 str(e)) 1317 finally: 1318 socket.setdefaulttimeout(None) 1319 return True
1320
1321 - def stop_nodes(self, nodes):
1322 ''' 1323 Internal method to stop a list with nodes 1324 @param nodes: the list with nodes to stop 1325 @type nodes: C{[L{master_discovery_fkie.NodeInfo}, ...]} 1326 ''' 1327 # put into the queue and start the que handling 1328 for node in nodes: 1329 self._progress_queue.add2queue(str(self._progress_queue.count()), 1330 'Stop nodes', 1331 self.stop_node, 1332 (node, (len(nodes)==1))) 1333 self._progress_queue.start()
1334
1335 - def stop_nodes_by_name(self, nodes):
1336 ''' 1337 Stop nodes given in a list by their names. 1338 @param nodes: a list with full node names 1339 @type nodes: C{[str]} 1340 ''' 1341 result = [] 1342 if not self.master_info is None: 1343 for n in nodes: 1344 node = self.master_info.getNode(n) 1345 if not node is None: 1346 result.append(node) 1347 self.stop_nodes(result)
1348
1349 - def kill_node(self, node, force=False):
1350 if not node is None and not node.uri is None and (not self._is_in_ignore_list(node.name) or force): 1351 pid = node.pid 1352 if pid is None: 1353 # try to get the process id of the node 1354 try: 1355 socket.setdefaulttimeout(10) 1356 rpc_node = xmlrpclib.ServerProxy(node.uri) 1357 code, msg, pid = rpc_node.getPid(rospy.get_name()) 1358 except: 1359 # self.masterTab.stopButton.setEnabled(False) 1360 pass 1361 finally: 1362 socket.setdefaulttimeout(None) 1363 # kill the node 1364 if not pid is None: 1365 try: 1366 nm.starter().kill(self.getHostFromNode(node), pid, False) 1367 except Exception as e: 1368 rospy.logwarn("Error while kill the node %s: %s", str(node.name), str(e)) 1369 raise DetailedError("Kill error", 1370 ''.join(['Error while kill the node ', node.name]), 1371 str(e)) 1372 return True
1373 1374
1375 - def on_kill_nodes(self):
1376 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes()) 1377 1378 # put into the queue and start the que handling 1379 for node in selectedNodes: 1380 self._progress_queue.add2queue(str(self._progress_queue.count()), 1381 'Kill nodes', 1382 self.kill_node, 1383 (node, (len(selectedNodes)==1))) 1384 self._progress_queue.start()
1385
1386 - def unregister_node(self, node, force=False):
1387 if not node is None and not node.uri is None and (not self._is_in_ignore_list(node.name) or force): 1388 # stop the node? 1389 # try: 1390 # p = xmlrpclib.ServerProxy(node.uri) 1391 # p.shutdown(rospy.get_name(), ''.join(['[node manager] request from ', self.hostname])) 1392 # except Exception, e: 1393 # rospy.logwarn("Error while stop node '%s': %s", str(node.name), str(e)) 1394 # self.masterTab.stopButton.setEnabled(False) 1395 # unregister all entries of the node from ROS master 1396 try: 1397 socket.setdefaulttimeout(10) 1398 master = xmlrpclib.ServerProxy(node.masteruri) 1399 master_multi = xmlrpclib.MultiCall(master) 1400 # master_multi.deleteParam(node.name, node.name) 1401 for p in node.published: 1402 rospy.loginfo("unregister publisher '%s' [%s] from ROS master: %s", p, node.name, node.masteruri) 1403 master_multi.unregisterPublisher(node.name, p, node.uri) 1404 for s in node.subscribed: 1405 rospy.loginfo("unregister subscriber '%s' [%s] from ROS master: %s", p, node.name, node.masteruri) 1406 master_multi.unregisterSubscriber(node.name, s, node.uri) 1407 if not self.master_state is None: 1408 for s in node.services: 1409 rospy.loginfo("unregister service '%s' [%s] from ROS master: %s", p, node.name, node.masteruri) 1410 service = self.master_info.getService(s) 1411 if not (service is None): 1412 master_multi.unregisterService(node.name, s, service.uri) 1413 r = master_multi() 1414 for code, msg, _ in r: 1415 if code != 1: 1416 rospy.logwarn("unregistration failed: %s", msg) 1417 except Exception, e: 1418 rospy.logwarn("Error while unregister node %s: %s", str(node.name), str(e)) 1419 raise DetailedError("Unregister error", 1420 ''.join(['Error while Unregister node ', node.name]), 1421 str(e)) 1422 finally: 1423 socket.setdefaulttimeout(None) 1424 return True
1425
1426 - def on_unregister_nodes(self):
1427 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes()) 1428 # put into the queue and start the que handling 1429 for node in selectedNodes: 1430 self._progress_queue.add2queue(str(self._progress_queue.count()), 1431 'Unregister nodes', 1432 self.unregister_node, 1433 (node, (len(selectedNodes)==1))) 1434 self._progress_queue.start()
1435
1436 - def on_stop_context_toggled(self, state):
1437 menu = QtGui.QMenu(self) 1438 self.killAct = QtGui.QAction("&Kill Node", self, shortcut=QtGui.QKeySequence.New, statusTip="Kill selected node", triggered=self.kill_nodes) 1439 self.unregAct = QtGui.QAction("&Unregister Nodes...", self, shortcut=QtGui.QKeySequence.Open, statusTip="Open an existing file", triggered=self.unreg_nodes) 1440 menu.addAction(self.killAct) 1441 menu.addAction(self.unregAct) 1442 menu.exec_(self.masterTab.stopContextButton.pos())
1443 1444
1445 - def getHostFromNode(self, node):
1446 ''' 1447 If the node is running the host the node URI will be returned. Otherwise 1448 tries to get the host from the launch configuration. If the configuration 1449 contains no machine assignment for this node the host of the ROS master URI 1450 will be used. 1451 @param node: 1452 @type node: L{master_discovery_fkie.NodeInfo} 1453 ''' 1454 if not node.uri is None: 1455 return nm.nameres().getHostname(node.uri) 1456 # try to get it from the configuration 1457 for c in node.cfgs: 1458 if not isinstance(c, tuple): 1459 launch_config = self.__configs[c] 1460 item = launch_config.getNode(node.name) 1461 if not item is None and item.machine_name and not item.machine_name == 'localhost': 1462 return launch_config.Roscfg.machines[item.machine_name].address 1463 # return the host of the assigned ROS master 1464 return nm.nameres().getHostname(node.masteruri)
1465
1466 - def on_io_clicked(self):
1467 ''' 1468 Shows IO of the selected nodes. 1469 ''' 1470 key_mod = QtGui.QApplication.keyboardModifiers() 1471 if (key_mod & QtCore.Qt.ShiftModifier or key_mod & QtCore.Qt.ControlModifier): 1472 self.masterTab.ioButton.showMenu() 1473 else: 1474 cursor = self.cursor() 1475 self.setCursor(QtCore.Qt.WaitCursor) 1476 try: 1477 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes()) 1478 if selectedNodes: 1479 ret = True 1480 if len(selectedNodes) > 5: 1481 ret = QtGui.QMessageBox.question(self, "Show IO", "You are going to open the IO of "+ str(len(selectedNodes)) + " nodes at once\nContinue?", QtGui.QMessageBox.Ok, QtGui.QMessageBox.Cancel) 1482 ret = (ret == QtGui.QMessageBox.Ok) 1483 if ret: 1484 for node in selectedNodes: 1485 self._progress_queue.add2queue(str(self._progress_queue.count()), 1486 'Show IO', 1487 nm.screen().openScreen, 1488 (node.name, self.getHostFromNode(node), False)) 1489 self._progress_queue.start() 1490 else: 1491 self.on_show_all_screens() 1492 finally: 1493 self.setCursor(cursor)
1494
1495 - def on_kill_screens(self):
1496 ''' 1497 Kills selected screens, if some available. 1498 ''' 1499 cursor = self.cursor() 1500 self.setCursor(QtCore.Qt.WaitCursor) 1501 try: 1502 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes()) 1503 for node in selectedNodes: 1504 self._progress_queue.add2queue(str(self._progress_queue.count()), 1505 'Kill screen', 1506 nm.screen().killScreens, 1507 (node.name, self.getHostFromNode(node), False)) 1508 self._progress_queue.start() 1509 finally: 1510 self.setCursor(cursor)
1511
1512 - def on_show_all_screens(self):
1513 ''' 1514 Shows all available screens. 1515 ''' 1516 cursor = self.cursor() 1517 self.setCursor(QtCore.Qt.WaitCursor) 1518 try: 1519 host = nm.nameres().getHostname(self.masteruri) 1520 sel_screen = [] 1521 try: 1522 screens = nm.screen().getActiveScreens(host, auto_pw_request=True) 1523 sel_screen = SelectDialog.getValue('Open screen', screens, False, self) 1524 except Exception, e: 1525 rospy.logwarn("Error while get screen list: %s", str(e)) 1526 WarningMessageBox(QtGui.QMessageBox.Warning, "Screen list error", 1527 ''.join(['Error while get screen list from ', host]), 1528 str(e)).exec_() 1529 for screen in sel_screen: 1530 try: 1531 if not nm.screen().openScreenTerminal(host, screen, screen): 1532 # self.masterTab.ioButton.setEnabled(False) 1533 pass 1534 except Exception, e: 1535 rospy.logwarn("Error while show IO for %s: %s", str(screen), str(e)) 1536 WarningMessageBox(QtGui.QMessageBox.Warning, "Show IO error", 1537 ''.join(['Error while show IO ', screen, ' on ', host]), 1538 str(e)).exec_() 1539 finally: 1540 self.setCursor(cursor)
1541 1542
1543 - def on_log_clicked(self):
1544 ''' 1545 Shows log files of the selected nodes. 1546 ''' 1547 try: 1548 key_mod = QtGui.QApplication.keyboardModifiers() 1549 if (key_mod & QtCore.Qt.ShiftModifier or key_mod & QtCore.Qt.ControlModifier): 1550 self.masterTab.logButton.showMenu() 1551 else: 1552 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes()) 1553 ret = True 1554 if len(selectedNodes) > 5: 1555 ret = QtGui.QMessageBox.question(self, "Show Log", "You are going to open the logs of "+ str(len(selectedNodes)) + " nodes at once\nContinue?", QtGui.QMessageBox.Ok, QtGui.QMessageBox.Cancel) 1556 ret = (ret == QtGui.QMessageBox.Ok) 1557 if ret: 1558 for node in selectedNodes: 1559 self._progress_queue.add2queue(str(self._progress_queue.count()), 1560 'Show log', 1561 nm.starter().openLog, 1562 (node.name, self.getHostFromNode(node))) 1563 self._progress_queue.start() 1564 except Exception, e: 1565 import traceback 1566 print traceback.format_exc() 1567 rospy.logwarn("Error while show log: %s", str(e)) 1568 WarningMessageBox(QtGui.QMessageBox.Warning, "Show log error", 1569 'Error while show Log', 1570 str(e)).exec_()
1571
1572 - def on_log_path_copy(self):
1573 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes()) 1574 nodenames = [] 1575 for n in selectedNodes: 1576 nodenames.append(n.name) 1577 try: 1578 host = nm.nameres().getHostname(self.masteruri) 1579 path_on_host = nm.starter().copylogPath2Clipboards(host, nodenames) 1580 user = nm.ssh().USER_DEFAULT 1581 try: 1582 user = nm.ssh().SSH_AUTH[host] 1583 except: 1584 pass 1585 QtGui.QApplication.clipboard().setText(''.join([user, '@', host, ':', path_on_host])) 1586 except Exception as e: 1587 WarningMessageBox(QtGui.QMessageBox.Warning, "Get log path", 1588 'Error while get log path', 1589 str(e)).exec_()
1590 # self._progress_queue.add2queue(str(self._progress_queue.count()), 1591 # 'Get log path', 1592 # nm.starter().copylogPath2Clipboards, 1593 # (nm.nameres().getHostname(self.masteruri), nodenames)) 1594 # self._progress_queue.start() 1595 1596 # def on_log_show_selected(self): 1597 # try: 1598 # nm.screen().LOG_PATH. 1599 # screens = nm.screen().getActiveScreens(host, auto_pw_request=True) 1600 # sel_screen = SelectDialog.getValue('Open log', screens, False, self) 1601 # except Exception, e: 1602 # rospy.logwarn("Error while get screen list: %s", str(e)) 1603 # WarningMessageBox(QtGui.QMessageBox.Warning, "Screen list error", 1604 # ''.join(['Error while get screen list from ', host]), 1605 # str(e)).exec_() 1606
1607 - def on_log_delete_clicked(self):
1608 ''' 1609 Deletes log files of the selected nodes. 1610 ''' 1611 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes()) 1612 for node in selectedNodes: 1613 self._progress_queue.add2queue(str(self._progress_queue.count()), 1614 'Delete Log', 1615 nm.starter().deleteLog, 1616 (node.name, self.getHostFromNode(node), False)) 1617 self._progress_queue.start()
1618
1619 - def on_dynamic_config_clicked(self):
1620 ''' 1621 Opens the dynamic configuration dialogs for selected nodes. 1622 ''' 1623 if not self.master_info is None: 1624 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes()) 1625 for n in selectedNodes: 1626 try: 1627 nodes = sorted([s[:-len('/set_parameters')] for s in self.master_info.services.keys() if (s.endswith('/set_parameters') and s.startswith(str(n.name)))]) 1628 node = None 1629 if len(nodes) == 1: 1630 node = nodes[0] 1631 elif len(nodes) > 1: 1632 items = SelectDialog.getValue('Dynamic configuration selection', [i for i in nodes], True) 1633 if items: 1634 node = items[0] 1635 if not node is None: 1636 # self.masterTab.dynamicConfigButton.setEnabled(False) 1637 import os, subprocess 1638 env = dict(os.environ) 1639 env["ROS_MASTER_URI"] = str(self.master_info.masteruri) 1640 ps = subprocess.Popen(['rosrun', 'dynamic_reconfigure', 'reconfigure_gui', node], env=env) 1641 # wait for process to avoid 'defunct' processes 1642 thread = threading.Thread(target=ps.wait) 1643 thread.setDaemon(True) 1644 thread.start() 1645 except Exception, e: 1646 rospy.logwarn("Start dynamic reconfiguration for '%s' failed: %s", str(node), str(e)) 1647 WarningMessageBox(QtGui.QMessageBox.Warning, "Start dynamic reconfiguration error", 1648 ''.join(['Start dynamic reconfiguration for ', str(node), ' failed!']), 1649 str(e)).exec_()
1650
1651 - def on_edit_config_clicked(self):
1652 ''' 1653 ''' 1654 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes()) 1655 for node in selectedNodes: 1656 choices = self._getCfgChoises(node, True) 1657 choice = self._getUserCfgChoice(choices, node.name) 1658 config = choices[choice] if choices and choice else '' 1659 if isinstance(config, LaunchConfig): 1660 # get the file, which include the node and the main configuration file 1661 node_cfg = config.getNode(node.name) 1662 files = [config.Filename] 1663 if not node_cfg.filename in files: 1664 files.append(node_cfg.filename) 1665 self.request_xml_editor.emit(files, ''.join(['name="', os.path.basename(node.name), '"']))
1666
1667 - def on_edit_rosparam_clicked(self):
1668 ''' 1669 ''' 1670 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes()) 1671 for node in selectedNodes: 1672 # set the parameter in the ROS parameter server 1673 try: 1674 inputDia = MasterParameterDialog(node.masteruri if not node.masteruri is None else self.masteruri, ''.join([node.name, roslib.names.SEP]), parent=self) 1675 inputDia.setWindowTitle(' - '.join([os.path.basename(node.name), "parameter"])) 1676 inputDia.show() 1677 except: 1678 import traceback 1679 rospy.logwarn("Error on retrieve parameter for %s: %s", str(node.name), str(traceback.format_exc()))
1680
1681 - def on_close_clicked(self):
1682 ''' 1683 Closes the open launch configurations. If more then one configuration is 1684 open a selection dialog will be open. 1685 ''' 1686 choices = dict() 1687 1688 for path, cfg in self.__configs.items(): 1689 if isinstance(path, tuple): 1690 if nm.nameres().getHostname(path[1]) == nm.nameres().getHostname(self.masteruri): 1691 choices[''.join(['[', path[0], ']'])] = path 1692 else: 1693 choices[''.join([os.path.basename(path), ' [', str(LaunchConfig.packageName(os.path.dirname(path))[0]), ']'])] = path 1694 cfgs = [] 1695 1696 # if len(choices) > 1: 1697 cfgs = SelectDialog.getValue('Close configurations', choices.keys(), False, self) 1698 # elif len(choices) == 1: 1699 # cfgs = choices.values()[0] 1700 1701 # close configurations 1702 for c in cfgs: 1703 self._close_cfg(choices[c]) 1704 self.updateButtons()
1705
1706 - def _close_cfg(self, cfg):
1707 try: 1708 self.removeConfigFromModel(cfg) 1709 if isinstance(cfg, tuple): 1710 if not self.master_info is None: 1711 # close default configuration: stop the default_cfg node 1712 node = self.master_info.getNode(cfg[0]) 1713 if not node is None: 1714 self.stop_nodes([node]) 1715 # else: 1716 # # remove machine entries 1717 # try: 1718 # for name, machine in self.__configs[cfg].Roscfg.machines.items(): 1719 # if machine.name: 1720 # nm.nameres().remove(name=machine.name, host=machine.address) 1721 # except: 1722 # import traceback 1723 # print traceback.format_exc() 1724 else: 1725 # remove from name resolution 1726 for name, machine in self.__configs[cfg].Roscfg.machines.items(): 1727 if machine.name: 1728 nm.nameres().removeInfo(machine.name, machine.address) 1729 del self.__configs[cfg] 1730 except: 1731 import traceback 1732 print traceback.format_exc() 1733 pass
1734
1735 - def on_topic_echo_clicked(self):
1736 ''' 1737 Shows the output of the topic in a terminal. 1738 ''' 1739 self._show_topic_output(False)
1740
1741 - def on_topic_hz_clicked(self):
1742 ''' 1743 Shows the hz of the topic in a terminal. 1744 ''' 1745 self._show_topic_output(True)
1746
1747 - def on_topic_pub_clicked(self):
1748 selectedTopics = self.topicsFromIndexes(self.masterTab.topicsView.selectionModel().selectedIndexes()) 1749 if len(selectedTopics) > 0: 1750 for topic in selectedTopics: 1751 if not self._start_publisher(topic.name, topic.type): 1752 break 1753 else: # create a new topic 1754 # fill the input fields 1755 # determine the list all available message types 1756 root_paths = [os.path.normpath(p) for p in os.getenv("ROS_PACKAGE_PATH").split(':')] 1757 packages = {} 1758 msg_types = [] 1759 for p in root_paths: 1760 ret = self._getPackages(p) 1761 packages = dict(ret.items() + packages.items()) 1762 for (p, direc) in packages.items(): 1763 import rosmsg 1764 for file in rosmsg._list_types('/'.join([direc, 'msg']), 'msg', rosmsg.MODE_MSG): 1765 msg_types.append("%s/%s"%(p, file)) 1766 msg_types.sort() 1767 fields = {'Type' : ('string', msg_types), 'Name' : ('string', [''])} 1768 1769 #create a dialog 1770 dia = ParameterDialog(fields, parent=self) 1771 dia.setWindowTitle('Publish to topic') 1772 dia.setFilterVisible(False) 1773 dia.resize(300, 95) 1774 dia.setFocusField('Name') 1775 if dia.exec_(): 1776 params = dia.getKeywords() 1777 try: 1778 if params['Name'] and params['Type']: 1779 try: 1780 self._start_publisher(params['Name'], params['Type']) 1781 except Exception, e: 1782 import traceback 1783 print traceback.format_exc() 1784 rospy.logwarn("Publish topic '%s' failed: %s", str(params['Name']), str(e)) 1785 WarningMessageBox(QtGui.QMessageBox.Warning, "Publish topic error", 1786 ''.join(['Publish topic ', params['Name'], ' failed!']), 1787 str(e)).exec_() 1788 else: 1789 WarningMessageBox(QtGui.QMessageBox.Warning, "Invalid name or type", 1790 ''.join(["Can't publish to topic '", params['Name'], "' with type '", params['Type'], "'!"])).exec_() 1791 except (KeyError, ValueError), e: 1792 WarningMessageBox(QtGui.QMessageBox.Warning, "Warning", 1793 'Error while add a parameter to the ROS parameter server', 1794 str(e)).exec_()
1795
1796 - def _start_publisher(self, topic_name, topic_type):
1797 try: 1798 mclass = roslib.message.get_message_class(topic_type) 1799 if mclass is None: 1800 WarningMessageBox(QtGui.QMessageBox.Warning, "Publish error", 1801 'Error while publish to %s'%topic_name, 1802 ''.join(['invalid message type: ', topic_type,'.\nIf this is a valid message type, perhaps you need to run "rosmake"'])).exec_() 1803 return 1804 slots = mclass.__slots__ 1805 types = mclass._slot_types 1806 args = ServiceDialog._params_from_slots(slots, types) 1807 p = { '! Publish rate' : ('string', ['latch', 'once', '1']), topic_type : ('dict', args) } 1808 dia = ParameterDialog(p) 1809 dia.setWindowTitle(''.join(['Publish to ', topic_name])) 1810 dia.resize(450,300) 1811 dia.setFocusField('! Publish rate') 1812 1813 if dia.exec_(): 1814 params = dia.getKeywords() 1815 rate = params['! Publish rate'] 1816 opt_str = '' 1817 opt_name_suf = '__latch_' 1818 if rate == 'latch': 1819 opt_str = '' 1820 elif rate == 'once' or rate == '-1': 1821 opt_str = '--once' 1822 opt_name_suf = '__once_' 1823 else: 1824 try: 1825 i = int(rate) 1826 if i > 0: 1827 opt_str = ''.join(['-r ', rate]) 1828 opt_name_suf = ''.join(['__', rate, 'Hz_']) 1829 except: 1830 pass 1831 # remove empty lists 1832 topic_params = self._rem_empty_lists(params[topic_type]) 1833 # print params[topic_type] 1834 # print topic_params 1835 pub_cmd = ' '.join(['pub', topic_name, topic_type, '"', str(topic_params), '"', opt_str]) 1836 # nm.starter().runNodeWithoutConfig(nm.nameres().address(self.masteruri), 'rostopic', 'rostopic', ''.join(['rostopic_pub', topic_name, opt_name_suf, str(rospy.Time.now())]), args=[pub_cmd], masteruri=self.masteruri) 1837 self._progress_queue.add2queue(str(self._progress_queue.count()), 1838 'Start publisher', 1839 nm.starter().runNodeWithoutConfig, 1840 (nm.nameres().address(self.masteruri), 'rostopic', 'rostopic', ''.join(['rostopic_pub', topic_name, opt_name_suf, str(rospy.Time.now())]), [pub_cmd], self.masteruri)) 1841 self._progress_queue.start() 1842 return True 1843 else: 1844 return False 1845 except Exception, e: 1846 rospy.logwarn("Publish topic '%s' failed: %s", str(topic_name), str(e)) 1847 WarningMessageBox(QtGui.QMessageBox.Warning, "Publish topic error", 1848 ''.join(['Publish topic ', topic_name, ' failed!']), 1849 str(e)).exec_() 1850 import traceback 1851 print traceback.format_exc() 1852 return False
1853
1854 - def _rem_empty_lists(self, param_dict):
1855 result = dict() 1856 for key, value in param_dict.iteritems(): 1857 if isinstance(value, dict): 1858 result[key] = self._rem_empty_lists(value) 1859 elif not (isinstance(value, list) and not value): 1860 result[key] = value 1861 return result
1862
1863 - def _getPackages(self, path):
1864 result = {} 1865 if os.path.isdir(path): 1866 fileList = os.listdir(path) 1867 if 'manifest.xml' in fileList: 1868 return {os.path.basename(path) : path} 1869 for f in fileList: 1870 ret = self._getPackages(os.path.sep.join([path, f])) 1871 result = dict(ret.items() + result.items()) 1872 return result
1873 1874
1875 - def on_topic_pub_stop_clicked(self):
1876 selectedTopics = self.topicsFromIndexes(self.masterTab.topicsView.selectionModel().selectedIndexes()) 1877 if not self.master_info is None: 1878 nodes2stop = [] 1879 for topic in selectedTopics: 1880 topic_prefix = ''.join(['/rostopic_pub', topic.name, '_']) 1881 node_names = self.master_info.node_names 1882 for n in node_names: 1883 1884 if n.startswith(topic_prefix): 1885 nodes2stop.append(n) 1886 self.stop_nodes_by_name(nodes2stop)
1887
1888 - def _show_topic_output(self, show_hz_only):
1889 ''' 1890 Shows the output of the topic in a terminal. 1891 ''' 1892 selectedTopics = self.topicsFromIndexes(self.masterTab.topicsView.selectionModel().selectedIndexes()) 1893 for topic in selectedTopics: 1894 try: 1895 # if self._get_nm_masteruri() == self.masteruri: 1896 # if self.__echo_topics_dialogs.has_key(topic.name): 1897 # self.__echo_topics_dialogs[topic.name].activateWindow() 1898 # else: 1899 # topicDia = EchoDialog(topic.name, topic.type, show_hz_only, self) 1900 # self.__echo_topics_dialogs[topic.name] = topicDia 1901 # topicDia.finished_signal.connect(self._topic_dialog_closed) 1902 # topicDia.show() 1903 # else: 1904 # connect to topic on remote host 1905 import os, shlex, subprocess 1906 env = dict(os.environ) 1907 env["ROS_MASTER_URI"] = str(self.masteruri) 1908 cmd = ' '.join(['rosrun', 'node_manager_fkie', 'nm', '-t', topic.name, topic.type, '--hz' if show_hz_only else '', ''.join(['__name:=echo_','hz_' if show_hz_only else '',str(nm.nameres().getHostname(self.masteruri)), topic.name])]) 1909 rospy.loginfo("Echo topic: %s", cmd) 1910 ps = subprocess.Popen(shlex.split(cmd), env=env, close_fds=True) 1911 self.__echo_topics_dialogs[topic.name] = ps 1912 # wait for process to avoid 'defunct' processes 1913 thread = threading.Thread(target=ps.wait) 1914 thread.setDaemon(True) 1915 thread.start() 1916 except Exception, e: 1917 rospy.logwarn("Echo topic '%s' failed: %s", str(topic.name), str(e)) 1918 WarningMessageBox(QtGui.QMessageBox.Warning, "Echo of topic error", 1919 ''.join(['Echo of topic ', topic.name, ' failed!']), 1920 str(e)).exec_()
1921 1922
1923 - def _topic_dialog_closed(self, topic_name):
1924 if self.__echo_topics_dialogs.has_key(topic_name): 1925 del self.__echo_topics_dialogs[topic_name]
1926
1927 - def on_service_call_clicked(self):
1928 ''' 1929 calls a service. 1930 ''' 1931 selectedServices = self.servicesFromIndexes(self.masterTab.servicesView.selectionModel().selectedIndexes()) 1932 for service in selectedServices: 1933 param = ServiceDialog(service, self) 1934 param.show()
1935
1936 - def on_topic_filter_changed(self, text):
1937 ''' 1938 Filter the displayed topics 1939 ''' 1940 self.topic_proxyModel.setFilterRegExp(QtCore.QRegExp(text, QtCore.Qt.CaseInsensitive, QtCore.QRegExp.Wildcard))
1941
1942 - def on_service_filter_changed(self, text):
1943 ''' 1944 Filter the displayed services 1945 ''' 1946 self.service_proxyModel.setFilterRegExp(QtCore.QRegExp(text, QtCore.Qt.CaseInsensitive, QtCore.QRegExp.Wildcard))
1947
1948 - def on_parameter_filter_changed(self, text):
1949 ''' 1950 Filter the displayed parameter 1951 ''' 1952 self.parameter_proxyModel.setFilterRegExp(QtCore.QRegExp(text, QtCore.Qt.CaseInsensitive, QtCore.QRegExp.Wildcard))
1953
1954 - def on_get_parameter_clicked(self):
1955 ''' 1956 Requests parameter list from the ROS parameter server. 1957 ''' 1958 self.parameterHandler.requestParameterList(self.masteruri)
1959
1960 - def on_add_parameter_clicked(self):
1961 ''' 1962 Adds a parameter to the ROS parameter server. 1963 ''' 1964 selectedParameter = self.parameterFromIndexes(self.masterTab.parameterView.selectionModel().selectedIndexes()) 1965 ns = '/' 1966 if selectedParameter: 1967 ns = roslib.names.namespace(selectedParameter[0][0]) 1968 fields = {'name' : ('string', ['', ns]), 'type' : ('string', ['string', 'int', 'float', 'bool']), 'value': ('string', '')} 1969 newparamDia = ParameterDialog(fields, parent=self) 1970 newparamDia.setWindowTitle('Add new parameter') 1971 newparamDia.setFilterVisible(False) 1972 newparamDia.resize(300, 140) 1973 if newparamDia.exec_(): 1974 params = newparamDia.getKeywords() 1975 try: 1976 if params['type'] == 'int': 1977 value = int(params['value']) 1978 elif params['type'] == 'float': 1979 value = float(params['value']) 1980 elif params['type'] == 'bool': 1981 value = bool(params['value'].lower() in ("yes", "true", "t", "1")) 1982 else: 1983 value = params['value'] 1984 self.parameterHandler.deliverParameter(self.masteruri, {params['name'] : value}) 1985 self.parameterHandler.requestParameterList(self.masteruri) 1986 except (KeyError, ValueError), e: 1987 WarningMessageBox(QtGui.QMessageBox.Warning, "Warning", 1988 'Error while add a parameter to the ROS parameter server', 1989 str(e)).exec_()
1990
1992 ''' 1993 Deletes the parameter from the ROS parameter server. 1994 ''' 1995 selectedParameter = self.parameterFromIndexes(self.masterTab.parameterView.selectionModel().selectedIndexes()) 1996 try: 1997 socket.setdefaulttimeout(15) 1998 name = rospy.get_name() 1999 master = xmlrpclib.ServerProxy(self.masteruri) 2000 master_multi = xmlrpclib.MultiCall(master) 2001 for (key, value) in selectedParameter: 2002 master_multi.deleteParam(name, key) 2003 r = master_multi() 2004 for code, msg, parameter in r: 2005 if code != 1: 2006 rospy.logwarn("Error on delete parameter '%s': %s", parameter, msg) 2007 except: 2008 import traceback 2009 rospy.logwarn("Error on delete parameter: %s", str(traceback.format_exc())) 2010 WarningMessageBox(QtGui.QMessageBox.Warning, "Warning", 2011 'Error while delete a parameter to the ROS parameter server', 2012 str(traceback.format_exc())).exec_() 2013 else: 2014 self.on_get_parameter_clicked() 2015 finally: 2016 socket.setdefaulttimeout(None)
2017
2018 - def _replaceDoubleSlash(self, liste):
2019 ''' 2020 used to avoid the adding of \\ for each \ in a string of a list 2021 ''' 2022 if liste and isinstance(liste, list): 2023 result = [] 2024 for l in liste: 2025 val = l 2026 if isinstance(l, (str, unicode)): 2027 val = l.replace("\\n", "\n") 2028 result.append("".join([val])) 2029 elif isinstance(l, list): 2030 val = self._replaceDoubleSlash(l) 2031 result.append(val) 2032 return result 2033 return liste
2034
2035 - def _on_parameter_item_changed(self, item):
2036 ''' 2037 add changes to the ROS parameter server 2038 ''' 2039 if isinstance(item, ParameterValueItem): 2040 try: 2041 if isinstance(item.value, bool): 2042 value = bool(item.text().lower() in ("yes", "true", "t", "1")) 2043 elif isinstance(item.value, int): 2044 value = int(item.text()) 2045 elif isinstance(item.value, float): 2046 value = float(item.text()) 2047 elif isinstance(item.value, list): 2048 import yaml 2049 value = yaml.load(item.text()) 2050 value = self._replaceDoubleSlash(value) 2051 else: 2052 value = item.text() 2053 self.parameterHandler.deliverParameter(self.masteruri, {item.name : value}) 2054 except ValueError, e: 2055 WarningMessageBox(QtGui.QMessageBox.Warning, "Warning", 2056 'Error while add changes to the ROS parameter server', 2057 str(e)).exec_() 2058 item.setText(item.value)
2059
2060 - def _on_param_list(self, masteruri, code, msg, params):
2061 ''' 2062 @param masteruri: The URI of the ROS parameter server 2063 @type masteruri: C{str} 2064 @param code: The return code of the request. If not 1, the message is set and the list can be ignored. 2065 @type code: C{int} 2066 @param msg: The message of the result. 2067 @type msg: C{str} 2068 @param params: The list the parameter names. 2069 @type param: C{[str]} 2070 ''' 2071 if code == 1: 2072 params.sort() 2073 self.parameterHandler.requestParameterValues(masteruri, params)
2074
2075 - def _on_param_values(self, masteruri, code, msg, params):
2076 ''' 2077 @param masteruri: The URI of the ROS parameter server 2078 @type masteruri: C{str} 2079 @param code: The return code of the request. If not 1, the message is set and the list can be ignored. 2080 @type code: C{int} 2081 @param msg: The message of the result. 2082 @type msg: C{str} 2083 @param params: The dictionary the parameter names and request result. 2084 @type param: C{dict(paramName : (code, statusMessage, parameterValue))} 2085 ''' 2086 if code == 1: 2087 result = {} 2088 for p, (code_n, msg_n, val) in params.items(): 2089 if code_n == 1: 2090 result[p] = val 2091 else: 2092 result[p] = '' 2093 self.parameter_model.updateModelData(result) 2094 else: 2095 rospy.logwarn("Error on retrieve parameter from %s: %s", str(masteruri), str(msg))
2096
2097 - def _on_delivered_values(self, masteruri, code, msg, params):
2098 ''' 2099 @param masteruri: The URI of the ROS parameter server 2100 @type masteruri: C{str} 2101 @param code: The return code of the request. If not 1, the message is set and the list can be ignored. 2102 @type code: C{int} 2103 @param msg: The message of the result. 2104 @type msg: C{str} 2105 @param params: The dictionary the parameter names and request result. 2106 @type param: C{dict(paramName : (code, statusMessage, parameterValue))} 2107 ''' 2108 errmsg = '' 2109 if code == 1: 2110 for p, (code_n, msg, val) in params.items(): 2111 if code_n != 1: 2112 errmsg = '\n'.join([errmsg, msg]) 2113 else: 2114 errmsg = msg if msg else 'Unknown error on set parameter' 2115 if errmsg: 2116 WarningMessageBox(QtGui.QMessageBox.Warning, "Warning", 2117 'Error while delivering parameter to the ROS parameter server', 2118 errmsg).exec_()
2119 2120
2121 - def _get_nm_masteruri(self):
2122 ''' 2123 Requests the ROS master URI from the ROS master through the RPC interface and 2124 returns it. The 'materuri' attribute will be set to the requested value. 2125 @return: ROS master URI 2126 @rtype: C{str} or C{None} 2127 ''' 2128 if not hasattr(self, '_nm_materuri') or self._nm_materuri is None: 2129 masteruri = nm.masteruri_from_ros() 2130 master = xmlrpclib.ServerProxy(masteruri) 2131 code, message, self._nm_materuri = master.getUri(rospy.get_name()) 2132 return self._nm_materuri
2133 2134 2135 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2136 #%%%%%%%%%%%%% Shortcuts handling %%%%%%%% 2137 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2138
2139 - def select_host_block(self, index):
2140 ''' 2141 Selects all nodes of a host with given index 2142 @param index: the index of the host in the tree model 2143 @type index: C{int} 2144 ''' 2145 root = self.masterTab.nodeTreeView.model().index(index, 0); 2146 if not root.isValid(): 2147 return 2148 self.masterTab.nodeTreeView.expand(root) 2149 firstChild = root.child(0, 0) 2150 last_row_index = len(self.node_tree_model.header)-1 2151 lastChild = root.child(0, last_row_index) 2152 i = 0 2153 selection = QtGui.QItemSelection() 2154 while root.child(i, 0).isValid(): 2155 index = root.child(i, 0) 2156 item = self.node_tree_model.itemFromIndex(index) 2157 if not item is None and not self._is_in_ignore_list(item.name): 2158 selection.append(QtGui.QItemSelectionRange(index, root.child(i, last_row_index))) 2159 i = i + 1 2160 # selection = QtGui.QItemSelection(firstChild, lastChild) 2161 self.masterTab.nodeTreeView.selectionModel().select(selection, QtGui.QItemSelectionModel.ClearAndSelect)
2162
2163 - def _is_in_ignore_list(self, name):
2164 for i in self._stop_ignores: 2165 if name.endswith(i): 2166 return True 2167 return False
2168
2169 - def on_shortcut1_activated(self):
2170 self.select_host_block(0)
2171 - def on_shortcut2_activated(self):
2172 self.select_host_block(1)
2173 - def on_shortcut3_activated(self):
2174 self.select_host_block(2)
2175 - def on_shortcut4_activated(self):
2176 self.select_host_block(3)
2177 - def on_shortcut5_activated(self):
2178 self.select_host_block(4)
2179
2180 - def on_shortcut_collapse_all(self):
2181 self.masterTab.nodeTreeView.selectionModel().clearSelection() 2182 self.masterTab.nodeTreeView.collapseAll()
2183
2184 - def on_copy_node_clicked(self):
2185 result = '' 2186 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes()) 2187 for node in selectedNodes: 2188 try: 2189 result = ' '.join([result, node.name]) 2190 except Exception, e: 2191 pass 2192 QtGui.QApplication.clipboard().setText(result.strip())
2193
2194 - def on_copy_topic_clicked(self):
2195 result = '' 2196 selectedTopics = self.topicsFromIndexes(self.masterTab.topicsView.selectionModel().selectedIndexes()) 2197 for topic in selectedTopics: 2198 try: 2199 result = ' '.join([result, topic.name, topic.type]) 2200 except Exception, e: 2201 pass 2202 QtGui.QApplication.clipboard().setText(result.strip())
2203
2204 - def on_copy_service_clicked(self):
2205 result = '' 2206 selectedServices = self.servicesFromIndexes(self.masterTab.servicesView.selectionModel().selectedIndexes()) 2207 for service in selectedServices: 2208 try: 2209 result = ' '.join([result, service.name, service.type]) 2210 except Exception, e: 2211 pass 2212 QtGui.QApplication.clipboard().setText(result.strip())
2213
2214 - def on_copy_parameter_clicked(self):
2215 result = '' 2216 selectedParameter = self.parameterFromIndexes(self.masterTab.parameterView.selectionModel().selectedIndexes()) 2217 for (key, value) in selectedParameter: 2218 try: 2219 result = ' '.join([result, key, str(value)]) 2220 except Exception, e: 2221 pass 2222 QtGui.QApplication.clipboard().setText(result.strip())
2223
2224 2225 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2226 #%%%%%%%%%%%%% Filter handling %%%%%%%%%%% 2227 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2228 2229 -class TopicsSortFilterProxyModel(QtGui.QSortFilterProxyModel):
2230 - def filterAcceptsRow(self, sourceRow, sourceParent):
2231 ''' 2232 Perform filtering on columns 0 and 3 (Name, Type) 2233 ''' 2234 index0 = self.sourceModel().index(sourceRow, 0, sourceParent) 2235 index3 = self.sourceModel().index(sourceRow, 3, sourceParent) 2236 2237 regex = self.filterRegExp() 2238 return (regex.indexIn(self.sourceModel().data(index0)) != -1 2239 or regex.indexIn(self.sourceModel().data(index3)) != -1)
2240
2241 -class ServicesSortFilterProxyModel(QtGui.QSortFilterProxyModel):
2242 - def filterAcceptsRow(self, sourceRow, sourceParent):
2243 ''' 2244 Perform filtering on columns 0 and 1 (Name, Type) 2245 ''' 2246 index0 = self.sourceModel().index(sourceRow, 0, sourceParent) 2247 index1 = self.sourceModel().index(sourceRow, 1, sourceParent) 2248 2249 regex = self.filterRegExp() 2250 return (regex.indexIn(self.sourceModel().data(index0)) != -1 2251 or regex.indexIn(self.sourceModel().data(index1)) != -1)
2252
2253 -class ParameterSortFilterProxyModel(QtGui.QSortFilterProxyModel):
2254 - def filterAcceptsRow(self, sourceRow, sourceParent):
2255 ''' 2256 Perform filtering on columns 0 and 1 (Name, Value) 2257 ''' 2258 index0 = self.sourceModel().index(sourceRow, 0, sourceParent) 2259 index1 = self.sourceModel().index(sourceRow, 1, sourceParent) 2260 2261 regex = self.filterRegExp() 2262 return (regex.indexIn(self.sourceModel().data(index0)) != -1 2263 or regex.indexIn(self.sourceModel().data(index1)) != -1)
2264