1
2
3
4
5
6
7
8
9
10 from __future__ import division
11 import os
12
13 from python_qt_binding import loadUi
14 from python_qt_binding.QtCore import QFile, QIODevice, Qt, Signal, QAbstractListModel, pyqtSignal, pyqtSlot,SIGNAL,SLOT
15 from python_qt_binding.QtGui import QFileDialog, QGraphicsScene, QIcon, QImage, QPainter, QWidget,QLabel, QComboBox, QSizePolicy,QTextEdit ,QCompleter, QBrush,QDialog, QColor, QPen, QPushButton, QTabWidget, QPlainTextEdit,QGridLayout, QVBoxLayout, QHBoxLayout, QMessageBox
16 from python_qt_binding.QtSvg import QSvgGenerator
17
18 import rosgraph.impl.graph
19 import rosservice
20 import rostopic
21 import rospkg
22
23
24
25 import rosnode
26 import roslib
27 import rospy
28 from concert_msgs.msg import ConcertClients
29 from rocon_app_manager_msgs.srv import GetPlatformInfo, Status, Invite, StartApp, StopApp
30 from rocon_app_manager_msgs.msg import PlatformInfo
31
32
33 from .dotcode import RosGraphDotcodeGenerator
34 from .interactive_graphics_view import InteractiveGraphicsView
35 from qt_dotgraph.dot_to_qt import DotToQtGenerator
36 from qt_gui.plugin import Plugin
37
38
39 from qt_dotgraph.pydotfactory import PydotFactory
40
41
42
43 from rocon_gateway import Graph
44 from conductor_graph_info import ConductorGraphInfo
45
46
47
48
49
50
51
52
54 """A completer that completes multiple times from a list"""
55
56 - def init(self, parent=None):
57 QCompleter.init(self, parent)
58
60 path = QCompleter.pathFromIndex(self, index)
61 lst = str(self.widget().text()).split(',')
62 if len(lst) > 1:
63 path = '%s, %s' % (','.join(lst[:-1]), path)
64 return path
65
67 path = str(path.split(',')[-1]).lstrip(' ')
68 return [path]
69
70
72 - def __init__(self,tabWidget,node_item,callback_func):
73 self._tabWidget = tabWidget
74 self._callback_func = callback_func
75 self._node_item = node_item
76
77
79 for k in range(self._tabWidget.count()):
80 if self._tabWidget.tabText(k) == self._node_item._label.text():
81 self._tabWidget.setCurrentIndex (k)
82
83
85 """Ros package and stacknames"""
86 - def __init__(self, linewidget, topics_only):
87 super(QAbstractListModel, self).__init__(linewidget)
88 self.names = []
89
91 namesset = set()
92 for n in names:
93 namesset.add(str(n).strip())
94 namesset.add("-%s" % (str(n).strip()))
95 self.names = sorted(namesset)
96
98 return len(self.names)
99
100 - def data(self, index, role):
101 if index.isValid() and (role == Qt.DisplayRole or role == Qt.EditRole):
102 return self.names[index.row()]
103 return None
104
105
106
107
108
109
111
112 _deferred_fit_in_view = Signal()
113 _client_list_update_signal = Signal()
114
116 self._context = context
117 super(ConductorGraph, self).__init__(context)
118 self.initialised = False
119 self.setObjectName('Conductor Graph')
120 self._current_dotcode = None
121
122 self._node_item_events = {}
123 self._client_info_list = {}
124 self._widget = QWidget()
125
126
127
128 self.dotcode_factory = PydotFactory()
129
130 self.dotcode_generator = RosGraphDotcodeGenerator()
131 self.dot_to_qt = DotToQtGenerator()
132 self._graph = ConductorGraphInfo()
133
134 rospack = rospkg.RosPack()
135 ui_file = os.path.join(rospack.get_path('rocon_conductor_graph'), 'ui', 'conductor_graph.ui')
136
137 loadUi(ui_file, self._widget, {'InteractiveGraphicsView': InteractiveGraphicsView})
138 self._widget.setObjectName('ConductorGraphUi')
139
140 if context.serial_number() > 1:
141 self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % context.serial_number()))
142
143 self._scene = QGraphicsScene()
144 self._scene.setBackgroundBrush(Qt.white)
145 self._widget.graphics_view.setScene(self._scene)
146
147
148 self._widget.refresh_graph_push_button.setIcon(QIcon.fromTheme('window-new'))
149 self._widget.refresh_graph_push_button.pressed.connect(self._update_conductor_graph)
150
151 self._widget.highlight_connections_check_box.toggled.connect(self._redraw_graph_view)
152 self._widget.auto_fit_graph_check_box.toggled.connect(self._redraw_graph_view)
153 self._widget.fit_in_view_push_button.setIcon(QIcon.fromTheme('zoom-original'))
154 self._widget.fit_in_view_push_button.pressed.connect(self._fit_in_view)
155
156 self._update_conductor_graph()
157 self._deferred_fit_in_view.connect(self._fit_in_view, Qt.QueuedConnection)
158 self._deferred_fit_in_view.emit()
159
160
161 self._widget.tabWidget.currentChanged.connect(lambda index: self._test_function(index));
162 self._client_list_update_signal.connect(self._update_conductor_graph)
163 rospy.Subscriber("/concert/list_concert_clients", ConcertClients, self._update_client_list)
164
165 context.add_widget(self._widget)
166
168
169
170 service_text_widget = None
171 cur_tab_widget = self._widget.tabWidget.currentWidget()
172
173 if cur_tab_widget == None:
174 return
175
176 object_name = 'services_text_widget'
177 for k in cur_tab_widget.children():
178 if k.objectName().count(object_name) >= 1 :
179 service_text_widget = k
180 break
181
182 if service_text_widget == None:
183 return
184
185 service_text_widget.clear()
186
187
189 self.initialised = True
190 self._refresh_rosgraph()
191
194
196
197
198 self._refresh_rosgraph()
199 self._update_client_tab()
200
202 if not self.initialised:
203 return
204 self._update_graph_view(self._generate_dotcode())
205
207 return self.dotcode_generator.generate_dotcode(rosgraphinst=self._graph,
208 dotcode_factory=self.dotcode_factory,
209 orientation='LR'
210 )
212 if dotcode == self._current_dotcode:
213 return
214 self._current_dotcode = dotcode
215 self._redraw_graph_view()
216
220
222
223
224 service_text_widget = None
225
226 cur_tab_widget = self._widget.tabWidget.currentWidget()
227 if cur_tab_widget == None:
228 return
229
230 object_name = 'services_text_widget'
231 for k in cur_tab_widget.children():
232 if k.objectName().count(object_name) >= 1 :
233 service_text_widget = k
234 break
235
236 if service_text_widget == None:
237 return
238
239 service_text_widget.clear()
240
241 service = self._graph._client_info_list[node_name]['gateway_name']+"/"+service_name
242 info_text = ''
243
244 if service_name == 'status':
245 service_handle = rospy.ServiceProxy(service, Status)
246 call_result = service_handle()
247
248 info_text = "<html>"
249 info_text += "<p>-------------------------------------------</p>"
250 info_text += "<p><b>application_namespace: </b>" +call_result.application_namespace+"</p>"
251 info_text += "<p><b>remote_controller: </b>" +call_result.remote_controller+"</p>"
252 info_text += "<p><b>app_status: </b>" +call_result.app_status+"</p>"
253 info_text +="</html>"
254
255 elif service_name == 'platform_info':
256
257 service_handle = rospy.ServiceProxy(service, GetPlatformInfo)
258 call_result = service_handle()
259
260 info_text = "<html>"
261 info_text += "<p>-------------------------------------------</p>"
262 info_text += "<p><b>platform: </b>" +call_result.platform_info.platform+"</p>"
263 info_text += "<p><b>system: </b>" +call_result.platform_info.system+"</p>"
264 info_text += "<p><b>robot: </b>" +call_result.platform_info.robot+"</p>"
265 info_text += "<p><b>name: </b>" +call_result.platform_info.name+"</p>"
266 info_text +="</html>"
267
268 elif service_name == 'invite':
269
270 service_handle = rospy.ServiceProxy(service, Invite)
271
272 dlg = QDialog(self._widget)
273
274 dlg.setSizePolicy(QSizePolicy.MinimumExpanding,QSizePolicy.Ignored)
275 dlg.setMinimumSize(500,0)
276 dlg_rect = dlg.geometry()
277
278
279 ver_layout = QVBoxLayout(dlg)
280 ver_layout.setContentsMargins (9,9,9,9)
281
282
283 text_grid_sub_widget = QWidget()
284 text_grid_layout = QGridLayout(text_grid_sub_widget)
285 text_grid_layout.setColumnStretch (1, 0)
286 text_grid_layout.setRowStretch (2, 0)
287
288
289 remote_target_name =u""
290 title_widget1 = QLabel("remote_target_name: ")
291 context_widget1 = QTextEdit()
292 context_widget1.setSizePolicy(QSizePolicy.MinimumExpanding,QSizePolicy.Ignored)
293 context_widget1.setMinimumSize(0,30)
294 context_widget1.append("")
295
296
297 application_namespace=u""
298 title_widget2 = QLabel("application_namespace: ")
299 context_widget2 = QTextEdit()
300 context_widget2.setSizePolicy(QSizePolicy.MinimumExpanding,QSizePolicy.Ignored)
301 context_widget2.setMinimumSize(0,30)
302 context_widget2.append("")
303
304
305 cancel=False
306 title_widget3 = QLabel("cancel: ")
307 context_widget3 = QComboBox()
308 context_widget3.setSizePolicy(QSizePolicy.MinimumExpanding,QSizePolicy.Ignored)
309 context_widget3.setMinimumSize(0,30)
310
311 context_widget3.addItem("True",True)
312 context_widget3.addItem("False",False)
313
314
315 text_grid_layout.addWidget(title_widget1)
316 text_grid_layout.addWidget(context_widget1)
317
318 text_grid_layout.addWidget(title_widget2)
319 text_grid_layout.addWidget(context_widget2)
320
321 text_grid_layout.addWidget(title_widget3)
322 text_grid_layout.addWidget(context_widget3)
323
324
325 ver_layout.addWidget(text_grid_sub_widget)
326
327
328 button_hor_sub_widget = QWidget()
329 button_hor_layout = QHBoxLayout(button_hor_sub_widget)
330
331
332 btn_call = QPushButton("Call")
333 btn_cancel = QPushButton("cancel")
334
335 btn_call.clicked.connect(lambda: dlg.done(0))
336 btn_call.clicked.connect(lambda : service_handle(context_widget1.toPlainText(),
337 context_widget2.toPlainText(),
338 context_widget3.itemData(context_widget3.currentIndex())
339 ))
340
341 btn_cancel.clicked.connect(lambda: dlg.done(0))
342
343
344 button_hor_layout.addWidget(btn_call)
345 button_hor_layout.addWidget(btn_cancel)
346
347
348 ver_layout.addWidget(button_hor_sub_widget)
349
350 dlg.setVisible(True)
351
352 elif service_name == 'start_app':
353
354 service_handle = rospy.ServiceProxy(service, StartApp)
355
356 dlg = QDialog(self._widget)
357
358 dlg.setSizePolicy(QSizePolicy.MinimumExpanding,QSizePolicy.Ignored)
359 dlg.setMinimumSize(500,0)
360 dlg_rect = dlg.geometry()
361
362
363 ver_layout = QVBoxLayout(dlg)
364 ver_layout.setContentsMargins (9,9,9,9)
365
366
367 text_grid_sub_widget = QWidget()
368 text_grid_layout = QGridLayout(text_grid_sub_widget)
369 text_grid_layout.setColumnStretch (1, 0)
370 text_grid_layout.setRowStretch (2, 0)
371
372
373 name =u""
374 title_widget1 = QLabel("name: ")
375 context_widget1 = QTextEdit()
376 context_widget1.setSizePolicy(QSizePolicy.MinimumExpanding,QSizePolicy.Ignored)
377 context_widget1.setMinimumSize(0,30)
378 context_widget1.append("")
379
380
381 cancel=False
382 title_widget2 = QLabel("remappings: ")
383 context_widget2 = QTextEdit()
384 context_widget2.setSizePolicy(QSizePolicy.MinimumExpanding,QSizePolicy.Ignored)
385 context_widget2.setMinimumSize(0,30)
386 context_widget2.append("")
387
388
389 text_grid_layout.addWidget(title_widget1)
390 text_grid_layout.addWidget(context_widget1)
391
392 text_grid_layout.addWidget(title_widget2)
393 text_grid_layout.addWidget(context_widget2)
394
395
396 ver_layout.addWidget(text_grid_sub_widget)
397
398
399 button_hor_sub_widget = QWidget()
400 button_hor_layout = QHBoxLayout(button_hor_sub_widget)
401
402
403 btn_call = QPushButton("Call")
404 btn_cancel = QPushButton("cancel")
405
406 btn_call.clicked.connect(lambda: dlg.done(0))
407 btn_call.clicked.connect(lambda : service_handle(context_widget1.toPlainText(),
408 []))
409
410 btn_cancel.clicked.connect(lambda: dlg.done(0))
411
412
413 button_hor_layout.addWidget(btn_call)
414 button_hor_layout.addWidget(btn_cancel)
415
416
417 ver_layout.addWidget(button_hor_sub_widget)
418
419 dlg.setVisible(True)
420
421 print 'start app'
422
423 elif service_name == 'stop_app':
424 service_handle = rospy.ServiceProxy(service, StopApp)
425 call_result = service_handle()
426 print 'stop app'
427 else:
428 print 'has no service'
429
430 service_text_widget.appendHtml(info_text)
431
432
434 self._widget.tabWidget.clear()
435 for k in self._graph._client_info_list.values():
436 main_widget=QWidget()
437
438 ver_layout = QVBoxLayout(main_widget)
439
440 ver_layout.setContentsMargins (9,9,9,9)
441 ver_layout.setSizeConstraint (ver_layout.SetDefaultConstraint)
442
443 sub_widget = QWidget()
444 sub_widget.setAccessibleName('sub_widget')
445 btn_grid_layout = QGridLayout(sub_widget)
446
447 btn_grid_layout.setContentsMargins (9,9,9,9)
448
449 btn_grid_layout.setColumnStretch (1, 0)
450 btn_grid_layout.setRowStretch (2, 0)
451
452 btn_invite = QPushButton("invite")
453 btn_platform_info = QPushButton("platform_info")
454 btn_status = QPushButton("status")
455 btn_start_app = QPushButton("start_app")
456 btn_stop_app = QPushButton("stop_app")
457
458 btn_invite.clicked.connect(lambda: self._start_service(self._widget.tabWidget.tabText(self._widget.tabWidget.currentIndex()),"invite"))
459 btn_platform_info.clicked.connect(lambda: self._start_service(self._widget.tabWidget.tabText(self._widget.tabWidget.currentIndex()),"platform_info"))
460 btn_status.clicked.connect(lambda: self._start_service(self._widget.tabWidget.tabText(self._widget.tabWidget.currentIndex()),"status"))
461 btn_start_app.clicked.connect(lambda: self._start_service(self._widget.tabWidget.tabText(self._widget.tabWidget.currentIndex()),"start_app"))
462 btn_stop_app.clicked.connect(lambda: self._start_service(self._widget.tabWidget.tabText(self._widget.tabWidget.currentIndex()),"stop_app"))
463
464 btn_grid_layout.addWidget(btn_invite)
465 btn_grid_layout.addWidget(btn_platform_info)
466 btn_grid_layout.addWidget(btn_status)
467 btn_grid_layout.addWidget(btn_start_app)
468 btn_grid_layout.addWidget(btn_stop_app)
469
470 ver_layout.addWidget(sub_widget)
471
472 app_context_widget = QPlainTextEdit()
473 app_context_widget.setObjectName(k["app_name"]+'_'+'app_context_widget')
474 app_context_widget.setAccessibleName('app_context_widget')
475 app_context_widget.appendHtml(k["app_context"])
476
477 ver_layout.addWidget(app_context_widget)
478
479 services_text_widget = QPlainTextEdit()
480 services_text_widget.setObjectName(k["app_name"]+'_'+'services_text_widget')
481 ver_layout.addWidget(services_text_widget)
482
483
484 path = ""
485 if k["isNew"] == True:
486 path = os.path.join(os.path.dirname(os.path.abspath(__file__)),"../../resources/images/new.gif")
487
488
489 self._widget.tabWidget.addTab(main_widget,QIcon(path), k["app_name"]);
490
492 self._scene.clear()
493 self._node_item_events = {}
494
495 if self._widget.highlight_connections_check_box.isChecked():
496 highlight_level = 3
497 else:
498 highlight_level = 1
499
500
501 (nodes, edges) = self.dot_to_qt.dotcode_to_qt_items(self._current_dotcode,
502 highlight_level=highlight_level,
503 same_label_siblings=True)
504
505
506 for node_item in nodes.itervalues():
507
508 if node_item._label.text() == self._graph._concert_conductor_name:
509 royal_blue = QColor(65, 105, 255)
510 node_item._default_color = royal_blue
511 node_item.set_color(royal_blue)
512
513
514
515
516 if self._graph._client_info_list.has_key(str(node_item._label.text())):
517 node_item.setToolTip(self._graph._client_info_list[node_item._label.text()]['uuid'])
518
519
520 self._node_item_events[node_item._label.text()] = NodeEventHandler(self._widget.tabWidget,node_item,node_item.mouseDoubleClickEvent);
521 node_item.mouseDoubleClickEvent = self._node_item_events[node_item._label.text()].NodeEvent;
522
523 self._scene.addItem(node_item)
524
525
526 for edge_items in edges.itervalues():
527 for edge_item in edge_items:
528 edge_item.add_to_scene(self._scene)
529
530 edge_dst_name = edge_item.to_node._label.text()
531 if self._graph._client_info_list.has_key(edge_dst_name):
532 connection_strength = self._graph._client_info_list[edge_dst_name]['connection_strength']
533 if connection_strength == 'very_strong':
534 green = QColor(0, 255, 0)
535 edge_item._default_color = green
536 edge_item.set_color(green)
537
538 elif connection_strength == 'strong':
539 green_yellow = QColor(125, 255,0)
540 edge_item._default_color = green_yellow
541 edge_item.set_color(green_yellow)
542
543 elif connection_strength == 'normal':
544 yellow = QColor(238, 238,0)
545 edge_item._default_color = yellow
546 edge_item.set_color(yellow)
547
548 elif connection_strength == 'weak':
549 yellow_red = QColor(255, 125,0)
550 edge_item._default_color = yellow_red
551 edge_item.set_color(yellow_red)
552
553 elif connection_strength == 'very_weak':
554 red = QColor(255, 0,0)
555 edge_item._default_color = red
556 edge_item.set_color(red)
557
558 self._scene.setSceneRect(self._scene.itemsBoundingRect())
559
560 if self._widget.auto_fit_graph_check_box.isChecked():
561 self._fit_in_view()
562
564 self._widget.graphics_view.fitInView(self._scene.itemsBoundingRect(), Qt.KeepAspectRatio)
565