1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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
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()
109
110 self.__in_question = []
111
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()
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
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
146
147
148
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
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
163
164
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
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
181
182
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
193
194
195
196 sm = self.masterTab.parameterView.selectionModel()
197 sm.selectionChanged.connect(self.on_parameter_selection_changed)
198 self.masterTab.parameterView.setSortingEnabled(True)
199
200
201
202
203
204 self.masterTab.startButton.clicked.connect(self.on_start_clicked)
205 self.masterTab.stopButton.clicked.connect(self.on_stop_clicked)
206
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
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
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
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
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
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
263 log_menu.addAction(self.logCopyPathAct)
264
265
266 self.masterTab.logButton.setMenu(log_menu)
267
268
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
299
300
301
302
303
304
305
306
307
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
321 return self.__master_state
322
323 @master_state.setter
326
327 @property
329 return self.__master_info
330
331 @master_info.setter
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
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
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
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
381
382 if update_nodes:
383
384 self.updateRunningNodesInModel(self.__master_info)
385
386 if update_others:
387
388 self.updateTopicsListModel(self.__master_info)
389
390 self.updateServiceListModel(self.__master_info)
391
392 self.updateDefaultConfigs(self.__master_info)
393
394
395
396
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
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
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
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
448
486
487
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
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
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
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
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
536 if self.__configs.has_key(launchfile):
537
538 self.removeConfigFromModel(launchfile)
539
540
541
542
543
544
545
546
547
548 try:
549
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
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
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
600 self.updateRunningNodesInModel(self.__master_info)
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
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
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()
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
650 for ((masteruri, addr), nodes) in hosts.items():
651 self.node_tree_model.appendConfigNodes(masteruri, addr, nodes)
652 self.updateButtons()
653
664
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
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
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
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
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
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
720
721
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
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
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
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
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
781
782
783
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
807
808
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
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
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
856
857
859 if not index.parent ().isValid():
860 self.masterTab.nodeTreeView.selectionModel().clear()
861
864
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
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
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
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
956
957
958
959
960
961
962
963
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
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
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
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
1015 selectedParameter = self.parameterFromIndexes(self.masterTab.parameterView.selectionModel().selectedIndexes())
1016 self.masterTab.deleteParameterButton.setEnabled(len(selectedParameter) > 0)
1017
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
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
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
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
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
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
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
1091
1092
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
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
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
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
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
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
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
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
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
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
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
1270 value = None
1271
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
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
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
1302 socket.setdefaulttimeout(10)
1303
1304 p = xmlrpclib.ServerProxy(node.uri)
1305
1306 p.shutdown(rospy.get_name(), ''.join(['[node manager] request from ', self.mastername]))
1307
1308 except Exception, e:
1309
1310
1311 rospy.logwarn("Error while stop node '%s': %s", str(node.name), str(e))
1312 if str(e).find(' 111') == 1:
1313
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
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
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
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
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
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
1360 pass
1361 finally:
1362 socket.setdefaulttimeout(None)
1363
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
1376 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes())
1377
1378
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
1387 if not node is None and not node.uri is None and (not self._is_in_ignore_list(node.name) or force):
1388
1389
1390
1391
1392
1393
1394
1395
1396 try:
1397 socket.setdefaulttimeout(10)
1398 master = xmlrpclib.ServerProxy(node.masteruri)
1399 master_multi = xmlrpclib.MultiCall(master)
1400
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
1427 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes())
1428
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
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
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
1464 return nm.nameres().getHostname(node.masteruri)
1465
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
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
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
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
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
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
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
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
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
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
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
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
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
1668 '''
1669 '''
1670 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes())
1671 for node in selectedNodes:
1672
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
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
1697 cfgs = SelectDialog.getValue('Close configurations', choices.keys(), False, self)
1698
1699
1700
1701
1702 for c in cfgs:
1703 self._close_cfg(choices[c])
1704 self.updateButtons()
1705
1707 try:
1708 self.removeConfigFromModel(cfg)
1709 if isinstance(cfg, tuple):
1710 if not self.master_info is None:
1711
1712 node = self.master_info.getNode(cfg[0])
1713 if not node is None:
1714 self.stop_nodes([node])
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724 else:
1725
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
1736 '''
1737 Shows the output of the topic in a terminal.
1738 '''
1739 self._show_topic_output(False)
1740
1742 '''
1743 Shows the hz of the topic in a terminal.
1744 '''
1745 self._show_topic_output(True)
1746
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:
1754
1755
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
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
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
1832 topic_params = self._rem_empty_lists(params[topic_type])
1833
1834
1835 pub_cmd = ' '.join(['pub', topic_name, topic_type, '"', str(topic_params), '"', opt_str])
1836
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
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
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
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
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
1896
1897
1898
1899
1900
1901
1902
1903
1904
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
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
1924 if self.__echo_topics_dialogs.has_key(topic_name):
1925 del self.__echo_topics_dialogs[topic_name]
1926
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
1937 '''
1938 Filter the displayed topics
1939 '''
1940 self.topic_proxyModel.setFilterRegExp(QtCore.QRegExp(text, QtCore.Qt.CaseInsensitive, QtCore.QRegExp.Wildcard))
1941
1943 '''
1944 Filter the displayed services
1945 '''
1946 self.service_proxyModel.setFilterRegExp(QtCore.QRegExp(text, QtCore.Qt.CaseInsensitive, QtCore.QRegExp.Wildcard))
1947
1949 '''
1950 Filter the displayed parameter
1951 '''
1952 self.parameter_proxyModel.setFilterRegExp(QtCore.QRegExp(text, QtCore.Qt.CaseInsensitive, QtCore.QRegExp.Wildcard))
1953
1959
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
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
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
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
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
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
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
2137
2138
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
2161 self.masterTab.nodeTreeView.selectionModel().select(selection, QtGui.QItemSelectionModel.ClearAndSelect)
2162
2164 for i in self._stop_ignores:
2165 if name.endswith(i):
2166 return True
2167 return False
2168
2179
2181 self.masterTab.nodeTreeView.selectionModel().clearSelection()
2182 self.masterTab.nodeTreeView.collapseAll()
2183
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
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
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
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
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
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
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