31 from __future__
import division
35 from python_qt_binding
import loadUi
36 from python_qt_binding.QtCore
import QAbstractListModel, QFile, QIODevice, Qt, Signal
37 from python_qt_binding.QtGui
import QIcon, QImage, QPainter
38 from python_qt_binding.QtWidgets
import QCompleter, QFileDialog, QGraphicsScene, QWidget
39 from python_qt_binding.QtSvg
import QSvgGenerator
41 import rosgraph.impl.graph
52 from .dotcode
import RosGraphDotcodeGenerator, NODE_NODE_GRAPH, NODE_TOPIC_ALL_GRAPH, NODE_TOPIC_GRAPH
53 from .interactive_graphics_view
import InteractiveGraphicsView
65 """A completer that completes multiple times from a list""" 67 def init(self, parent=None):
68 QCompleter.init(self, parent)
71 path = QCompleter.pathFromIndex(self, index)
72 lst =
unicode(self.widget().text()).split(
',')
74 path =
'%s, %s' % (
','.join(lst[:-1]), path)
78 path =
unicode(path.split(
',')[-1]).lstrip(
' ')
84 """Ros package and stacknames""" 87 super(NamespaceCompletionModel, self).
__init__(linewidget)
93 namesset.add(
unicode(n).strip())
94 namesset.add(
"-%s" % (
unicode(n).strip()))
95 self.
names = sorted(namesset)
98 return len(self.
names)
101 if index.isValid()
and (role == Qt.DisplayRole
or role == Qt.EditRole):
102 return self.
names[index.row()]
108 _deferred_fit_in_view = Signal()
111 super(RosGraph, self).
__init__(context)
113 self.setObjectName(
'RosGraph')
128 rp = rospkg.RosPack()
129 ui_file = os.path.join(rp.get_path(
'rqt_graph'),
'resource',
'RosGraph.ui')
130 loadUi(ui_file, self.
_widget, {
'InteractiveGraphicsView': InteractiveGraphicsView})
131 self._widget.setObjectName(
'RosGraphUi')
132 if context.serial_number() > 1:
133 self._widget.setWindowTitle(
134 self._widget.windowTitle() + (
' (%d)' % context.serial_number()))
137 self._scene.setBackgroundBrush(Qt.white)
138 self._widget.graphics_view.setScene(self.
_scene)
140 self._widget.graph_type_combo_box.insertItem(0, self.tr(
'Nodes only'), NODE_NODE_GRAPH)
141 self._widget.graph_type_combo_box.insertItem(
142 1, self.tr(
'Nodes/Topics (active)'), NODE_TOPIC_GRAPH)
143 self._widget.graph_type_combo_box.insertItem(
144 2, self.tr(
'Nodes/Topics (all)'), NODE_TOPIC_ALL_GRAPH)
145 self._widget.graph_type_combo_box.setCurrentIndex(0)
146 self._widget.graph_type_combo_box.currentIndexChanged.connect(self.
_refresh_rosgraph)
150 completer.setCompletionMode(QCompleter.PopupCompletion)
151 completer.setWrapAround(
True)
152 completer.setCaseSensitivity(Qt.CaseInsensitive)
154 self._widget.filter_line_edit.setCompleter(completer)
157 self._widget.topic_filter_line_edit,
False)
159 topic_completer.setCompletionMode(QCompleter.PopupCompletion)
160 topic_completer.setWrapAround(
True)
161 topic_completer.setCaseSensitivity(Qt.CaseInsensitive)
162 self._widget.topic_filter_line_edit.editingFinished.connect(self.
_refresh_rosgraph)
163 self._widget.topic_filter_line_edit.setCompleter(topic_completer)
165 self._widget.namespace_cluster_spin_box.valueChanged.connect(self.
_refresh_rosgraph)
175 self._widget.refresh_graph_push_button.setIcon(QIcon.fromTheme(
'view-refresh'))
176 self._widget.refresh_graph_push_button.pressed.connect(self.
_update_rosgraph)
180 self._widget.fit_in_view_push_button.setIcon(QIcon.fromTheme(
'zoom-original'))
181 self._widget.fit_in_view_push_button.pressed.connect(self.
_fit_in_view)
183 self._widget.load_dot_push_button.setIcon(QIcon.fromTheme(
'document-open'))
184 self._widget.load_dot_push_button.pressed.connect(self.
_load_dot)
185 self._widget.save_dot_push_button.setIcon(QIcon.fromTheme(
'document-save-as'))
186 self._widget.save_dot_push_button.pressed.connect(self.
_save_dot)
187 self._widget.save_as_svg_push_button.setIcon(QIcon.fromTheme(
'document-save-as'))
188 self._widget.save_as_svg_push_button.pressed.connect(self.
_save_svg)
189 self._widget.save_as_image_push_button.setIcon(QIcon.fromTheme(
'image'))
190 self._widget.save_as_image_push_button.pressed.connect(self.
_save_image)
193 self._deferred_fit_in_view.connect(self.
_fit_in_view, Qt.QueuedConnection)
194 self._deferred_fit_in_view.emit()
196 context.add_widget(self.
_widget)
199 instance_settings.set_value(
200 'graph_type_combo_box_index', self._widget.graph_type_combo_box.currentIndex())
201 instance_settings.set_value(
'filter_line_edit_text', self._widget.filter_line_edit.text())
202 instance_settings.set_value(
203 'topic_filter_line_edit_text', self._widget.topic_filter_line_edit.text())
204 instance_settings.set_value(
205 'namespace_cluster_spin_box_value', self._widget.namespace_cluster_spin_box.value())
206 instance_settings.set_value(
207 'actionlib_check_box_state', self._widget.actionlib_check_box.isChecked())
208 instance_settings.set_value(
209 'dead_sinks_check_box_state', self._widget.dead_sinks_check_box.isChecked())
210 instance_settings.set_value(
211 'leaf_topics_check_box_state', self._widget.leaf_topics_check_box.isChecked())
212 instance_settings.set_value(
213 'quiet_check_box_state', self._widget.quiet_check_box.isChecked())
214 instance_settings.set_value(
215 'unreachable_check_box_state', self._widget.unreachable_check_box.isChecked())
216 instance_settings.set_value(
217 'auto_fit_graph_check_box_state', self._widget.auto_fit_graph_check_box.isChecked())
218 instance_settings.set_value(
219 'highlight_connections_check_box_state', self._widget.highlight_connections_check_box.isChecked())
220 instance_settings.set_value(
221 'group_tf_check_box_state', self._widget.group_tf_check_box.isChecked())
222 instance_settings.set_value(
223 'hide_tf_nodes_check_box_state', self._widget.hide_tf_nodes_check_box.isChecked())
224 instance_settings.set_value(
225 'group_image_check_box_state', self._widget.group_image_check_box.isChecked())
226 instance_settings.set_value(
227 'hide_dynamic_reconfigure_check_box_state', self._widget.hide_dynamic_reconfigure_check_box.isChecked())
230 self._widget.graph_type_combo_box.setCurrentIndex(
231 int(instance_settings.value(
'graph_type_combo_box_index', 0)))
232 self._widget.filter_line_edit.setText(instance_settings.value(
'filter_line_edit_text',
'/'))
233 self._widget.topic_filter_line_edit.setText(
234 instance_settings.value(
'topic_filter_line_edit_text',
'/'))
235 self._widget.namespace_cluster_spin_box.setValue(
236 int(instance_settings.value(
'namespace_cluster_spin_box_value', 2)))
237 self._widget.actionlib_check_box.setChecked(
238 instance_settings.value(
'actionlib_check_box_state',
True)
in [
True,
'true'])
239 self._widget.dead_sinks_check_box.setChecked(
240 instance_settings.value(
'dead_sinks_check_box_state',
True)
in [
True,
'true'])
241 self._widget.leaf_topics_check_box.setChecked(
242 instance_settings.value(
'leaf_topics_check_box_state',
True)
in [
True,
'true'])
243 self._widget.quiet_check_box.setChecked(
244 instance_settings.value(
'quiet_check_box_state',
True)
in [
True,
'true'])
245 self._widget.unreachable_check_box.setChecked(
246 instance_settings.value(
'unreachable_check_box_state',
True)
in [
True,
'true'])
247 self._widget.auto_fit_graph_check_box.setChecked(
248 instance_settings.value(
'auto_fit_graph_check_box_state',
True)
in [
True,
'true'])
249 self._widget.highlight_connections_check_box.setChecked(
250 instance_settings.value(
'highlight_connections_check_box_state',
True)
in [
True,
'true'])
251 self._widget.hide_tf_nodes_check_box.setChecked(
252 instance_settings.value(
'hide_tf_nodes_check_box_state',
False)
in [
True,
'true'])
253 self._widget.group_tf_check_box.setChecked(
254 instance_settings.value(
'group_tf_check_box_state',
True)
in [
True,
'true'])
255 self._widget.group_image_check_box.setChecked(
256 instance_settings.value(
'group_image_check_box_state',
True)
in [
True,
'true'])
257 self._widget.hide_dynamic_reconfigure_check_box.setChecked(
258 instance_settings.value(
'hide_dynamic_reconfigure_check_box_state',
True)
in [
True,
'true'])
264 self._widget.graph_type_combo_box.setEnabled(
True)
265 self._widget.filter_line_edit.setEnabled(
True)
266 self._widget.topic_filter_line_edit.setEnabled(
True)
267 self._widget.namespace_cluster_spin_box.setEnabled(
True)
268 self._widget.actionlib_check_box.setEnabled(
True)
269 self._widget.dead_sinks_check_box.setEnabled(
True)
270 self._widget.leaf_topics_check_box.setEnabled(
True)
271 self._widget.quiet_check_box.setEnabled(
True)
272 self._widget.unreachable_check_box.setEnabled(
True)
273 self._widget.group_tf_check_box.setEnabled(
True)
274 self._widget.hide_tf_nodes_check_box.setEnabled(
True)
275 self._widget.group_image_check_box.setEnabled(
True)
276 self._widget.hide_dynamic_reconfigure_check_box.setEnabled(
True)
278 self.
_graph = rosgraph.impl.graph.Graph()
279 self._graph.set_master_stale(5.0)
280 self._graph.set_node_stale(5.0)
282 self.node_completionmodel.refresh(self._graph.nn_nodes)
283 self.topic_completionmodel.refresh(self._graph.nt_nodes)
292 ns_filter = self._widget.filter_line_edit.text()
293 topic_filter = self._widget.topic_filter_line_edit.text()
294 graph_mode = self._widget.graph_type_combo_box.itemData(
295 self._widget.graph_type_combo_box.currentIndex())
297 namespace_cluster = self._widget.namespace_cluster_spin_box.value()
298 accumulate_actions = self._widget.actionlib_check_box.isChecked()
299 hide_dead_end_topics = self._widget.dead_sinks_check_box.isChecked()
300 hide_single_connection_topics = self._widget.leaf_topics_check_box.isChecked()
301 quiet = self._widget.quiet_check_box.isChecked()
302 unreachable = self._widget.unreachable_check_box.isChecked()
303 group_tf_nodes = self._widget.group_tf_check_box.isChecked()
304 hide_tf_nodes = self._widget.hide_tf_nodes_check_box.isChecked()
305 group_image_nodes = self._widget.group_image_check_box.isChecked()
306 hide_dynamic_reconfigure = self._widget.hide_dynamic_reconfigure_check_box.isChecked()
308 return self.dotcode_generator.generate_dotcode(
311 topic_filter=topic_filter,
312 graph_mode=graph_mode,
313 hide_single_connection_topics=hide_single_connection_topics,
314 hide_dead_end_topics=hide_dead_end_topics,
315 cluster_namespaces_level=namespace_cluster,
316 accumulate_actions=accumulate_actions,
318 orientation=orientation,
320 unreachable=unreachable,
321 group_tf_nodes=group_tf_nodes,
322 hide_tf_nodes=hide_tf_nodes,
323 group_image_nodes=group_image_nodes,
324 hide_dynamic_reconfigure=hide_dynamic_reconfigure)
333 if url
is not None and ':' in url:
334 item_type, item_path = url.split(
':', 1)
335 if item_type ==
'node':
336 tool_tip =
'Node:\n %s' % (item_path)
337 service_names = rosservice.get_service_list(node=item_path)
339 tool_tip +=
'\nServices:' 340 for service_name
in service_names:
342 service_type = rosservice.get_service_type(service_name)
343 tool_tip +=
'\n %s [%s]' % (service_name, service_type)
344 except rosservice.ROSServiceIOException
as e:
345 tool_tip +=
'\n %s' % (e)
347 elif item_type ==
'topic':
348 topic_type, topic_name, _ = rostopic.get_topic_type(item_path)
349 return 'Topic:\n %s\nType:\n %s' % (topic_name, topic_type)
355 if self._widget.highlight_connections_check_box.isChecked():
362 highlight_level=highlight_level,
363 same_label_siblings=
True,
366 self._scene.setSceneRect(self._scene.itemsBoundingRect())
367 if self._widget.auto_fit_graph_check_box.isChecked():
371 if file_name
is None:
372 file_name, _ = QFileDialog.getOpenFileName(
373 self.
_widget, self.tr(
'Open graph from file'),
None, self.tr(
'DOT graph (*.dot)'))
374 if file_name
is None or file_name ==
'':
378 fh = open(file_name,
'rb')
385 self._widget.graph_type_combo_box.setEnabled(
False)
386 self._widget.filter_line_edit.setEnabled(
False)
387 self._widget.topic_filter_line_edit.setEnabled(
False)
388 self._widget.namespace_cluster_spin_box.setEnabled(
False)
389 self._widget.actionlib_check_box.setEnabled(
False)
390 self._widget.dead_sinks_check_box.setEnabled(
False)
391 self._widget.leaf_topics_check_box.setEnabled(
False)
392 self._widget.quiet_check_box.setEnabled(
False)
393 self._widget.unreachable_check_box.setEnabled(
False)
394 self._widget.group_tf_check_box.setEnabled(
False)
395 self._widget.hide_tf_nodes_check_box.setEnabled(
False)
396 self._widget.group_image_check_box.setEnabled(
False)
397 self._widget.hide_dynamic_reconfigure_check_box.setEnabled(
False)
402 self._widget.graphics_view.fitInView(self._scene.itemsBoundingRect(), Qt.KeepAspectRatio)
405 file_name, _ = QFileDialog.getSaveFileName(
406 self.
_widget, self.tr(
'Save as DOT'),
'rosgraph.dot', self.tr(
'DOT graph (*.dot)'))
407 if file_name
is None or file_name ==
'':
410 handle = QFile(file_name)
411 if not handle.open(QIODevice.WriteOnly | QIODevice.Text):
418 file_name, _ = QFileDialog.getSaveFileName(
419 self.
_widget, self.tr(
'Save as SVG'),
'rosgraph.svg', self.tr(
'Scalable Vector Graphic (*.svg)'))
420 if file_name
is None or file_name ==
'':
423 generator = QSvgGenerator()
424 generator.setFileName(file_name)
425 generator.setSize((self._scene.sceneRect().size() * 2.0).toSize())
427 painter = QPainter(generator)
428 painter.setRenderHint(QPainter.Antialiasing)
429 self._scene.render(painter)
433 file_name, _ = QFileDialog.getSaveFileName(
434 self.
_widget, self.tr(
'Save as image'),
'rosgraph.png', self.tr(
'Image (*.bmp *.jpg *.png *.tiff)'))
435 if file_name
is None or file_name ==
'':
438 img = QImage((self._scene.sceneRect().size() * 2.0)
439 .toSize(), QImage.Format_ARGB32_Premultiplied)
440 painter = QPainter(img)
441 painter.setRenderHint(QPainter.Antialiasing)
442 self._scene.render(painter)
def _redraw_graph_view(self)
def __init__(self, linewidget, topics_only)
def restore_settings(self, plugin_settings, instance_settings)
def __init__(self, context)
def splitPath(self, path)
def _generate_dotcode(self)
def save_settings(self, plugin_settings, instance_settings)
def data(self, index, role)
def _generate_tool_tip(self, url)
def init(self, parent=None)
def pathFromIndex(self, index)
def _refresh_rosgraph(self)
def _load_dot(self, file_name=None)
def rowCount(self, parent)
def _update_graph_view(self, dotcode)
def _update_rosgraph(self)