Skip to content

Commit

Permalink
parse subgraphs recursively (#72)
Browse files Browse the repository at this point in the history
* parse subgraphs recursively

Before, only one subgraphlevel was added correctly, as pydot.get_subgraph_list
only returns the subgraphs at the current level.
I put the node and edge parsing into a separate function and made them recursive.

* removed debug output

This was only relevant for developing

* create unique names of edges

* added a test case for recursive subgraphs

* make less code appear as moved in the diff to kinetiv-devel

* move code to where it's needed

* check for the correct key

* removed unused variables and use update function of dictionaries

* minor style
  • Loading branch information
fmauch authored and dirk-thomas committed Jul 6, 2017
1 parent 40c51df commit 9f26286
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 7 deletions.
30 changes: 23 additions & 7 deletions qt_dotgraph/src/qt_dotgraph/dot_to_qt.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,13 @@ def addEdgeItem(self, edge, nodes, edges, highlight_level, same_label_siblings=F
edge_item.add_sibling_edge(sibling)
sibling.add_sibling_edge(edge_item)

if label not in edges:
edges[label] = []
edges[label].append(edge_item)
edge_name = source_node.strip('"\n"') + '_TO_' + destination_node.strip('"\n"')
if label is not None:
edge_name = edge_name + '_' + label

if edge_name not in edges:
edges[edge_name] = []
edges[edge_name].append(edge_item)

def dotcode_to_qt_items(self, dotcode, highlight_level, same_label_siblings=False):
"""
Expand All @@ -238,15 +242,20 @@ def dotcode_to_qt_items(self, dotcode, highlight_level, same_label_siblings=Fals
#graph = pygraphviz.AGraph(string=self._current_dotcode, strict=False, directed=True)
#graph.layout(prog='dot')

nodes = self.parse_nodes(graph, highlight_level)
edges = self.parse_edges(graph, nodes, highlight_level, same_label_siblings)
return nodes, edges

def parse_nodes(self, graph, highlight_level):
"""Recursively searches all nodes inside the graph and all subgraphs."""
# let pydot imitate pygraphviz api
graph.nodes_iter = graph.get_node_list
graph.edges_iter = graph.get_edge_list

graph.subgraphs_iter = graph.get_subgraph_list

nodes = {}
for subgraph in graph.subgraphs_iter():
subgraph_nodeitem = self.getNodeItemForSubgraph(subgraph, highlight_level)
nodes.update(self.parse_nodes(subgraph, highlight_level))
# skip subgraphs with empty bounding boxes
if subgraph_nodeitem is None:
continue
Expand All @@ -263,11 +272,18 @@ def dotcode_to_qt_items(self, dotcode, highlight_level, same_label_siblings=Fals
if node.get_name() in ('graph', 'node', 'empty'):
continue
nodes[node.get_name()] = self.getNodeItemForNode(node, highlight_level)
return nodes

edges = {}
def parse_edges(self, graph, nodes, highlight_level, same_label_siblings):
"""Recursively searches all edges inside the graph and all subgraphs."""
# let pydot imitate pygraphviz api
graph.subgraphs_iter = graph.get_subgraph_list
graph.edges_iter = graph.get_edge_list

edges = {}
for subgraph in graph.subgraphs_iter():
subgraph.edges_iter = subgraph.get_edge_list
edges.update(self.parse_edges(subgraph, nodes, highlight_level, same_label_siblings))
for edge in subgraph.edges_iter():
self.addEdgeItem(edge, nodes, edges,
highlight_level=highlight_level,
Expand All @@ -278,4 +294,4 @@ def dotcode_to_qt_items(self, dotcode, highlight_level, same_label_siblings=Fals
highlight_level=highlight_level,
same_label_siblings=same_label_siblings)

return nodes, edges
return edges
109 changes: 109 additions & 0 deletions qt_dotgraph/test/dot_to_qt_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,112 @@ def test_label_sizes(self):

def test_unquoted(self):
self.assertEqual("foo", get_unquoted({'bar': 'foo'}, 'bar'))

def test_recursive(self):
if DotToQtGeneratorTest._Q_APP is None:
raise unittest.case.SkipTest

gen = DotToQtGenerator()
dotcode = '''
strict digraph {
graph [bb="0,0,249,541",
compound=True,
rank=same,
rankdir=TB,
ranksep=0.2,
simplify=True
];
node [label="\N"];
subgraph "/Container" {
graph [bb="8,67,241,321",
color=None,
compound=True,
label="/Container",
lheight=0.21,
lp="124.5,309.5",
lwidth=0.81,
rank=same,
rankdir=TB,
ranksep=0.2,
style=bold
];
subgraph "/Container/Subcontainer" {
graph [bb="84,142,233,287",
color=None,
compound=True,
label="/Container/Subcontainer",
lheight=0.21,
lp="158.5,275.5",
lwidth=1.85,
rank=same,
rankdir=TB,
ranksep=0.2,
style=bold
];
"/Container/Subcontainer/logstate1" [height=0.5,
label=logstate1,
pos="133,235",
shape=box,
url=None,
width=0.90278];
"/Container/Subcontainer/finished" [color=blue,
height=0.5,
label=finished,
pos="133,168",
shape=ellipse,
url=None,
width=1.0833];
"/Container/Subcontainer/logstate1" -> "/Container/Subcontainer/finished" [label=done,
lp="146.5,201.5",
pos="e,133,186.19 133,216.92 133,210.7 133,203.5 133,196.6",
url=None];
}
"/Container/finished" [color=blue,
height=0.5,
label=finished,
pos="86,93",
shape=ellipse,
url=None,
width=1.0833];
"/Container/Subcontainer/finished" -> "/Container/finished" [label=finished,
lp="132,126.5",
pos="e,96.623,110.5 122.33,150.44 116.39,141.19 108.85,129.5 102.19,119.15",
url=None];
"/Container/logstate" [height=0.5,
label=logstate,
pos="46,168",
shape=box,
url=None,
width=0.81944];
"/Container/logstate" -> "/Container/finished" [label=done,
lp="82.5,126.5",
pos="e,74.304,110.45 53.482,149.8 57.712,140.5 63.287,128.93 69,119 69.051,118.91 69.102,118.82 69.153,118.74",
url=None];
}
"/finished" [height=0.5,
pos="86,18",
width=1.1555];
"/Container/finished" -> "/finished" [label=finished,
lp="108,51.5",
pos="e,86,36.176 86,74.7 86,66.245 86,55.869 86,46.373",
url=None];
"/start" -> "/Container/Subcontainer/logstate1" [
lp="146.5,436.5",
pos="e,133,250.01 133,355.84 133,337.5 133,316.81 133,260.22",
url=None];
"/start" -> "/Container/logstate" [
lp="146.5,436.5",
pos="e,46,185.01 133,355.84 46,337.5 46,316.81 46,192.22",
url=None];
"/start" [height=0.5,
pos="133,373",
width=0.79437];
}
'''

(nodes, edges) = gen.dotcode_to_qt_items(dotcode, 1)

expected_nodes = ['"/Container/Subcontainer"', '"/Container/finished"', '"/start"', '"/Container"', '"/Container/Subcontainer/logstate1"', '"/Container/Subcontainer/finished"', '"/Container/logstate"', '"/finished"']
expected_edges = ['/Container/logstate_TO_/Container/finished_done', '/Container/Subcontainer/finished_TO_/Container/finished_finished', '/start_TO_/Container/Subcontainer/logstate1', '/Container/finished_TO_/finished_finished', '/start_TO_/Container/logstate', '/Container/Subcontainer/logstate1_TO_/Container/Subcontainer/finished_done']
self.assertEqual(expected_nodes, nodes.keys())
self.assertEqual(expected_edges, edges.keys())

0 comments on commit 9f26286

Please sign in to comment.