Commit 48af1941 authored by Maxime Perrotin's avatar Maxime Perrotin
Browse files

Allow configuration of statecharts

parent 28cb9527
......@@ -22,6 +22,7 @@
<file>icons/texture.png</file>
<file>opengeode.ui</file>
<file>hyperlink.ui</file>
<file>statechart_cfg.ui</file>
<file>fonts/Ubuntu-B.ttf</file>
<file>fonts/Ubuntu-BI.ttf</file>
<file>fonts/Ubuntu-R.ttf</file>
......
......@@ -22,10 +22,17 @@
import os
import logging
from collections import defaultdict
from functools import partial
from itertools import chain
import re
from PySide import QtGui, QtCore
# import resource file to get the configuration widget
from PySide.QtUiTools import QUiLoader
import icons
g_statechart_lock = False
try:
import pygraphviz as dotgraph
except ImportError:
......@@ -494,10 +501,6 @@ def render_statechart(scene, graphtree=None, keep_pos=False, dump_gfx=''):
if dump_gfx.split('.')[-1].lower() != 'png':
dump_gfx += '.png'
# graph.layout(prog='neato', args='-Nfontsize=12, -Efontsize=8 '
# '-Gsplines=curved -Gsep=0.3 -Gdpi=72 '
# '-Gstart=random10 -Goverlap=scale '
# '-Nstyle=rounded -Nshape=record -Elen=1 {kp} {dump}'
graph.layout(prog='neato', args='{cfg} {kp} {dump}'
.format(cfg=config, kp='-n1' if keep_pos else '',
dump=('-Tpng -o' + dump_gfx) if dump_gfx else ''))
......@@ -551,7 +554,22 @@ def render_statechart(scene, graphtree=None, keep_pos=False, dump_gfx=''):
each.setZValue(each.zValue() + symb.zValue() + 1)
def create_dot_graph(root_ast, basic=False):
def lock():
''' Prevent multiple callers to render at the same time '''
global g_statechart_lock
g_statechart_lock = True
def unlock():
''' Prevent multiple callers to render at the same time '''
global g_statechart_lock
g_statechart_lock = False
def locked():
''' Return the lock status '''
return g_statechart_lock
def create_dot_graph(root_ast, basic=False, scene=None):
''' Return a dot.AGraph item, from an ogAST.Process or child entry
Set basic=True to generate a simple graph with at most one edge
between two states and no diamond nodes
......@@ -559,10 +577,14 @@ def create_dot_graph(root_ast, basic=False):
graph = dotgraph.AGraph(strict=False, directed=True)
ret = {'graph': graph, 'children': {}, 'config': {}}
diamond = 0
input_signals = {sig['name'].lower() for sig in root_ast.input_signals}
# XXX misses the timers
# valid_inputs: list of messages to be displayed in the statecharts
# user can remove them from the file to make cleaner diagrams
# config_params can be set to tune the call to graphviz
valid_inputs = []
valid_inputs = set()
config_params = {}
inputs_to_save = set()
identifier = getattr(root_ast, "statename", root_ast.processName)
......@@ -573,10 +595,10 @@ def create_dot_graph(root_ast, basic=False):
split = each.split()
if len(split) == 3 and split[0] == "cfg":
config_params[split[1]] = split[2]
else:
valid_inputs.append(each)
elif each:
valid_inputs.add(each.lower())
except IOError:
valid_inputs = None
valid_inputs = input_signals
config_params = {"-Nfontsize" : "12",
"-Efontsize" : "8",
"-Gsplines" : "curved",
......@@ -590,6 +612,41 @@ def create_dot_graph(root_ast, basic=False):
else:
LOG.info ("Statechart settings read from configuration file")
if scene:
# Load and display a table for the user to filter out messages that
# are not relevant to display on the statechart - and make it lighter
# Repeat for substates, too.
lock()
def right(leftList, rightList):
for each in leftList.selectedItems():
item = leftList.takeItem(leftList.row(each))
rightList.addItem(item)
def left(leftList, rightList):
for each in rightList.selectedItems():
item = rightList.takeItem(rightList.row(each))
leftList.addItem(item)
loader = QUiLoader()
ui_file = QtCore.QFile(":/statechart_cfg.ui")
ui_file.open(QtCore.QFile.ReadOnly)
dialog = loader.load(ui_file)
dialog.setParent (scene.views()[0], QtCore.Qt.Dialog)
okButton = dialog.findChild(QtGui.QPushButton, "okButton")
rightButton = dialog.findChild(QtGui.QToolButton, "toRight")
leftButton = dialog.findChild(QtGui.QToolButton, "toLeft")
rightList = dialog.findChild(QtGui.QListWidget, "rightList")
leftList = dialog.findChild(QtGui.QListWidget, "leftList")
okButton.pressed.connect(dialog.accept)
rightButton.pressed.connect(partial(right, leftList, rightList))
leftButton.pressed.connect(partial(left, leftList, rightList))
ui_file.close()
rightList.addItems(list(valid_inputs))
leftList.addItems(list(input_signals - valid_inputs))
go = dialog.exec_()
valid_inputs.clear()
for idx in xrange(rightList.count()):
valid_inputs.add(rightList.item(idx).text())
unlock()
for state in root_ast.mapping.viewkeys():
# create a new node for each state (including nested states)
if state.endswith('START'):
......@@ -666,9 +723,9 @@ def create_dot_graph(root_ast, basic=False):
fixedsize='true',
width=15.0 / 72.0,
height=15.0 / 72.0, label='')
if valid_inputs is None or label in valid_inputs or not label:
if label.lower() in valid_inputs or not label.strip():
graph.add_edge(source, str(diamond), label=label)
inputs_to_save.add(label)
inputs_to_save.add(label.lower())
source = str(diamond)
label = ''
diamond += 1
......@@ -678,29 +735,34 @@ def create_dot_graph(root_ast, basic=False):
else:
target = term.inputString.lower() or ' '
if basic:
target_states[target].add(label)
elif valid_inputs is None or label in valid_inputs or not label:
graph.add_edge(source, target, label=label)
inputs_to_save.add(label)
target_states[target] |= set(label.split(','))
else:
labs = set(lab.strip() for lab in label.split(',') if
lab.strip().lower() in valid_inputs | {""})
actual = ',\n'.join(labs)
graph.add_edge(source, target, label=actual)
inputs_to_save |= set(lab.lower() for lab in labs)
for target, labels in target_states.viewitems():
sublab = [lab for lab in labels if valid_inputs is None or label in
valid_inputs]
sublab = [lab.strip() for lab in labels if
lab.strip().lower() in valid_inputs | {""}]
# Basic mode
if sublab:
graph.add_edge(source, target, label=',\n'.join(sublab))
inputs_to_save |= set(sublab)
inputs_to_save |= set(lab.lower() for lab in sublab)
# with open('statechart.dot', 'w') as output:
# output.write(graph.to_string())
#return graph
if valid_inputs is None:
with open(identifier + ".cfg", "w") as cfg_file:
for name, value in config_params.viewitems():
cfg_file.write("cfg {} {}\n".format(name, value))
for each in inputs_to_save:
cfg_file.write(each + "\n")
with open(identifier + ".cfg", "w") as cfg_file:
for name, value in config_params.viewitems():
cfg_file.write("cfg {} {}\n".format(name, value))
for each in inputs_to_save:
cfg_file.write(each + "\n")
ret['config'] = config_params
for each in root_ast.composite_states:
ret['children'][each.statename] = create_dot_graph(each, basic)
# Recursively generate the graphs for nested states
# Inherit from the list of signals from the higer level state
each.input_signals = root_ast.input_signals
ret['children'][each.statename] = create_dot_graph(each, basic, scene)
return ret
......
......@@ -444,7 +444,7 @@ class Symbol(QObject, QGraphicsPathItem, object):
def loadHyperlinkDialog(self):
''' Load dialog from ui file for defining hyperlink '''
loader = QUiLoader()
ui_file = QFile(':/hyperlink.ui') # UI_DIALOG_FILE)
ui_file = QFile(':/hyperlink.ui')
ui_file.open(QFile.ReadOnly)
self.hyperlink_dialog = loader.load(ui_file)
ui_file.close()
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -1027,6 +1027,8 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
ast, _, _ = ogParser.parse_pr(string=pr_data)
try:
process_ast, = ast.processes
process_ast.input_signals = \
sdlSymbols.CONTEXT.processes[0].input_signals
except ValueError:
LOG.debug('No statechart to render')
return None
......@@ -1034,7 +1036,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
# 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, scene=self)
def export_branch_to_picture(self, symbol, filename, doc_format):
......@@ -2149,6 +2151,7 @@ class OG_MainWindow(QtGui.QMainWindow, object):
self.mdi_area = None
self.sub_mdi = None
self.statechart_mdi = None
self.current_window = None
self.datadict = None
self.setWindowState(Qt.WindowMaximized)
......@@ -2303,7 +2306,10 @@ class OG_MainWindow(QtGui.QMainWindow, object):
''' Signal sent by Qt when the MDI area tab changes
Here we check if the Statechart tab is selected, and we draw/refresh
the statechart automatically in that case '''
if mdi == self.statechart_mdi:
if(mdi == self.statechart_mdi and
mdi != self.current_window and not Statechart.locked()):
# this signal is executed even when model windows are open
# so the lock is necessary to prevent recursive execution
scene = self.view.top_scene()
try:
graph = scene.sdl_to_statechart()
......@@ -2314,7 +2320,11 @@ class OG_MainWindow(QtGui.QMainWindow, object):
self.statechart_scene.itemsBoundingRect(),
Qt.KeepAspectRatioByExpanding)
except (AttributeError, IOError, TypeError) as err:
LOG.debug(str(err))
LOG.debug("Statechart error: " + str(err))
if mdi is not None:
# When leaving the focus, this signal is received with mdi == None
# but the window is not changed, so don't update current_window
self.current_window = mdi
@QtCore.Slot(QtGui.QTreeWidgetItem, int)
......
# $ANTLR 3.1.3 Mar 17, 2009 19:23:44 sdl92.g 2016-11-29 10:37:54
# $ANTLR 3.1.3 Mar 17, 2009 19:23:44 sdl92.g 2017-02-19 22:16:11
import sys
from antlr3 import *
......
# $ANTLR 3.1.3 Mar 17, 2009 19:23:44 sdl92.g 2016-11-29 10:37:53
# $ANTLR 3.1.3 Mar 17, 2009 19:23:44 sdl92.g 2017-02-19 22:16:10
 
import sys
from antlr3 import *
......@@ -924,7 +924,7 @@ class sdl92Parser(Parser):
stream_end.add(end10.tree)
 
# AST Rewrite
# elements: SYSTEM, entity_in_system, system_name
# elements: system_name, entity_in_system, SYSTEM
# token labels:
# rule labels: retval
# token list labels:
......@@ -1079,7 +1079,7 @@ class sdl92Parser(Parser):
stream_end.add(end16.tree)
 
# AST Rewrite
# elements: end, def_selection_list, USE, use_asn1, package_name
# elements: use_asn1, def_selection_list, end, package_name, USE
# token labels:
# rule labels: retval
# token list labels:
......@@ -1476,7 +1476,7 @@ class sdl92Parser(Parser):
stream_end.add(end29.tree)
 
# AST Rewrite
# elements: signal_id, input_params, SIGNAL, paramnames
# elements: paramnames, signal_id, input_params, SIGNAL
# token labels:
# rule labels: retval
# token list labels:
......@@ -1630,7 +1630,7 @@ class sdl92Parser(Parser):
stream_end.add(end34.tree)
 
# AST Rewrite
# elements: CHANNEL, channel_id, route
# elements: route, CHANNEL, channel_id
# token labels:
# rule labels: retval
# token list labels:
......@@ -1800,7 +1800,7 @@ class sdl92Parser(Parser):
stream_end.add(end43.tree)
 
# AST Rewrite
# elements: signal_id, source_id, dest_id
# elements: source_id, dest_id, signal_id
# token labels:
# rule labels: retval
# token list labels:
......@@ -1950,7 +1950,7 @@ class sdl92Parser(Parser):
stream_end.add(end49.tree)
 
# AST Rewrite
# elements: BLOCK, entity_in_block, block_id
# elements: entity_in_block, BLOCK, block_id
# token labels:
# rule labels: retval
# token list labels:
......@@ -2360,7 +2360,7 @@ class sdl92Parser(Parser):
stream_end.add(end62.tree)
 
# AST Rewrite
# elements: channel_id, route_id
# elements: route_id, channel_id
# token labels:
# rule labels: retval
# token list labels:
......@@ -2691,7 +2691,7 @@ class sdl92Parser(Parser):
 
 
# AST Rewrite
# elements: cif, number_of_instances, a, processBody, type_inst, procedure, process_id, REFERENCED, composite_state, PROCESS, pfpar, text_area
# elements: cif, type_inst, process_id, number_of_instances, PROCESS, pfpar, composite_state, processBody, text_area, REFERENCED, a, procedure
# token labels:
# rule labels: a, retval
# token list labels:
......@@ -3030,7 +3030,7 @@ class sdl92Parser(Parser):
stream_sort.add(sort86.tree)
 
# AST Rewrite
# elements: variable_id, sort
# elements: sort, variable_id
# token labels:
# rule labels: retval
# token list labels:
......@@ -3396,7 +3396,7 @@ class sdl92Parser(Parser):
stream_end.add(e2.tree)
 
# AST Rewrite
# elements: PROCEDURE, processBody, procedure_id, res, procedure, cif, e1, text_area, e2, EXTERNAL, fpar
# elements: text_area, procedure, EXTERNAL, cif, e1, res, PROCEDURE, fpar, procedure_id, processBody, e2
# token labels:
# rule labels: res, e1, e2, retval
# token list labels:
......@@ -3635,7 +3635,7 @@ class sdl92Parser(Parser):
 
 
# AST Rewrite
# elements: variable_id, RETURNS, sort
# elements: RETURNS, variable_id, sort
# token labels:
# rule labels: retval
# token list labels:
......@@ -3772,7 +3772,7 @@ class sdl92Parser(Parser):
stream_end.add(end107.tree)
 
# AST Rewrite
# elements: FPAR, formal_variable_param
# elements: formal_variable_param, FPAR
# token labels:
# rule labels: retval
# token list labels:
......@@ -3948,7 +3948,7 @@ class sdl92Parser(Parser):
stream_sort.add(sort114.tree)
 
# AST Rewrite
# elements: INOUT, variable_id, sort, IN, OUT
# elements: variable_id, sort, OUT, INOUT, IN
# token labels:
# rule labels: retval
# token list labels:
......@@ -4096,7 +4096,7 @@ class sdl92Parser(Parser):
stream_cif_end_text.add(cif_end_text117.tree)
 
# AST Rewrite
# elements: cif, cif_end_text, content
# elements: cif_end_text, cif, content
# token labels:
# rule labels: retval
# token list labels:
......@@ -4326,7 +4326,7 @@ class sdl92Parser(Parser):
break #loop43
 
# AST Rewrite
# elements: synonym_definition, timer_declaration, syntype_definition, newtype_definition, use_clause, procedure, variable_definition, signal_declaration, res, fpar
# elements: variable_definition, res, signal_declaration, procedure, syntype_definition, fpar, newtype_definition, timer_declaration, use_clause, synonym_definition
# token labels:
# rule labels: res, retval
# token list labels:
......@@ -4735,7 +4735,7 @@ class sdl92Parser(Parser):
stream_end.add(end142.tree)
 
# AST Rewrite
# elements: range_condition, SYNTYPE, syntype_name, parent_sort
# elements: range_condition, parent_sort, syntype_name, SYNTYPE
# token labels:
# rule labels: retval
# token list labels:
......@@ -5020,7 +5020,7 @@ class sdl92Parser(Parser):
stream_end.add(end151.tree)
 
# AST Rewrite
# elements: structure_definition, type_name, array_definition, NEWTYPE
# elements: structure_definition, NEWTYPE, type_name, array_definition
# token labels:
# rule labels: retval
# token list labels:
......@@ -5207,7 +5207,7 @@ class sdl92Parser(Parser):
stream_R_PAREN.add(char_literal158)
 
# AST Rewrite
# elements: ARRAY, sort, sort
# elements: sort, sort, ARRAY
# token labels:
# rule labels: retval
# token list labels:
......@@ -5309,7 +5309,7 @@ class sdl92Parser(Parser):
stream_end.add(end161.tree)
 
# AST Rewrite
# elements: STRUCT, field_list
# elements: field_list, STRUCT
# token labels:
# rule labels: retval
# token list labels:
......@@ -5556,7 +5556,7 @@ class sdl92Parser(Parser):
stream_sort.add(sort168.tree)
 
# AST Rewrite
# elements: sort, field_name
# elements: field_name, sort
# token labels:
# rule labels: retval
# token list labels:
......@@ -5696,7 +5696,7 @@ class sdl92Parser(Parser):
stream_end.add(end173.tree)
 
# AST Rewrite
# elements: variables_of_sort, DCL
# elements: DCL, variables_of_sort
# token labels:
# rule labels: retval
# token list labels:
......@@ -6163,7 +6163,7 @@ class sdl92Parser(Parser):
 
 
# AST Rewrite
# elements: ground_expression, variable_id, sort
# elements: variable_id, sort, ground_expression
# token labels:
# rule labels: retval
# token list labels:
......@@ -6648,7 +6648,7 @@ class sdl92Parser(Parser):
 
 
# AST Rewrite
# elements: end, cif, hyperlink, name, transition, START
# elements: end, START, transition, hyperlink, name, cif
# token labels:
# rule labels: name, retval
# token list labels:
......@@ -6885,7 +6885,7 @@ class sdl92Parser(Parser):
stream_SEMI.add(SEMI210)
 
# AST Rewrite
# elements: hyperlink, transition, cif, connector_name
# elements: hyperlink, cif, connector_name, transition
# token labels:
# rule labels: retval
# token list labels:
......@@ -7155,7 +7155,7 @@ class sdl92Parser(Parser):
stream_end.add(f.tree)
 
# AST Rewrite
# elements: hyperlink, STATE, cif, state_part, e, statelist
# elements: statelist, hyperlink, cif, e, STATE, state_part
# token labels:
# rule labels: e, retval
# token list labels:
......@@ -7400,7 +7400,7 @@ class sdl92Parser(Parser):
 
 
# AST Rewrite
# elements: ASTERISK, exception_state
# elements: exception_state, ASTERISK
# token labels:
# rule labels: retval
# token list labels:
......@@ -7908,7 +7908,7 @@ class sdl92Parser(Parser):
stream_end.add(f.tree)
 
# AST Rewrite
# elements: connection_points, body, statename, e
# elements: e, body, statename, connection_points
# token labels:
# rule labels: e, body, retval
# token list labels:
......@@ -8134,7 +8134,7 @@ class sdl92Parser(Parser):
stream_end.add(f.tree)
 
# AST Rewrite
# elements: body, e, entities, statename, connection_points
# elements: connection_points, e, body, statename, entities
# token labels:
# rule labels: e, entities, body, retval
# token list labels:
......@@ -8670,7 +8670,7 @@ class sdl92Parser(Parser):
stream_point.add(point259.tree)
 
# AST Rewrite
# elements: point, state_part_id
# elements: state_part_id, point
# token labels: state_part_id
# rule labels: retval
# token list labels:
......@@ -8789,7 +8789,7 @@ class sdl92Parser(Parser):
 
 
# AST Rewrite
# elements: DEFAULT, state_point
# elements: state_point, DEFAULT
# token labels: state_point
# rule labels: retval
# token list labels:
......@@ -8982,7 +8982,7 @@ class sdl92Parser(Parser):
stream_end.add(end266.tree)
 
# AST Rewrite
# elements: OUT, end, state_entry_exit_points
# elements: end, state_entry_exit_points, OUT
# token labels:
# rule labels: retval
# token list labels:
......@@ -9577,7 +9577,7 @@ class sdl92Parser(Parser):
 
 
# AST Rewrite
# elements: hyperlink, connect_list, cif, transition, CONNECT, end
# elements: end, transition, CONNECT, hyperlink, connect_list, cif
# token labels:
# rule labels: retval
# token list labels:
......@@ -9927,7 +9927,7 @@ class sdl92Parser(Parser):
stream_transition.add(transition300.tree)
 
# AST Rewrite
# elements: transition, cif, hyperlink
# elements: transition, hyperlink, cif
# token labels:
# rule labels: retval
# token list labels:
......@@ -10236,7 +10236,7 @@ class sdl92Parser(Parser):
 
 
# AST Rewrite
# elements: p, PROVIDED, transition, cif, hyperlink, expression, e
# elements: e, transition, expression, p, PROVIDED, hyperlink, cif
# token labels: p
# rule labels: e, retval
# token list labels:
......@@ -10881,7 +10881,7 @@ class sdl92Parser(Parser):
 
 
# AST Rewrite
# elements: INPUT, enabling_condition, inputlist, hyperlink, end, cif, transition
# elements: hyperlink, enabling_condition, INPUT, inputlist, end, transition, cif
# token labels:
# rule labels: retval
# token list labels:
......@@ -11836,7 +11836,7 @@ class sdl92Parser(Parser):
stream_end.add(end360.tree)
 
# AST Rewrite
# elements: variable_id, EXPORT
# elements: EXPORT, variable_id
# token labels:
# rule labels: retval
# token list labels:
......@@ -11990,7 +11990,7 @@ class sdl92Parser(Parser):
stream_end.add(end365.tree)
 
# AST Rewrite
# elements: end, procedure_call_body, cif, hyperlink
# elements: hyperlink, end, procedure_call_body, cif
# token labels:
# rule labels: retval
# token list labels:
......@@ -12384,7 +12384,7 @@ class sdl92Parser(Parser):
stream_R_PAREN.add(R_PAREN377)
 
# AST Rewrite
# elements: timer_id, expression
# elements: expression, timer_id
# token labels:
# rule labels: retval
# token list labels:
......@@ -12642,7 +12642,7 @@ class sdl92Parser(Parser):
 
 
# AST Rewrite
# elements: expression_list, timer_id
# elements: timer_id, expression_list
# token labels:
# rule labels: retval
# token list labels:
......@@ -12781,7 +12781,7 @@ class sdl92Parser(Parser):
stream_end.add(f.tree)
 
# AST Rewrite
# elements: answer_part, alternative_part, ALTERNATIVE
# elements: alternative_part, ALTERNATIVE, answer_part
# token labels:
# rule labels: retval
# token list labels:
......@@ -13274,7 +13274,7 @@ class sdl92Parser(Parser):
stream_end.add(f.tree)
 
# AST Rewrite
# elements: alternative_part, answer_part, question, hyperlink, e, cif, DECISION
# elements: e, question, DECISION, hyperlink, alternative_part, cif, answer_part
# token labels:
# rule labels: e, retval
# token list labels:
......@@ -13477,7 +13477,7 @@ class sdl92Parser(Parser):
 
 
# AST Rewrite
# elements: answer, cif, hyperlink, transition
# elements: answer, transition, cif, hyperlink
# token labels:
# rule labels: retval
# token list labels:
......@@ -13750,7 +13750,7 @@ class sdl92Parser(Parser):
 
 
# AST Rewrite
# elements: hyperlink, transition, ELSE, cif
# elements: cif, transition, hyperlink, ELSE
# token labels:
# rule labels: retval
# token list labels:
......@@ -14365,7 +14365,7 @@ class sdl92Parser(Parser):
 
 
# AST Rewrite
# elements: constant, EQ, NEQ, LE, GT, GE, LT
# elements: constant, EQ, LT, LE, GE, NEQ, GT
# token labels:
# rule labels: retval
# token list labels:
......@@ -14608,7 +14608,7 @@ class sdl92Parser(Parser):
stream_end.add(end436.tree)
 
# AST Rewrite
# elements: actual_parameters, CREATE, createbody