Commit fa25689f authored by Maxime Perrotin's avatar Maxime Perrotin
Browse files

Merge branch 'master' of https://github.com/esa/opengeode

parents 24720fc3 0a1a1c22
......@@ -142,11 +142,12 @@ The background pattern was downloaded from www.subtlepatterns.com
Changelog
=========
1.5.5 (08/2016)
1.5.6 (08/2016)
- vi interface supports history
- vi interface for substitution can apply to the whole model (with g)
- refactoring function via vi interface (eg. %state,fromName,toName,)
- Fixed issue with rendering (coordinates of symbols could be wrong)
- Introduce data dictionary
1.5.4 (08/2016)
- Various GUI improvements
......
/*
Pretty print ASN.1 files in HTML with anchors
Type names use underscores
*/
group icd_uper;
//delimiters "$", "$"
Infinity() ::= "&#8734"
NewLine() ::= "<br/>"
Orange() ::= "#FF8f00"
Blue() ::= "#379CEE"
ItemNumber(nIndex) ::= "Item #$nIndex$"
Integer() ::= "INTEGER"
BitString() ::= "BIT-STRING"
OctetString() ::= "OCTET-STRING"
Boolean() ::= "BOOLEAN"
Choice() ::= "CHOICE"
Enumerated() ::= "ENUMERATED"
IA5String() ::= "IA5String"
NumericString() ::= "NumericString"
NullType() ::= "NULL"
Real() ::= "REAL"
Sequence() ::= "SEQUENCE"
SequenceOf() ::= "SEQUENCE-OF"
// HTML formatting for the input ASN.1 grammar (new in version 3.2.x)
// Lower/greater than symbols (< and >) must be replaced with HTML code
LeftDiple() ::= "&lt;"
RightDiple() ::= "&gt;"
BlueTas(sBlueTasC,sTasName) ::= <<
<a name="ASN1_$sBlueTasC$">$sTasName$</a>
>>
Asn1Token(sKeyword) ::= <<
<b><font color="#5F9EA0">$sKeyword$</font></b>
>>
StringLiteral(sStringLiteral) ::= <<
<font color="#A31515">$sStringLiteral$</font>
>>
TasName(sTasName, sTasNameC) ::= <<
<a name="ASN1_$sTasNameC$"></a><font color="#B8860B"><b>$sTasNameC$</b></font></a>
>>
TasName2(sTasName, sTasNameC) ::= <<
<a href="#ASN1_$sTasNameC$"><font color="#000000">$sTasNameC$</font></a>
>>
Comment(sComment) ::= <<
<font color="#008000"><i>$sComment$</i></font>
>>
// End HTML formatting for the ASN.1 grammar
RealSizeExplained() ::= <<
>>
IntSizeExplained() ::= <<
>>
ArraySizeExplained() ::= <<
>>
ZeroSizeExplained() ::= <<
>>
EmitEnumItem(sName, nValue) ::= <<
>>
EmitEnumItemWithComment(sName, nValue, sComment) ::= <<
>>
EmitEnumInternalContents(arrsItems) ::= <<
>>
// applicable to Integers, booleans, reals
EmitPrimitiveType(sColor, sTasName, sTasNameC, sAsnKindName, sMinBytes, sMaxBytes, sMaxBitsExplained, sCommentLine, sAsn1Constraints, sMinBits, sMaxBits, arrsComments) ::= <<
>>
EmitSequence(sColor, sTasName, sTasNameC, sMinBytes, sMaxBytes, sMaxBitsExplained, sCommentLine, arrsChildren, arrsComments) ::= <<
>>
EmmitSeqChild_RefType(sRefName, sRefNameC) ::= <<
>>
OddRow() ::= ""
EvenRow() ::= ""
EmmitSequenceChild(sCssClass, nIndex, sName, sComment, sOptionality, sType, sConstraint, sMin, sMax) ::= <<
>>
EmmitSequencePreambleSingleComment(nIndex, sOptChildName) ::= <<
>>
EmmitSequencePreambleComment(arrsOptChildren) ::= <<
>>
/* *** CHOICE ****/
EmitChoice(sColor, sTasName, sTasNameC, sMinBytes, sMaxBytes, sMaxBitsExplained, sCommentLine, arrsChildren, arrsComments) ::= <<
>>
EmmitChoiceChild(sCssClass, nIndex, sName, sComment, sType, sConstraint, sMin, sMax) ::= <<
>>
EmmitChoiceIndexSingleComment(nIndex, sChildName) ::= <<
>>
EmmitChoiceIndexComment(arrsOptChildren) ::= <<
>>
/* *********** CHOICE END ********** */
/* *********** SEQUENCE OF, OCTET STRING etc ********** */
EmitSizeable(sColor, sTasName, sTasNameC, sKind, sMinBytes, sMaxBytes, sMaxBitsExplained, sCommentLine, arrsRows, arrsComments) ::= <<
>>
EmitRowWith3Dots() ::= <<
>>
EmmitTass(sTypeContent) ::= <<
$sTypeContent$
&nbsp;<br/>
>>
EmmitModule(sModName, arrsComments, arrsTases) ::= <<
<div style="width: 100%">
<h2 >Module : $sModName$</h2>
<font face="Courier" color="DimGray"><pre>
$arrsComments;separator="\n"$
</pre></font>
$arrsTases;separator="\n"$
</div>
>>
EmmitFile(sAsnFileName, arrsModules) ::= <<
<div style="width: 100%">
<h1 >Asn1 file : $sAsnFileName$</h1>
$arrsModules;separator="\n"$
</div>
>>
EmmitFilePart2(sFileName, sAsn1Content) ::= <<
<h1 >$sFileName$</h1>
<div style="width: 100%; white-space:pre; font-size:medium">
$sAsn1Content$
</div>
>>
RootHtml(arrsFiles1, arrsFiles2, bIntegerSizeMustBeExplained, bRealSizeMustBeExplained, bLengthSizeMustBeExplained, bWithComponentMustBeExplained, bZeroBitsMustBeExplained) ::= <<
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>ICD</title>
<style type="text/css">
h1
{
color: #033a7a;
}
h2
{
color: #033a7a;
}
</style>
</head>
<body>
$arrsFiles2;separator="\n"$
</body>
</html>
>>
......@@ -27,5 +27,6 @@
<file>fonts/Ubuntu-R.ttf</file>
<file>fonts/Ubuntu-RI.ttf</file>
<file>misc/lander.mp3</file>
<file>misc/pretty_print_asn1.stg</file>
</qresource>
</RCC>
......@@ -161,7 +161,7 @@
<widget class="QWidget" name="dockWidgetContents_3">
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="SDL_View" name="datatypes_view"/>
<widget class="QTextBrowser" name="asn1_browser"/>
</item>
</layout>
</widget>
......
......@@ -887,7 +887,6 @@ package {process_name} is'''.format(process_name=process_name,
break
for statename in process.cs_mapping.viewkeys() - done:
cs_item = process.cs_mapping[statename]
taste_template.append(u'-- Now what')
taste_template.append(u'{first}if not msgPending and '
u'trId = -1 and {}.state = {} then'
.format(LPREFIX, statename, first='els' if done else ''))
......
......@@ -21,8 +21,9 @@ import distutils.spawn as spawn
import sys
import importlib
import logging
from PySide.QtCore import QProcess
from PySide.QtCore import QProcess, QFile
import icons
LOG = logging.getLogger(__name__)
terminal_formatter = logging.Formatter(fmt="[%(levelname)s] %(message)s")
......@@ -80,6 +81,7 @@ def parse_asn1(*files, **options):
ast_version = options.get('ast_version', ASN1.UniqueEnumeratedNames)
rename_policy = options.get('rename_policy', ASN1.NoRename)
flags = options.get('flags', [ASN1.AstOnly])
pprint = options.get('pretty_print', False)
assert isinstance(ast_version, ASN1)
assert isinstance(rename_policy, ASN1)
assert isinstance(flags, list)
......@@ -109,9 +111,21 @@ def parse_asn1(*files, **options):
stg = asn1scc_root + os.sep + 'python.stg'
if pprint:
# Generate an html file with pretty-printed ASN.1 types
stg_qrc = QFile(':misc/pretty_print_asn1.stg')
stg_qrc.open(1)
content = stg_qrc.readAll()
stgfile = tempdir + os.sep + 'pretty_print_asn1.stg'
with open(stgfile, 'w') as tmpfile:
tmpfile.write(content.data())
out_html = tempdir + os.sep + 'dataview.html'
html = ['-customIcdUper', stgfile + '::' + out_html]
else:
html = []
args = [arg0, '-customStgAstVerion', str(ast_version.value),
'-customStg', stg + '::' + filepath,
'-renamePolicy', str(rename_policy.value)] + list(*files)
'-renamePolicy', str(rename_policy.value)] + html + list(*files)
asn1scc = QProcess()
LOG.debug(os.getcwd())
LOG.debug(binary + ' ' + ' '.join(args))
......@@ -126,6 +140,9 @@ def parse_asn1(*files, **options):
else:
ast = importlib.import_module(filename)
AST[filename] = ast
if pprint:
# add the path to the optionally-gernated pretty-printed HTML file
ast.html = out_html
return ast
......
......@@ -360,8 +360,46 @@ class Signalroute(Connection):
def update_completion_list(self, pr_text):
''' Called after text has been edited '''
# TODO - call parseSingleElement, check sdlSymbols for examples
pass
from sdlSymbols import CONTEXT
ast, _, _, _, _ = self.parser.parseSingleElement('signalroute',
pr_text)
# ast is a dict:
# {'routes': [{'dest': str, 'source': str', 'signals': [str]}],
# 'name': u'c'} - dest and source can be 'ENV'
all_sigs = []
for each in ast['routes']:
source = each['source']
dest = each['dest']
sigs = [{'name': name} for name in each['signals']]
all_sigs.extend(sigs)
def get_sig(signal):
''' If signal is declared, return the signature '''
for sig in CONTEXT.signals:
if sig['name'].lower() == signal['name'].lower():
return sig
return signal
if each['dest'] == 'ENV':
# output signals of process 'source'
process, = [p for p in CONTEXT.processes
if p.processName.lower() == each['source'].lower()]
existing = [sig['name'].lower()
for sig in process.output_signals]
for sig in sigs:
if sig['name'].lower() not in existing:
process.output_signals.append(get_sig(sig))
else:
# input signals of process 'source'
process, = [p for p in CONTEXT.processes
if p.processName.lower() == each['dest'].lower()]
existing = [sig['name'].lower()
for sig in process.input_signals]
for sig in sigs:
if sig['name'].lower() not in existing:
process.input_signals.append(get_sig(sig))
existing = [sig['name'].lower() for sig in CONTEXT.signals]
for each in all_sigs:
if each['name'].lower() not in existing:
CONTEXT.signals.append(each)
def resize_item(self, new_rect):
''' Called after signallist text has been edited '''
......
This diff is collapsed.
......@@ -1023,6 +1023,8 @@ class AST(object):
# Refs to the ASN.1 dataview AST (set with USE clauses)
self.dataview = None
self.asn1Modules = None
# DV is the Asn1scc imported module
self.DV = None
# ASN.1-defined constants (constants in Ada but variables in C)
# dictionnary: {ConstantName: type } - copied from dataview.py
self.asn1_constants = {}
......
......@@ -161,7 +161,8 @@ def set_global_DV(asn1_filenames):
DV = parse_asn1(tuple(asn1_filenames),
ast_version=ASN1.UniqueEnumeratedNames,
rename_policy=rename_policy,
flags=[ASN1.AstOnly])
flags=[ASN1.AstOnly],
pretty_print=True)
except (ImportError, NameError) as err:
# Can happen if DataView.py is not there
LOG.error('Error loading ASN.1 model')
......@@ -4362,6 +4363,7 @@ def pr_file(root):
# and not just after the ASN1 specific parsing
ast.dataview = types()
ast.asn1_constants = DV.variables
ast.DV = DV
return ast, errors, warnings
......
......@@ -120,6 +120,9 @@ MODULES = [
CGenerator,
] # type: List[module]
# Define custom UserRoles
ANCHOR = Qt.UserRole + 1
try:
import LlvmGenerator
MODULES.append(LlvmGenerator)
......@@ -134,7 +137,7 @@ except ImportError:
__all__ = ['opengeode', 'SDL_Scene', 'SDL_View', 'parse']
__version__ = '1.5.5'
__version__ = '1.5.6'
if hasattr(sys, 'frozen'):
# Detect if we are running on Windows (py2exe-generated)
......@@ -366,11 +369,14 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
''' Main graphic scene (canvas) where the user can place SDL symbols '''
# Signal to be emitted when the scene is left (e.g. UP button)
scene_left = QtCore.Signal()
context_change = QtCore.Signal()
def __init__(self, context='process'):
'''
Create an SDL Scene for a given context:
process, procedure or composite state
''' Create a Scene for a given context:
process, procedure, composite state, clipboard, etc.
Design note: creating subclasses per context was evaluated but
rejected - there are too few behavioural differences between them
Creating tons of files / classes is not right. Keep it simple.
'''
super(SDL_Scene, self).__init__()
# Reference to the parent scene
......@@ -815,6 +821,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
recursive=False,
nextstate=False, cpy=True))
symbol.update_completion_list(pr_text=pr_text)
self.context_change.emit()
def highlight(self, item):
......@@ -883,6 +890,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
self.undo_stack.push(undo_cmd)
item.try_resize()
item.parentItem().select()
self.update_completion_list(item.parent)
self.refresh()
else:
try:
......@@ -1130,6 +1138,7 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
subscene = SDL_Scene(context=context)
subscene.messages_window = self.messages_window
subscene.parent_scene = parent
subscene.context_change.connect(self.context_change.emit)
return subscene
......@@ -1366,14 +1375,15 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
self.clearSelection()
self.clear_highlight()
item = self.search_item.next()
item = item.parentItem()
item.select()
self.highlight(item)
item.ensureVisible()
except StopIteration:
LOG.info('No more matches')
self.search(self.search_pattern)
except AttributeError:
LOG.info('No search pattern set. Use "/<pattern>"')
except AttributeError as err:
LOG.info('No search pattern. Use "/pattern"')
elif (event.key() == Qt.Key_J and
event.modifiers() == Qt.ControlModifier):
# Debug mode
......@@ -1394,6 +1404,8 @@ class SDL_View(QtGui.QGraphicsView, object):
# signal to ask the main application that a new scene is needed
need_new_scene = QtCore.Signal()
update_asn1_dock = QtCore.Signal(ogAST.AST)
# When changing scene the data dictionary has to be updated
update_datadict = QtCore.Signal()
def __init__(self, scene):
''' Create the SDL view holding the scene '''
......@@ -1574,23 +1586,22 @@ class SDL_View(QtGui.QGraphicsView, object):
'''
LOG.debug('GO_UP')
self.scene().clear_focus()
# Scene may need to be informed when it is left:
# Signal to the world that the current scene is left:
self.scene().scene_left.emit()
scene, horpos, verpos = self.scene_stack.pop()
self.setScene(scene)
self.wrapping_window.setWindowTitle(self.scene().name)
#self.horizontalScrollBar().setSliderPosition(horpos)
#self.verticalScrollBar().setSliderPosition(verpos)
self.set_toolbar()
if not self.scene_stack:
self.up_button.setEnabled(False)
self.setSceneRect(self.scene().sceneRect())
self.viewport().update()
#self.scene().refresh()
#self.refresh()
self.horizontalScrollBar().setSliderPosition(horpos)
self.verticalScrollBar().setSliderPosition(verpos)
sdlSymbols.CONTEXT = self.context_history.pop()
self.update_datadict.emit()
self.scene().undo_stack.cleanChanged.connect(
lambda x: self.wrapping_window.setWindowModified(not x))
def go_down(self, scene, name=''):
''' Enter a nested diagram (procedure, composite state) '''
......@@ -1642,8 +1653,11 @@ class SDL_View(QtGui.QGraphicsView, object):
self.wrapping_window.setWindowTitle(self.scene().name)
self.up_button.setEnabled(True)
self.set_toolbar()
self.scene().scene_left.emit()
self.view_refresh()
self.scene().scene_left.emit()
self.update_datadict.emit()
self.scene().undo_stack.cleanChanged.connect(
lambda x: self.wrapping_window.setWindowModified(not x))
# pylint: disable=C0103
def mouseDoubleClickEvent(self, evt):
......@@ -1837,6 +1851,7 @@ class SDL_View(QtGui.QGraphicsView, object):
# Set AST to be used as data dictionnary and updated on the fly
sdlSymbols.AST = ast
sdlSymbols.CONTEXT = block
self.update_datadict.emit()
def open_diagram(self):
''' Load one or several .pr file and display the state machine '''
......@@ -2000,13 +2015,12 @@ class OG_MainWindow(QtGui.QMainWindow, object):
''' Create the main window '''
super(OG_MainWindow, self).__init__(parent)
self.view = None
self.scene = None
self.statechart_view = None
self.statechart_scene = None
self.vi_bar = Vi_bar()
# Docking areas
self.datatypes_view = None
self.datatypes_scene = None
self.datatypes_browser = None # type: QtGui.QTextBrowser
#self.datatypes_scene = None
self.asn1_area = None
# MDI area (need to keep them to avoid segfault due to pyside bugs)
self.mdi_area = None
......@@ -2018,20 +2032,29 @@ class OG_MainWindow(QtGui.QMainWindow, object):
''' Create a new, clean SDL scene. This function is necessary because
it is not possible to use QGraphicsScene.clear(), because of Pyside
bugs with deletion of items on application exit '''
self.scene = SDL_Scene(context='block')
scene = SDL_Scene(context='block')
if self.view:
self.scene.messages_window = self.view.messages_window
self.view.setScene(self.scene)
scene.messages_window = self.view.messages_window
self.view.setScene(scene)
self.view.refresh()
scene.undo_stack.cleanChanged.connect(
lambda x: self.view.wrapping_window.setWindowModified(not x))
scene.context_change.connect(self.update_datadict_window)
def start(self, file_name):
''' Initializes all objects to start the application '''
# Create a graphic scene: the main canvas
self.new_scene()
# widget wrapping the view. We have to maximize it
process_widget = self.findChild(QtGui.QWidget, 'process')
process_widget.showMaximized()
# Find SDL_View widget
self.view = self.findChild(SDL_View, 'graphicsView')
self.view.setScene(self.scene)
self.view.wrapping_window = process_widget
self.view.wrapping_window.setWindowTitle('block unnamed[*]')
# Create a default (block) scene for the view
self.new_scene()
# Find Menu Actions
open_action = self.findChild(QtGui.QAction, 'actionOpen')
......@@ -2079,18 +2102,6 @@ class OG_MainWindow(QtGui.QMainWindow, object):
filebar.up_button.triggered.connect(self.view.go_up)
self.addToolBar(Qt.TopToolBarArea, filebar)
self.scene.clearSelection()
self.scene.clear_highlight()
self.scene.clear_focus()
# widget wrapping the view. We have to maximize it
process_widget = self.findChild(QtGui.QWidget, 'process')
process_widget.showMaximized()
self.view.wrapping_window = process_widget
self.view.wrapping_window.setWindowTitle('block unnamed[*]')
self.scene.undo_stack.cleanChanged.connect(
lambda x: process_widget.setWindowModified(not x))
# get the messages list window (to display errors and warnings)
# it is a QtGui.QListWidget
msg_dock = self.findChild(QtGui.QDockWidget, 'msgDock')
......@@ -2100,7 +2111,7 @@ class OG_MainWindow(QtGui.QMainWindow, object):
messages = self.findChild(QtGui.QListWidget, 'messages')
messages.addItem('Welcome to OpenGEODE.')
self.view.messages_window = messages
self.scene.messages_window = messages
self.view.scene().messages_window = messages
messages.itemClicked.connect(self.view.show_item)
self.mdi_area = self.findChild(QtGui.QMdiArea, 'mdiArea')
self.sub_mdi = self.mdi_area.subWindowList()
......@@ -2112,7 +2123,6 @@ class OG_MainWindow(QtGui.QMainWindow, object):
self.mdi_area.subWindowActivated.connect(self.upd_statechart)
break
self.statechart_view = self.findChild(SDL_View, 'statechart_view')
self.statechart_scene = SDL_Scene(context='statechart')
self.statechart_view.setScene(self.statechart_scene)
......@@ -2121,12 +2131,7 @@ class OG_MainWindow(QtGui.QMainWindow, object):
asn1_dock = self.findChild(QtGui.QDockWidget, 'datatypes_dock')
dict_dock = self.findChild(QtGui.QDockWidget, 'datadict_dock')
self.tabifyDockWidget(asn1_dock, dict_dock)
self.datatypes_view = self.findChild(SDL_View, 'datatypes_view')
self.datatypes_scene = SDL_Scene(context='asn1')
self.datatypes_view.setScene(self.datatypes_scene)
self.asn1_area = sdlSymbols.ASN1Viewer()
self.asn1_area.text.setPlainText('-- ASN.1 Data Types')
self.asn1_area.text.try_resize()
self.asn1_browser = self.findChild(QtGui.QTextBrowser, 'asn1_browser')
self.view.update_asn1_dock.connect(self.set_asn1_view)
# Set up the data dictionary window
......@@ -2139,8 +2144,11 @@ class OG_MainWindow(QtGui.QMainWindow, object):
QtGui.QTreeWidgetItem(self.datadict, ["ASN.1 Constants"])
QtGui.QTreeWidgetItem(self.datadict, ["Input signals"])
QtGui.QTreeWidgetItem(self.datadict, ["Output signals"])
self.datatypes_scene.addItem(self.asn1_area)
QtGui.QTreeWidgetItem(self.datadict, ["States"])
QtGui.QTreeWidgetItem(self.datadict, ["Labels"])
QtGui.QTreeWidgetItem(self.datadict, ["Variables"])
QtGui.QTreeWidgetItem(self.datadict, ["Timers"])
self.view.update_datadict.connect(self.update_datadict_window)
# Create a timer for periodically saving a backup of the model
autosave = QTimer(self)
......@@ -2163,6 +2171,7 @@ class OG_MainWindow(QtGui.QMainWindow, object):
else:
# Create a default context - at Block level - for the autocompleter
sdlSymbols.CONTEXT = ogAST.Block()
self.update_datadict_window()
@QtCore.Slot(QtGui.QMdiSubWindow)
def upd_statechart(self, mdi):
......@@ -2186,59 +2195,119 @@ class OG_MainWindow(QtGui.QMainWindow, object):
@QtCore.Slot(QtGui.QTreeWidgetItem, int)
def datadict_item_selected(self, item, column):
''' Slot called when user clicks on an item of the data dictionary '''
print item, column
parent = item.parent()