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(
'')