33 from __future__
import division
34 from __future__
import print_function
41 from python_qt_binding
import loadUi
42 from python_qt_binding.QtCore
import QFile, QIODevice, Qt, Signal, Slot, QAbstractListModel
43 from python_qt_binding.QtGui
import QIcon, QImage, QPainter
44 from python_qt_binding.QtWidgets
import QFileDialog, QGraphicsScene, QWidget, QCompleter
45 from python_qt_binding.QtSvg
import QSvgGenerator
50 from .dotcode_pack
import RosPackageGraphDotcodeGenerator
62 """A completer that completes multiple times from a list"""
65 path = QCompleter.pathFromIndex(self, index)
66 lst = str(self.widget().text()).split(
',')
68 path =
'%s, %s' % (
','.join(lst[:-1]), path)
72 path = str(path.split(
',')[-1]).lstrip(
' ')
78 """Ros package and stacknames"""
80 def __init__(self, linewidget, rospack, rosstack):
81 super(StackageCompletionModel, self).
__init__(linewidget)
82 self.
allnames = sorted(list(set(list(rospack.list()) + list(rosstack.list()))))
88 def data(self, index, role):
90 if index.isValid()
and (role == Qt.DisplayRole
or role == Qt.EditRole):
97 _deferred_fit_in_view = Signal()
100 super(RosPackGraph, self).
__init__(context)
109 self.setObjectName(
'RosPackGraph')
111 rospack = rospkg.RosPack()
112 rosstack = rospkg.RosStack()
123 rp = rospkg.RosPack()
124 ui_file = os.path.join(rp.get_path(
'rqt_dep'),
'resource',
'RosPackGraph.ui')
125 loadUi(ui_file, self.
_widget, {
'InteractiveGraphicsView': InteractiveGraphicsView})
126 self.
_widget.setObjectName(
'RosPackGraphUi')
127 if context.serial_number() > 1:
129 self.
_widget.windowTitle() + (
' (%d)' % context.serial_number()))
132 self.
_scene.setBackgroundBrush(Qt.white)
135 self.
_widget.depth_combo_box.insertItem(0, self.tr(
'infinite'), -1)
136 self.
_widget.depth_combo_box.insertItem(1, self.tr(
'1'), 2)
137 self.
_widget.depth_combo_box.insertItem(2, self.tr(
'2'), 3)
138 self.
_widget.depth_combo_box.insertItem(3, self.tr(
'3'), 4)
139 self.
_widget.depth_combo_box.insertItem(4, self.tr(
'4'), 5)
142 self.
_widget.directions_combo_box.insertItem(0, self.tr(
'depends'), 0)
143 self.
_widget.directions_combo_box.insertItem(1, self.tr(
'depends_on'), 1)
144 self.
_widget.directions_combo_box.insertItem(2, self.tr(
'both'), 2)
147 self.
_widget.package_type_combo_box.insertItem(0, self.tr(
'wet & dry'), 3)
148 self.
_widget.package_type_combo_box.insertItem(1, self.tr(
'wet only'), 2)
149 self.
_widget.package_type_combo_box.insertItem(2, self.tr(
'dry only'), 1)
154 completer.setCompletionMode(QCompleter.PopupCompletion)
155 completer.setWrapAround(
True)
157 completer.setCaseSensitivity(Qt.CaseInsensitive)
159 self.
_widget.filter_line_edit.setCompleter(completer)
168 self.
_widget.refresh_graph_push_button.setIcon(QIcon.fromTheme(
'view-refresh'))
173 self.
_widget.fit_in_view_push_button.setIcon(QIcon.fromTheme(
'zoom-original'))
176 self.
_widget.load_dot_push_button.setIcon(QIcon.fromTheme(
'document-open'))
178 self.
_widget.save_dot_push_button.setIcon(QIcon.fromTheme(
'document-save-as'))
180 self.
_widget.save_as_svg_push_button.setIcon(QIcon.fromTheme(
'document-save-as'))
182 self.
_widget.save_as_image_push_button.setIcon(QIcon.fromTheme(
'image'))
188 context.add_widget(self.
_widget)
199 instance_settings.set_value(
200 'depth_combo_box_index', self.
_widget.depth_combo_box.currentIndex())
201 instance_settings.set_value(
202 'directions_combo_box_index', self.
_widget.directions_combo_box.currentIndex())
203 instance_settings.set_value(
204 'package_type_combo_box', self.
_widget.package_type_combo_box.currentIndex())
205 instance_settings.set_value(
'filter_line_edit_text', self.
_widget.filter_line_edit.text())
206 instance_settings.set_value(
207 'with_stacks_state', self.
_widget.with_stacks_check_box.isChecked())
208 instance_settings.set_value(
209 'hide_transitives_state', self.
_widget.hide_transitives_check_box.isChecked())
210 instance_settings.set_value(
211 'show_system_state', self.
_widget.show_system_check_box.isChecked())
212 instance_settings.set_value(
'mark_state', self.
_widget.mark_check_box.isChecked())
213 instance_settings.set_value(
'colorize_state', self.
_widget.colorize_check_box.isChecked())
214 instance_settings.set_value(
215 'auto_fit_graph_check_box_state', self.
_widget.auto_fit_graph_check_box.isChecked())
216 instance_settings.set_value(
'highlight_connections_check_box_state',
217 self.
_widget.highlight_connections_check_box.isChecked())
220 _str_filter = instance_settings.value(
'filter_line_edit_text',
'')
221 if (_str_filter ==
None or _str_filter ==
'')
and \
223 _str_filter =
'(Separate pkgs by comma)'
227 self.
_widget.depth_combo_box.setCurrentIndex(
228 int(instance_settings.value(
'depth_combo_box_index', 0)))
229 self.
_widget.directions_combo_box.setCurrentIndex(
230 int(instance_settings.value(
'directions_combo_box_index', 0)))
231 self.
_widget.package_type_combo_box.setCurrentIndex(
232 int(instance_settings.value(
'package_type_combo_box', 0)))
233 self.
_widget.filter_line_edit.setText(_str_filter)
234 self.
_widget.with_stacks_check_box.setChecked(
235 instance_settings.value(
'with_stacks_state',
True)
in [
True,
'true'])
236 self.
_widget.mark_check_box.setChecked(
237 instance_settings.value(
'mark_state',
True)
in [
True,
'true'])
238 self.
_widget.colorize_check_box.setChecked(
239 instance_settings.value(
'colorize_state',
False)
in [
True,
'true'])
240 self.
_widget.hide_transitives_check_box.setChecked(
241 instance_settings.value(
'hide_transitives_state',
False)
in [
True,
'true'])
242 self.
_widget.show_system_check_box.setChecked(
243 instance_settings.value(
'show_system_state',
False)
in [
True,
'true'])
244 self.
_widget.auto_fit_graph_check_box.setChecked(instance_settings.value(
245 'auto_fit_graph_check_box_state',
True)
in [
True,
'true'])
246 self.
_widget.highlight_connections_check_box.setChecked(instance_settings.value(
247 'highlight_connections_check_box_state',
True)
in [
True,
'true'])
253 self.
_widget.depth_combo_box.setEnabled(
True)
254 self.
_widget.directions_combo_box.setEnabled(
True)
255 self.
_widget.package_type_combo_box.setEnabled(
True)
256 self.
_widget.filter_line_edit.setEnabled(
True)
257 self.
_widget.with_stacks_check_box.setEnabled(
True)
258 self.
_widget.mark_check_box.setEnabled(
True)
259 self.
_widget.colorize_check_box.setEnabled(
True)
260 self.
_widget.hide_transitives_check_box.setEnabled(
True)
261 self.
_widget.show_system_check_box.setEnabled(
True)
267 self.
_widget.depth_combo_box.currentIndex())
268 self.
_options[
'directions'] = self.
_widget.directions_combo_box.itemData(
269 self.
_widget.directions_combo_box.currentIndex())
270 self.
_options[
'package_types'] = self.
_widget.package_type_combo_box.itemData(
271 self.
_widget.package_type_combo_box.currentIndex())
272 self.
_options[
'with_stacks'] = self.
_widget.with_stacks_check_box.isChecked()
273 self.
_options[
'mark_selected'] = self.
_widget.mark_check_box.isChecked()
274 self.
_options[
'hide_transitives'] = self.
_widget.hide_transitives_check_box.isChecked()
275 self.
_options[
'show_system'] = self.
_widget.show_system_check_box.isChecked()
277 self.
_options[
'colortheme'] =
True if self.
_widget.colorize_check_box.isChecked()
else None
278 self.
_options[
'names'] = self.
_widget.filter_line_edit.text().split(
',')
279 if self.
_options[
'names'] == [
u'None']:
281 self.
_options[
'highlight_level'] = \
282 3
if self.
_widget.highlight_connections_check_box.isChecked()
else 1
283 self.
_options[
'auto_fit'] = self.
_widget.auto_fit_graph_check_box.isChecked()
294 new_options_serialized = pickle.dumps(self.
_options)
299 self.
_scene.setBackgroundBrush(Qt.lightGray)
307 except Exception
as e:
308 print(str(type(e)), str(e), file=sys.stderr)
314 self.
_scene.setBackgroundBrush(Qt.white)
322 if name.strip().startswith(
'-'):
323 excludes.append(name.strip()[1:])
325 includes.append(name.strip())
329 if self.
_options[
'directions'] == 1:
331 if self.
_options[
'directions'] == 0:
335 selected_names=includes,
338 with_stacks=self.
_options[
'with_stacks'],
339 descendants=descendants,
341 mark_selected=self.
_options[
'mark_selected'],
342 colortheme=self.
_options[
'colortheme'],
343 hide_transitives=self.
_options[
'hide_transitives'],
344 show_system=self.
_options[
'show_system'],
345 hide_wet=self.
_options[
'package_types'] == 1,
346 hide_dry=self.
_options[
'package_types'] == 2)
355 if url
is not None and ':' in url:
356 item_type, item_path = url.split(
':', 1)
357 if item_type ==
'node':
358 tool_tip =
'Node:\n %s' % (item_path)
359 service_names = rosservice.get_service_list(node=item_path)
361 tool_tip +=
'\nServices:'
362 for service_name
in service_names:
364 service_type = rosservice.get_service_type(service_name)
365 tool_tip +=
'\n %s [%s]' % (service_name, service_type)
366 except rosservice.ROSServiceIOException
as e:
367 tool_tip +=
'\n %s' % (e)
369 elif item_type ==
'topic':
370 topic_type, topic_name, _ = rostopic.get_topic_type(item_path)
371 return 'Topic:\n %s\nType:\n %s' % (topic_name, topic_type)
376 for item
in self.
_scene.items():
377 self.
_scene.removeItem(item)
379 for node_item
in self.
_nodes.values():
380 self.
_scene.addItem(node_item)
381 for edge_items
in self.
_edges.values():
382 for edge_item
in edge_items:
383 edge_item.add_to_scene(self.
_scene)
385 self.
_scene.setSceneRect(self.
_scene.itemsBoundingRect())
390 if file_name
is None:
391 file_name, _ = QFileDialog.getOpenFileName(
392 self.
_widget, self.tr(
'Open graph from file'),
None, self.tr(
'DOT graph (*.dot)'))
393 if file_name
is None or file_name ==
'':
397 fh = open(file_name,
'rb')
404 self.
_widget.depth_combo_box.setEnabled(
False)
405 self.
_widget.directions_combo_box.setEnabled(
False)
406 self.
_widget.package_type_combo_box.setEnabled(
False)
407 self.
_widget.filter_line_edit.setEnabled(
False)
408 self.
_widget.with_stacks_check_box.setEnabled(
False)
409 self.
_widget.mark_check_box.setEnabled(
False)
410 self.
_widget.colorize_check_box.setEnabled(
False)
411 self.
_widget.hide_transitives_check_box.setEnabled(
False)
412 self.
_widget.show_system_check_box.setEnabled(
False)
419 self.
_widget.graphics_view.fitInView(self.
_scene.itemsBoundingRect(), Qt.KeepAspectRatio)
422 file_name, _ = QFileDialog.getSaveFileName(
423 self.
_widget, self.tr(
'Save as DOT'),
'rospackgraph.dot', self.tr(
'DOT graph (*.dot)'))
424 if file_name
is None or file_name ==
'':
427 handle = QFile(file_name)
428 if not handle.open(QIODevice.WriteOnly | QIODevice.Text):
435 file_name, _ = QFileDialog.getSaveFileName(
437 self.tr(
'Save as SVG'),
439 self.tr(
'Scalable Vector Graphic (*.svg)'))
440 if file_name
is None or file_name ==
'':
443 generator = QSvgGenerator()
444 generator.setFileName(file_name)
445 generator.setSize((self.
_scene.sceneRect().size() * 2.0).toSize())
447 painter = QPainter(generator)
448 painter.setRenderHint(QPainter.Antialiasing)
449 self.
_scene.render(painter)
453 file_name, _ = QFileDialog.getSaveFileName(
455 self.tr(
'Save as image'),
457 self.tr(
'Image (*.bmp *.jpg *.png *.tiff)'))
458 if file_name
is None or file_name ==
'':
462 (self.
_scene.sceneRect().size() * 2.0).toSize(),
463 QImage.Format_ARGB32_Premultiplied)
464 painter = QPainter(img)
465 painter.setRenderHint(QPainter.Antialiasing)
466 self.
_scene.render(painter)
472 self.
_widget.filter_line_edit.setText(
'')