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
15 from python_qt_binding.QtGui import QFileDialog, QGraphicsScene, QIcon, QImage, QPainter, QWidget, QCompleter, QBrush, QColor, QPen
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 from .dotcode import RosGraphDotcodeGenerator, GATEWAY_GATEWAY_GRAPH, GATEWAY_FLIPPED_GRAPH, GATEWAY_PULLED_GRAPH
24 from .interactive_graphics_view import InteractiveGraphicsView
25 from qt_dotgraph.dot_to_qt import DotToQtGenerator
26 from qt_gui.plugin import Plugin
27
28
29 from qt_dotgraph.pydotfactory import PydotFactory
30
31
32
33 from rocon_gateway import Graph
34
35
36
37
38
39
41 """A completer that completes multiple times from a list"""
42
43 - def init(self, parent=None):
44 QCompleter.init(self, parent)
45
47 path = QCompleter.pathFromIndex(self, index)
48 lst = str(self.widget().text()).split(',')
49 if len(lst) > 1:
50 path = '%s, %s' % (','.join(lst[:-1]), path)
51 return path
52
54 path = str(path.split(',')[-1]).lstrip(' ')
55 return [path]
56
57
59 """Ros package and stacknames"""
60 - def __init__(self, linewidget, topics_only):
61 super(QAbstractListModel, self).__init__(linewidget)
62 self.names = []
63
65 namesset = set()
66 for n in names:
67 namesset.add(str(n).strip())
68 namesset.add("-%s" % (str(n).strip()))
69 self.names = sorted(namesset)
70
72 return len(self.names)
73
74 - def data(self, index, role):
75 if index.isValid() and (role == Qt.DisplayRole or role == Qt.EditRole):
76 return self.names[index.row()]
77 return None
78
79
80
81
82
83
85
86 _deferred_fit_in_view = Signal()
87
89 super(GatewayGraph, self).__init__(context)
90 self.initialised = False
91 self.setObjectName('Gateway Graph')
92 self._current_dotcode = None
93
94 self._widget = QWidget()
95
96
97 self.dotcode_factory = PydotFactory()
98
99 self.dotcode_generator = RosGraphDotcodeGenerator()
100 self.dot_to_qt = DotToQtGenerator()
101 self._graph = Graph()
102
103 rospack = rospkg.RosPack()
104 ui_file = os.path.join(rospack.get_path('rocon_gateway_graph'), 'ui', 'gateway_graph.ui')
105
106 loadUi(ui_file, self._widget, {'InteractiveGraphicsView': InteractiveGraphicsView})
107 self._widget.setObjectName('GatewayGraphUi')
108 if context.serial_number() > 1:
109 self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % context.serial_number()))
110
111 self._scene = QGraphicsScene()
112 self._scene.setBackgroundBrush(Qt.white)
113 self._widget.graphics_view.setScene(self._scene)
114
115 self._widget.refresh_graph_push_button.setIcon(QIcon.fromTheme('view-refresh'))
116 self._widget.refresh_graph_push_button.pressed.connect(self._update_gateway_graph)
117
118 self._widget.graph_type_combo_box.insertItem(0, self.tr('Gateways'), GATEWAY_GATEWAY_GRAPH)
119 self._widget.graph_type_combo_box.insertItem(1, self.tr('Pulled Connections'), GATEWAY_PULLED_GRAPH)
120 self._widget.graph_type_combo_box.insertItem(2, self.tr('Flipped Connections'), GATEWAY_FLIPPED_GRAPH)
121 self._widget.graph_type_combo_box.setCurrentIndex(0)
122 self._widget.graph_type_combo_box.currentIndexChanged.connect(self._refresh_rosgraph)
123
124 self.node_completionmodel = NamespaceCompletionModel(self._widget.filter_line_edit, False)
125 completer = RepeatedWordCompleter(self.node_completionmodel, self)
126 completer.setCompletionMode(QCompleter.PopupCompletion)
127 completer.setWrapAround(True)
128 completer.setCaseSensitivity(Qt.CaseInsensitive)
129 self._widget.filter_line_edit.editingFinished.connect(self._refresh_rosgraph)
130 self._widget.filter_line_edit.setCompleter(completer)
131 self.topic_completionmodel = NamespaceCompletionModel(self._widget.topic_filter_line_edit, False)
132 topic_completer = RepeatedWordCompleter(self.topic_completionmodel, self)
133 topic_completer.setCompletionMode(QCompleter.PopupCompletion)
134 topic_completer.setWrapAround(True)
135 topic_completer.setCaseSensitivity(Qt.CaseInsensitive)
136 self._widget.topic_filter_line_edit.editingFinished.connect(self._refresh_rosgraph)
137 self._widget.topic_filter_line_edit.setCompleter(topic_completer)
138
139 self._widget.namespace_cluster_check_box.clicked.connect(self._refresh_rosgraph)
140 self._widget.watchlist_check_box.clicked.connect(self._refresh_rosgraph)
141 self._widget.all_advertisements_check_box.clicked.connect(self._refresh_rosgraph)
142
143 self._widget.highlight_connections_check_box.toggled.connect(self._redraw_graph_view)
144 self._widget.auto_fit_graph_check_box.toggled.connect(self._redraw_graph_view)
145 self._widget.fit_in_view_push_button.setIcon(QIcon.fromTheme('zoom-original'))
146 self._widget.fit_in_view_push_button.pressed.connect(self._fit_in_view)
147
148 self._widget.load_dot_push_button.setIcon(QIcon.fromTheme('document-open'))
149 self._widget.load_dot_push_button.pressed.connect(self._load_dot)
150 self._widget.save_dot_push_button.setIcon(QIcon.fromTheme('document-save-as'))
151 self._widget.save_dot_push_button.pressed.connect(self._save_dot)
152 self._widget.save_as_svg_push_button.setIcon(QIcon.fromTheme('document-save-as'))
153 self._widget.save_as_svg_push_button.pressed.connect(self._save_svg)
154 self._widget.save_as_image_push_button.setIcon(QIcon.fromTheme('image'))
155 self._widget.save_as_image_push_button.pressed.connect(self._save_image)
156
157 self._update_gateway_graph()
158 self._deferred_fit_in_view.connect(self._fit_in_view, Qt.QueuedConnection)
159 self._deferred_fit_in_view.emit()
160 context.add_widget(self._widget)
161
163 instance_settings.set_value('graph_type_combo_box_index', self._widget.graph_type_combo_box.currentIndex())
164 instance_settings.set_value('filter_line_edit_text', self._widget.filter_line_edit.text())
165 instance_settings.set_value('topic_filter_line_edit_text', self._widget.topic_filter_line_edit.text())
166 instance_settings.set_value('namespace_cluster_check_box_state', self._widget.namespace_cluster_check_box.isChecked())
167 instance_settings.set_value('watchlist_check_box_state', self._widget.watchlist_check_box.isChecked())
168 instance_settings.set_value('all_advertisements_check_box_state', self._widget.all_advertisements_check_box.isChecked())
169 instance_settings.set_value('auto_fit_graph_check_box_state', self._widget.auto_fit_graph_check_box.isChecked())
170 instance_settings.set_value('highlight_connections_check_box_state', self._widget.highlight_connections_check_box.isChecked())
171
173 self._widget.graph_type_combo_box.setCurrentIndex(int(instance_settings.value('graph_type_combo_box_index', 0)))
174 self._widget.filter_line_edit.setText(instance_settings.value('filter_line_edit_text', ''))
175 self._widget.topic_filter_line_edit.setText(instance_settings.value('topic_filter_line_edit_text', '/'))
176 self._widget.namespace_cluster_check_box.setChecked(instance_settings.value('namespace_cluster_check_box_state', True) in [True, 'true'])
177 self._widget.watchlist_check_box.setChecked(instance_settings.value('watchlist_check_box_state', True) in [True, 'true'])
178 self._widget.all_advertisements_check_box.setChecked(instance_settings.value('all_advertisements_check_box_state', False) in [False, 'false'])
179 self._widget.auto_fit_graph_check_box.setChecked(instance_settings.value('auto_fit_graph_check_box_state', True) in [True, 'true'])
180 self._widget.highlight_connections_check_box.setChecked(instance_settings.value('highlight_connections_check_box_state', True) in [True, 'true'])
181 self.initialised = True
182 self._refresh_rosgraph()
183
186
188
189 self._widget.graph_type_combo_box.setEnabled(True)
190 self._widget.filter_line_edit.setEnabled(True)
191 self._widget.topic_filter_line_edit.setEnabled(True)
192 self._widget.namespace_cluster_check_box.setEnabled(True)
193 self._widget.watchlist_check_box.setEnabled(True)
194 self._widget.all_advertisements_check_box.setEnabled(True)
195
196 self._graph.update()
197 self.node_completionmodel.refresh(self._graph.gateway_nodes)
198 self.topic_completionmodel.refresh(self._graph.flipped_nodes)
199 self._refresh_rosgraph()
200
202 if not self.initialised:
203 return
204 self._update_graph_view(self._generate_dotcode())
205
207 ns_filter = self._widget.filter_line_edit.text()
208 topic_filter = self._widget.topic_filter_line_edit.text()
209 graph_mode = self._widget.graph_type_combo_box.itemData(self._widget.graph_type_combo_box.currentIndex())
210 orientation = 'LR'
211 if self._widget.namespace_cluster_check_box.isChecked():
212 namespace_cluster = 1
213 else:
214 namespace_cluster = 0
215 hide_dead_end_topics = self._widget.watchlist_check_box.isChecked()
216 show_all_advertisements = self._widget.all_advertisements_check_box.isChecked()
217
218 return self.dotcode_generator.generate_dotcode(
219 rosgraphinst=self._graph,
220 ns_filter=ns_filter,
221 topic_filter=topic_filter,
222 graph_mode=graph_mode,
223 show_all_advertisements=show_all_advertisements,
224 hide_dead_end_topics=hide_dead_end_topics,
225 cluster_namespaces_level=namespace_cluster,
226 dotcode_factory=self.dotcode_factory,
227 orientation=orientation,
228 )
229
231 if dotcode == self._current_dotcode:
232 return
233 self._current_dotcode = dotcode
234 self._redraw_graph_view()
235
255
257 self._scene.clear()
258
259 if self._widget.highlight_connections_check_box.isChecked():
260 highlight_level = 3
261 else:
262 highlight_level = 1
263
264
265 (nodes, edges) = self.dot_to_qt.dotcode_to_qt_items(self._current_dotcode,
266 highlight_level=highlight_level,
267 same_label_siblings=True)
268
269
270 for node_item in nodes.itervalues():
271 if node_item._label.text() == self._graph.local_gateway_name():
272 orange = QColor(232, 132, 3)
273 node_item._default_color = orange
274 node_item.set_color(orange)
275 print "Local gateway: %s" % self._graph.local_gateway_name()
276 self._scene.addItem(node_item)
277 for edge_items in edges.itervalues():
278 for edge_item in edge_items:
279 edge_item.add_to_scene(self._scene)
280
281 self._scene.setSceneRect(self._scene.itemsBoundingRect())
282 if self._widget.auto_fit_graph_check_box.isChecked():
283 self._fit_in_view()
284
286 if file_name is None:
287 file_name, _ = QFileDialog.getOpenFileName(self._widget, self.tr('Open graph from file'), None, self.tr('DOT graph (*.dot)'))
288 if file_name is None or file_name == '':
289 return
290
291 try:
292 fh = open(file_name, 'rb')
293 dotcode = fh.read()
294 fh.close()
295 except IOError:
296 return
297
298
299 self._widget.graph_type_combo_box.setEnabled(False)
300 self._widget.filter_line_edit.setEnabled(False)
301 self._widget.topic_filter_line_edit.setEnabled(False)
302 self._widget.namespace_cluster_check_box.setEnabled(False)
303 self._widget.watchlist_check_box.setEnabled(False)
304 self._widget.all_advertisements_check_box.setEnabled(False)
305
306 self._update_graph_view(dotcode)
307
309 self._widget.graphics_view.fitInView(self._scene.itemsBoundingRect(), Qt.KeepAspectRatio)
310
312 file_name, _ = QFileDialog.getSaveFileName(self._widget, self.tr('Save as DOT'), 'rosgraph.dot', self.tr('DOT graph (*.dot)'))
313 if file_name is None or file_name == '':
314 return
315
316 handle = QFile(file_name)
317 if not handle.open(QIODevice.WriteOnly | QIODevice.Text):
318 return
319
320 handle.write(self._current_dotcode)
321 handle.close()
322
324 file_name, _ = QFileDialog.getSaveFileName(self._widget, self.tr('Save as SVG'), 'rosgraph.svg', self.tr('Scalable Vector Graphic (*.svg)'))
325 if file_name is None or file_name == '':
326 return
327
328 generator = QSvgGenerator()
329 generator.setFileName(file_name)
330 generator.setSize((self._scene.sceneRect().size() * 2.0).toSize())
331
332 painter = QPainter(generator)
333 painter.setRenderHint(QPainter.Antialiasing)
334 self._scene.render(painter)
335 painter.end()
336
338 file_name, _ = QFileDialog.getSaveFileName(self._widget, self.tr('Save as image'), 'rosgraph.png', self.tr('Image (*.bmp *.jpg *.png *.tiff)'))
339 if file_name is None or file_name == '':
340 return
341
342 img = QImage((self._scene.sceneRect().size() * 2.0).toSize(), QImage.Format_ARGB32_Premultiplied)
343 painter = QPainter(img)
344 painter.setRenderHint(QPainter.Antialiasing)
345 self._scene.render(painter)
346 painter.end()
347 img.save(file_name)
348