Commit 4ae90c89 authored by Maxime Perrotin's avatar Maxime Perrotin
Browse files

Prepare statechart renderer for an upgrade

...to support properly state aggregation and composition.
Clustering of graphs has to be handled manually because of graphviz
limitations.
parent 04cd549c
...@@ -2302,7 +2302,6 @@ def path_type(path): ...@@ -2302,7 +2302,6 @@ def path_type(path):
continue continue
# Sequence, Choice (case insensitive) # Sequence, Choice (case insensitive)
if current.kind in ('SequenceType', 'ChoiceType'): if current.kind in ('SequenceType', 'ChoiceType'):
#print list(Helper.sorted_fields(current))
elem_asn1 = elem.replace('_', '-').lower() elem_asn1 = elem.replace('_', '-').lower()
type_idx, = (c for c in current.Children type_idx, = (c for c in current.Children
if c.lower() == elem_asn1) if c.lower() == elem_asn1)
......
...@@ -356,12 +356,14 @@ def update(scene): ...@@ -356,12 +356,14 @@ def update(scene):
def render_statechart(scene, graph=None, keep_pos=False, dump_gfx=''): def render_statechart(scene, graph=None, keep_pos=False, dump_gfx=''):
''' Render a graphviz/dot statechart on the QGraphicsScene ''' Render a graphviz/dot statechart on the QGraphicsScene
set a filename to "dump_gfx" parameter to create a PNG of the graph set a filename to "dump_gfx" parameter to create a PNG of the graph
input is resulting from sdl_to_statechart, it contains a tree of graphs
in case of composite states.
''' '''
# Statechart symbols lookup table # Statechart symbols lookup table
lookup = {'point': Point, 'record': Record, 'diamond': Diamond} lookup = {'point': Point, 'record': Record, 'diamond': Diamond}
try: try:
# Bonus: the tool can render any dot graph... # Bonus: the tool can render any dot graph...
graph = graph or dotgraph.AGraph('taste.dot') graph = graph.get('graph', None) or dotgraph.AGraph('taste.dot')
except IOError: except IOError:
LOG.info('No statechart to display....') LOG.info('No statechart to display....')
raise raise
...@@ -375,7 +377,7 @@ def render_statechart(scene, graph=None, keep_pos=False, dump_gfx=''): ...@@ -375,7 +377,7 @@ def render_statechart(scene, graph=None, keep_pos=False, dump_gfx=''):
# Compute all the coordinates (self-modifying function) # Compute all the coordinates (self-modifying function)
# Force the fontsize of the nodes to be 12, as in OpenGEODE # Force the fontsize of the nodes to be 12, as in OpenGEODE
# use -n2 below to keep user-specified node coordinates # use -n1 below to keep user-specified node coordinates
if dump_gfx: if dump_gfx:
dump_name = 'sc_' + os.path.basename(dump_gfx) dump_name = 'sc_' + os.path.basename(dump_gfx)
dump_gfx = os.path.dirname(dump_gfx) or '.' + os.sep + dump_name dump_gfx = os.path.dirname(dump_gfx) or '.' + os.sep + dump_name
...@@ -427,6 +429,7 @@ def create_dot_graph(root_ast, basic=False): ...@@ -427,6 +429,7 @@ def create_dot_graph(root_ast, basic=False):
between two states and no diamond nodes between two states and no diamond nodes
''' '''
graph = dotgraph.AGraph(strict=False, directed=True) graph = dotgraph.AGraph(strict=False, directed=True)
ret = {'graph': graph, 'children': {}}
diamond = 0 diamond = 0
for state in root_ast.mapping.viewkeys(): for state in root_ast.mapping.viewkeys():
# create a new node for each state (including nested states) # create a new node for each state (including nested states)
...@@ -436,19 +439,18 @@ def create_dot_graph(root_ast, basic=False): ...@@ -436,19 +439,18 @@ def create_dot_graph(root_ast, basic=False):
else: else:
#print 'adding', state #print 'adding', state
graph.add_node(state, label=state, shape='record', style='rounded') graph.add_node(state, label=state, shape='record', style='rounded')
for each in root_ast.composite_states: # for each in root_ast.composite_states:
# this will have to be recursive # # this will have to be recursive
subnodes = (name for name in graph.iternodes() # subnodes = (name for name in graph.iternodes()
if name.startswith(each.statename.lower() + '_')) # if name.startswith(each.statename.lower() + '_'))
graph.add_subgraph(subnodes, name='cluster_' + each.statename.lower(), # graph.add_subgraph(subnodes, name='cluster_' + each.statename.lower(),
label=each.statename.lower(), # label=each.statename.lower(),
style='rounded', shape='record') # style='rounded', shape='record')
for state, inputs in root_ast.mapping.viewitems(): for state, inputs in root_ast.mapping.viewitems():
# Add edges # Add edges
transitions = \ transitions = \
inputs if not state.endswith('START') \ inputs if not state.endswith('START') \
else [root_ast.transitions[inputs]] else [root_ast.transitions[inputs]]
#[root_ast.content.start]
# Allow simplified graph, without diamonds and with at most one # Allow simplified graph, without diamonds and with at most one
# transition from a given state to another # transition from a given state to another
target_states = defaultdict(set) target_states = defaultdict(set)
...@@ -505,11 +507,11 @@ def create_dot_graph(root_ast, basic=False): ...@@ -505,11 +507,11 @@ def create_dot_graph(root_ast, basic=False):
target = state target = state
else: else:
target = term.inputString.lower() target = term.inputString.lower()
for each in root_ast.composite_states: # for each in root_ast.composite_states:
# check with deeper nesting # # check with deeper nesting
if each.statename.lower() == target.lower(): # if each.statename.lower() == target.lower():
target = 'cluster_' + target # target = 'cluster_' + target
break # break
if basic: if basic:
target_states[target].add(label) target_states[target].add(label)
else: else:
...@@ -517,9 +519,12 @@ def create_dot_graph(root_ast, basic=False): ...@@ -517,9 +519,12 @@ def create_dot_graph(root_ast, basic=False):
for target, labels in target_states.viewitems(): for target, labels in target_states.viewitems():
# Basic mode # Basic mode
graph.add_edge(source, target, label=',\n'.join(labels)) graph.add_edge(source, target, label=',\n'.join(labels))
# with open('statechart.dot', 'w') as output: # with open('statechart.dot', 'w') as output:
# output.write(graph.to_string()) # output.write(graph.to_string())
return graph #return graph
for each in root_ast.composite_states:
ret['children'][each.statename] = create_dot_graph(each, basic)
return ret
if __name__ == '__main__': if __name__ == '__main__':
......
...@@ -894,8 +894,10 @@ class SDL_Scene(QtGui.QGraphicsScene, object): ...@@ -894,8 +894,10 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
except ValueError: except ValueError:
LOG.error('No statechart to render') LOG.error('No statechart to render')
return None return None
# Flatten nested states # Flatten nested states (no, because neato does not support it,
Helper.flatten(process_ast) # dot supports only vertically-aligned states, and fdp does not
# support curved edges and is buggy with pygraphviz anyway)
# Helper.flatten(process_ast)
return Statechart.create_dot_graph(process_ast, basic) return Statechart.create_dot_graph(process_ast, basic)
...@@ -2058,7 +2060,8 @@ class OG_MainWindow(QtGui.QMainWindow, object): ...@@ -2058,7 +2060,8 @@ class OG_MainWindow(QtGui.QMainWindow, object):
scene = self.view.scene() scene = self.view.scene()
graph = scene.sdl_to_statechart() graph = scene.sdl_to_statechart()
try: try:
Statechart.render_statechart(self.statechart_scene, graph) Statechart.render_statechart(self.statechart_scene,
graph)
self.statechart_view.refresh() self.statechart_view.refresh()
except (IOError, TypeError) as err: except (IOError, TypeError) as err:
LOG.debug(str(err)) LOG.debug(str(err))
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment