def test_add_subgraph_escape_name(self): fac = PygraphvizFactory() g = fac.get_graph() fac.add_subgraph_to_graph(g, 'graph') self.assertEqual(1, len(g.subgraphs())) self.assertEqual('cluster_graph', g.subgraphs()[0].get_name()) self.assertEqual('graph', g.subgraphs()[0].graph_attr['label'])
def test_add_node_escape_name(self): fac = PygraphvizFactory() g = fac.get_graph() fac.add_node_to_graph(g, 'graph') self.assertEqual(1, len(g.nodes())) self.assertEqual('graph', g.nodes()[0].get_name()) self.assertEqual('graph', g.nodes()[0].attr['label'])
def test_add_edge(self): fac = PygraphvizFactory() g = fac.get_graph() fac.add_node_to_graph(g, 'foo') fac.add_node_to_graph(g, 'bar') fac.add_edge_to_graph(g, 'foo', 'bar') self.assertEqual(2, len(g.nodes())) self.assertEqual(1, len(g.edges())) self.assertEqual('foo', g.edges()[0][0]) self.assertEqual('bar', g.edges()[0][1])
def test_create_dot(self): fac = PygraphvizFactory() g = fac.get_graph() fac.add_node_to_graph(g, 'foo') fac.add_node_to_graph(g, 'edge') fac.add_edge_to_graph(g, 'foo', 'edge') fac.add_subgraph_to_graph(g, 'graph') snippets = ['strict digraph {\n\tgraph', 'foo', 'label=foo', '"edge"', 'label="edge"', 'foo -> "edge"'] result = fac.create_dot(g) for sn in snippets: self.assertTrue(sn in result, '%s \nmissing in\n %s' % (sn, result))
def test_get_graph(self): fac = PygraphvizFactory() g = fac.get_graph() self.assertEquals('same', g.graph_attr['rank']) self.assertTrue(g.is_directed())
def __init__(self, context): super(RosBehaviourTree, self).__init__(context) self.setObjectName('RosBehaviourTree') parser = argparse.ArgumentParser() RosBehaviourTree.add_arguments(parser, False) # if the context doesn't have an argv attribute then assume we're running with --no-roscore if not hasattr(context, 'argv'): args = sys.argv[1:] # Can run the viewer with or without live updating. Running without is # intended for viewing of bags only self.live_update = False else: args = context.argv() self.live_update = True parsed_args = parser.parse_args(args) self.context = context self.initialized = False self._current_dotcode = None # dotcode for the tree that is currently displayed self._viewing_bag = False # true if a bag file is loaded # True if next or previous buttons are pressed. Reset if the tree being # viewed is the last one in the list. self._browsing_timeline = False self._widget = QWidget() # factory builds generic dotcode items self.dotcode_factory = PygraphvizFactory() # PydotFactory() # self.dotcode_factory = PygraphvizFactory() # generator builds rosgraph self.dotcode_generator = RosBehaviourTreeDotcodeGenerator() self.current_topic = None self.behaviour_sub = None self._tip_message = None # message of the tip of the tree self._saved_settings_topic = None # topic subscribed to by previous instance self.visibility_level = py_trees.common.VisibilityLevel.DETAIL # dot_to_qt transforms into Qt elements using dot layout self.dot_to_qt = DotToQtGenerator() rp = rospkg.RosPack() ui_file = os.path.join(rp.get_path('rqt_py_trees'), 'resource', 'RosBehaviourTree.ui') loadUi(ui_file, self._widget, {'InteractiveGraphicsView': InteractiveGraphicsView}) self._widget.setObjectName('RosBehaviourTreeUi') if hasattr(context, 'serial_number') and context.serial_number() > 1: self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % context.serial_number())) self._scene = QGraphicsScene() self._scene.setBackgroundBrush(Qt.white) self._widget.graphics_view.setScene(self._scene) self._widget.highlight_connections_check_box.toggled.connect( self._redraw_graph_view) self._widget.auto_fit_graph_check_box.toggled.connect( self._redraw_graph_view) self._widget.fit_in_view_push_button.setIcon( QIcon.fromTheme('zoom-original')) self._widget.fit_in_view_push_button.pressed.connect(self._fit_in_view) self._widget.load_bag_push_button.setIcon( QIcon.fromTheme('document-open')) self._widget.load_bag_push_button.pressed.connect(self._load_bag) self._widget.load_dot_push_button.setIcon( QIcon.fromTheme('document-open')) self._widget.load_dot_push_button.pressed.connect(self._load_dot) self._widget.save_dot_push_button.setIcon( QIcon.fromTheme('document-save-as')) self._widget.save_dot_push_button.pressed.connect(self._save_dot) self._widget.save_as_svg_push_button.setIcon( QIcon.fromTheme('document-save-as')) self._widget.save_as_svg_push_button.pressed.connect(self._save_svg) self._widget.save_as_image_push_button.setIcon( QIcon.fromTheme('image')) self._widget.save_as_image_push_button.pressed.connect( self._save_image) for text in visibility.combo_to_py_trees: self._widget.visibility_level_combo_box.addItem(text) self._widget.visibility_level_combo_box.setCurrentIndex( self.visibility_level) self._widget.visibility_level_combo_box.currentIndexChanged[ 'QString'].connect(self._update_visibility_level) # set up the function that is called whenever the box is resized - # ensures that the timeline is correctly drawn. self._widget.resizeEvent = self._resize_event self._timeline = None self._timeline_listener = None # Connect the message changed function of this object to a corresponding # signal. This signal will be activated whenever the message being # viewed changes. self._message_changed.connect(self.message_changed) self._message_cleared.connect(self.message_cleared) # Set up combo box for topic selection # when the refresh_combo signal happens, update the combo topics available self._refresh_combo.connect(self._update_combo_topics) # filter events to catch the event which opens the combo box self._combo_event_filter = RosBehaviourTree.ComboBoxEventFilter( self._refresh_combo) self._widget.topic_combo_box.installEventFilter( self._combo_event_filter) self._widget.topic_combo_box.activated.connect(self._choose_topic) self._update_combo_topics() # Set up navigation buttons self._widget.previous_tool_button.pressed.connect(self._previous) self._widget.previous_tool_button.setIcon( QIcon.fromTheme('go-previous')) self._widget.next_tool_button.pressed.connect(self._next) self._widget.next_tool_button.setIcon(QIcon.fromTheme('go-next')) self._widget.first_tool_button.pressed.connect(self._first) self._widget.first_tool_button.setIcon(QIcon.fromTheme('go-first')) self._widget.last_tool_button.pressed.connect(self._last) self._widget.last_tool_button.setIcon(QIcon.fromTheme('go-last')) # play, pause and stop buttons self._widget.play_tool_button.pressed.connect(self._play) self._widget.play_tool_button.setIcon( QIcon.fromTheme('media-playback-start')) self._widget.stop_tool_button.pressed.connect(self._stop) self._widget.stop_tool_button.setIcon( QIcon.fromTheme('media-playback-stop')) # also connect the navigation buttons so that they stop the timer when # pressed while the tree is playing. self._widget.first_tool_button.pressed.connect(self._stop) self._widget.previous_tool_button.pressed.connect(self._stop) self._widget.last_tool_button.pressed.connect(self._stop) self._widget.next_tool_button.pressed.connect(self._stop) # set up shortcuts for navigation (vim) next_shortcut_vi = QShortcut(QKeySequence("l"), self._widget) next_shortcut_vi.activated.connect( self._widget.next_tool_button.pressed) previous_shortcut_vi = QShortcut(QKeySequence("h"), self._widget) previous_shortcut_vi.activated.connect( self._widget.previous_tool_button.pressed) first_shortcut_vi = QShortcut(QKeySequence("^"), self._widget) first_shortcut_vi.activated.connect( self._widget.first_tool_button.pressed) last_shortcut_vi = QShortcut(QKeySequence("$"), self._widget) last_shortcut_vi.activated.connect( self._widget.last_tool_button.pressed) # shortcuts for emacs next_shortcut_emacs = QShortcut(QKeySequence("Ctrl+f"), self._widget) next_shortcut_emacs.activated.connect( self._widget.next_tool_button.pressed) previous_shortcut_emacs = QShortcut(QKeySequence("Ctrl+b"), self._widget) previous_shortcut_emacs.activated.connect( self._widget.previous_tool_button.pressed) first_shortcut_emacs = QShortcut(QKeySequence("Ctrl+a"), self._widget) first_shortcut_emacs.activated.connect( self._widget.first_tool_button.pressed) last_shortcut_emacs = QShortcut(QKeySequence("Ctrl+e"), self._widget) last_shortcut_emacs.activated.connect( self._widget.last_tool_button.pressed) # set up stuff for dotcode cache self._dotcode_cache_capacity = 50 self._dotcode_cache = {} # cache is ordered on timestamps from messages, but earliest timestamp # isn't necessarily the message that was viewed the longest time ago, so # need to store keys self._dotcode_cache_keys = [] # set up stuff for scene cache (dotcode cache doesn't seem to make much difference) self._scene_cache_capacity = 50 self._scene_cache = {} self._scene_cache_keys = [] # Update the timeline buttons to correspond with a completely # uninitialised state. self._set_timeline_buttons(first_snapshot=False, previous_snapshot=False, next_snapshot=False, last_snapshot=False) self._deferred_fit_in_view.connect(self._fit_in_view, Qt.QueuedConnection) self._deferred_fit_in_view.emit() # This is used to store a timer which controls how fast updates happen when the play button is pressed. self._play_timer = None # updates the view self._refresh_view.connect(self._refresh_tree_graph) self._force_refresh = False if self.live_update: context.add_widget(self._widget) else: self.initialized = True # this needs to be set for trees to be displayed context.setCentralWidget(self._widget) if parsed_args.bag: self._load_bag(parsed_args.bag) elif parsed_args.latest_bag: # if the latest bag is requested, load it from the default directory, or # the one specified in the args bag_dir = parsed_args.bag_dir or os.getenv( 'ROS_HOME', os.path.expanduser('~/.ros')) + '/behaviour_trees' self.open_latest_bag(bag_dir, parsed_args.by_time)
def test_create_dot(self): fac = PygraphvizFactory() g = fac.get_graph() fac.add_node_to_graph(g, 'foo') fac.add_node_to_graph(g, 'edge') fac.add_edge_to_graph(g, 'foo', 'edge') fac.add_subgraph_to_graph(g, 'graph') snippets = [ 'strict digraph { graph', 'foo', 'label=foo', '"edge"', 'label="edge"', 'foo -> "edge"' ] result = fac.create_dot(g) # get rid of version specific quotes / whitespaces result = re.sub('""', ' ', result) result = re.sub('[\n\t ]+', ' ', result) for sn in snippets: self.assertTrue(sn in result, '%s \nmissing in\n %s' % (sn, result))