Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
TASTE
OpenGEODE
Commits
46f5625c
Commit
46f5625c
authored
Oct 30, 2015
by
Maxime Perrotin
Browse files
Improve statechart renderer
Added support for the state return node, and removed overlapping of edges
parent
2f867d3d
Changes
1
Hide whitespace changes
Inline
Side-by-side
opengeode/Statechart.py
View file @
46f5625c
...
...
@@ -46,7 +46,7 @@ LOG = logging.getLogger(__name__)
class
Record
(
genericSymbols
.
HorizontalSymbol
,
object
):
''' Graphviz node - it is floating and has no parent'''
_unique_followers
=
[]
_insertable_followers
=
[
'Record'
,
'Diamond'
]
_insertable_followers
=
[
'Record'
,
'Diamond'
,
'Stop'
]
_terminal_followers
=
[]
textbox_alignment
=
(
QtCore
.
Qt
.
AlignTop
|
QtCore
.
Qt
.
AlignHCenter
)
...
...
@@ -94,7 +94,7 @@ class Record(genericSymbols.HorizontalSymbol, object):
class
Point
(
genericSymbols
.
HorizontalSymbol
,
object
):
''' Graphviz point node - used for START transition'''
_unique_followers
=
[]
_insertable_followers
=
[
'Record'
,
'Diamond'
]
_insertable_followers
=
[
'Record'
,
'Diamond'
,
'Stop'
]
_terminal_followers
=
[]
textbox_alignment
=
(
QtCore
.
Qt
.
AlignTop
|
QtCore
.
Qt
.
AlignHCenter
)
...
...
@@ -136,7 +136,7 @@ class Point(genericSymbols.HorizontalSymbol, object):
class
Diamond
(
genericSymbols
.
HorizontalSymbol
,
object
):
''' Graphviz node - it is floating and has no parent'''
_unique_followers
=
[]
_insertable_followers
=
[
'Record'
]
_insertable_followers
=
[
'Record'
,
'Stop'
]
_terminal_followers
=
[]
textbox_alignment
=
(
QtCore
.
Qt
.
AlignTop
|
QtCore
.
Qt
.
AlignHCenter
)
...
...
@@ -175,6 +175,47 @@ class Diamond(genericSymbols.HorizontalSymbol, object):
pass
# pylint: disable=R0904
class
Stop
(
genericSymbols
.
HorizontalSymbol
,
object
):
''' Graphviz state exit node - it is floating and has no parent'''
_unique_followers
=
[]
_insertable_followers
=
[]
_terminal_followers
=
[]
textbox_alignment
=
(
QtCore
.
Qt
.
AlignTop
|
QtCore
.
Qt
.
AlignHCenter
)
has_text_area
=
True
def
__init__
(
self
,
node
,
graph
):
''' Initialization: compute the polygon shape '''
self
.
name
=
node
[
'name'
]
super
(
Stop
,
self
).
__init__
(
x
=
node
[
'pos'
][
0
],
y
=
node
[
'pos'
][
1
],
text
=
self
.
name
)
self
.
set_shape
(
node
[
'width'
],
node
[
'height'
])
self
.
graph
=
graph
def
set_shape
(
self
,
width
,
height
):
''' Define the polygon shape from width and height '''
path
=
QtGui
.
QPainterPath
()
path
.
lineTo
(
width
,
height
)
path
.
moveTo
(
width
,
0
)
path
.
lineTo
(
0
,
height
)
self
.
setPath
(
path
)
super
(
Stop
,
self
).
set_shape
(
width
,
height
)
def
resize_item
(
self
,
_
):
''' Redefine the resizing function - forbid resizing '''
pass
def
__str__
(
self
):
''' User-friendly information about the node '''
return
(
'Stop node '
+
self
.
name
+
' at pos '
+
str
(
self
.
pos
())
+
' bounding rect = '
+
str
(
self
.
boundingRect
()))
def
mouse_release
(
self
,
_
):
''' After moving item, ask dot to recompute the edges '''
pass
def
edges
(
scene
,
node
):
''' Return all edges of a given node '''
for
item
in
scene
.
items
():
...
...
@@ -304,16 +345,18 @@ def update(scene):
Parse the graph symbols and create a graphviz graph
Used to update the edges in case user moves nodes.
'''
print
'update'
nodes
=
[{
'name'
:
node
.
name
,
'pos'
:
node
.
mapToScene
(
node
.
boundingRect
().
center
()),
'shape'
:
type
(
node
),
'width'
:
node
.
boundingRect
().
width
(),
'height'
:
node
.
boundingRect
().
height
()}
for
node
in
scene
.
visible_symb
if
isinstance
(
node
,
(
Point
,
Diamond
,
Record
))]
(
Point
,
Diamond
,
Record
,
Stop
))]
graph
=
dotgraph
.
AGraph
(
strict
=
False
,
directed
=
True
,
splines
=
'spline'
,
start
=
'rand'
)
lookup
=
{
Point
:
'point'
,
Record
:
'record'
,
Diamond
:
'diamond'
}
lookup
=
{
Point
:
'point'
,
Record
:
'record'
,
Diamond
:
'diamond'
,
Stop
:
'plaintext'
}
for
node
in
nodes
:
center_pos
=
node
[
'pos'
]
bb_height
=
scene
.
itemsBoundingRect
().
height
()
...
...
@@ -326,7 +369,7 @@ def update(scene):
pos
=
unicode
(
'{x},{y}'
.
format
(
x
=
float
(
center_x
),
y
=
float
(
center_y
)))
if
node
[
'shape'
]
in
(
Point
,
Diamond
):
if
node
[
'shape'
]
in
(
Point
,
Diamond
,
Stop
):
graph
.
add_node
(
node
[
'name'
],
pos
=
pos
,
shape
=
lookup
[
node
[
'shape'
]],
fixedsize
=
'true'
,
width
=
node
[
'width'
]
/
RENDER_DPI
[
'X'
],
height
=
node
[
'height'
]
/
RENDER_DPI
[
'Y'
],
pin
=
True
)
...
...
@@ -373,7 +416,8 @@ def render_statechart(scene, graphtree=None, keep_pos=False, dump_gfx=''):
graphtree
[
'children'
][
aname
][
'scene'
]
=
temp_scene
break
# Statechart symbols lookup table
lookup
=
{
'point'
:
Point
,
'record'
:
Record
,
'diamond'
:
Diamond
}
lookup
=
{
'point'
:
Point
,
'record'
:
Record
,
'diamond'
:
Diamond
,
'plaintext'
:
Stop
}
try
:
# Bonus: the tool can render any dot graph...
graph
=
graphtree
.
get
(
'graph'
,
None
)
or
dotgraph
.
AGraph
(
'taste.dot'
)
...
...
@@ -399,7 +443,7 @@ def render_statechart(scene, graphtree=None, keep_pos=False, dump_gfx=''):
graph
.
layout
(
prog
=
'neato'
,
args
=
'-Nfontsize=12, -Efontsize=8 '
'-Gsplines=curved -Gsep=1 '
'-Gstart=random10 -Goverlap=
f
al
s
e '
'-Gstart=random10 -Goverlap=
sc
ale '
'-Nstyle=rounded -Nshape=record -Elen=1 {kp} {dump}'
.
format
(
kp
=
'-n1'
if
keep_pos
else
''
,
dump
=
(
'-Tpng -o'
+
dump_gfx
)
if
dump_gfx
else
''
))
...
...
@@ -424,7 +468,7 @@ def render_statechart(scene, graphtree=None, keep_pos=False, dump_gfx=''):
shape
=
node
.
get
(
'shape'
)
try
:
node_symbol
=
lookup
[
shape
](
node
,
graph
)
if
graphtree
[
'children'
]:
if
graphtree
[
'children'
]
and
shape
==
'record'
:
node_symbol
.
setBrush
(
QtGui
.
QBrush
(
QtGui
.
QColor
(
249
,
249
,
249
)))
G_SYMBOLS
.
add
(
node_symbol
)
node_symbols
.
append
(
node_symbol
)
...
...
@@ -467,7 +511,6 @@ def create_dot_graph(root_ast, basic=False):
graph
.
add_node
(
state
,
label
=
''
,
shape
=
'point'
,
fixedsize
=
'true'
,
width
=
10.0
/
72.0
)
else
:
#print 'adding', state
graph
.
add_node
(
state
,
label
=
state
,
shape
=
'record'
,
style
=
'rounded'
)
# for each in root_ast.composite_states:
# # this will have to be recursive
...
...
@@ -476,6 +519,11 @@ def create_dot_graph(root_ast, basic=False):
# graph.add_subgraph(subnodes, name='cluster_' + each.statename.lower(),
# label=each.statename.lower(),
# style='rounded', shape='record')
for
each
in
[
term
for
term
in
root_ast
.
terminators
if
term
.
kind
==
'return'
]:
# create a new node for each RETURN statement (in nested states)
ident
=
each
.
inputString
or
'default'
graph
.
add_node
(
ident
,
label
=
ident
,
shape
=
'plaintext'
)
for
state
,
inputs
in
root_ast
.
mapping
.
viewitems
():
# Add edges
transitions
=
\
...
...
@@ -503,9 +551,9 @@ def create_dot_graph(root_ast, basic=False):
label
=
''
def
find_terminators
(
trans
):
''' Recursively find all NEXTSTATES '''
''' Recursively find all NEXTSTATES
and RETURN nodes
'''
next_states
=
[
term
for
term
in
trans
.
terminators
if
term
.
kind
==
'next_state'
]
if
term
.
kind
in
(
'next_state'
,
'return'
)
]
joins
=
[
term
for
term
in
trans
.
terminators
if
term
.
kind
==
'join'
]
for
join
in
joins
:
...
...
@@ -546,12 +594,7 @@ def create_dot_graph(root_ast, basic=False):
if
term
.
inputString
.
strip
()
==
'-'
:
target
=
state
else
:
target
=
term
.
inputString
.
lower
()
# for each in root_ast.composite_states:
# # check with deeper nesting
# if each.statename.lower() == target.lower():
# target = 'cluster_' + target
# break
target
=
term
.
inputString
.
lower
()
or
'default'
if
basic
:
target_states
[
target
].
add
(
label
)
else
:
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment