Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

parse subgraphs recursively #72

Merged
43 changes: 36 additions & 7 deletions qt_dotgraph/src/qt_dotgraph/dot_to_qt.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,9 +236,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 @@ -256,15 +260,28 @@ 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

num_subgraphs = len(graph.get_subgraph_list())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variable is not used anywhere?


nodes = {}
for subgraph in graph.subgraphs_iter():
subgraph_nodeitem = self.getNodeItemForSubgraph(subgraph, highlight_level)
num_subgraphs = len(subgraph.get_subgraph_list())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variable is not used anywhere?


nodes = dict(nodes, **self.parse_nodes(subgraph, highlight_level))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the following would be more readable and make the intention of the line clearer:

nodes.update(...)

Same below for edges.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dirk-thomas thanks for the really thorough review. I included the mentioned changes.


# skip subgraphs with empty bounding boxes
if subgraph_nodeitem is None:
continue
Expand All @@ -281,11 +298,23 @@ 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 = dict(edges, **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 @@ -295,5 +324,5 @@ def dotcode_to_qt_items(self, dotcode, highlight_level, same_label_siblings=Fals
self.addEdgeItem(edge, nodes, edges,
highlight_level=highlight_level,
same_label_siblings=same_label_siblings)
return edges

return nodes, 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 @@ -99,3 +99,112 @@ def test_simple_integration(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())