From 091ca049241c265991c3e09e5aa9be6b4089a093 Mon Sep 17 00:00:00 2001 From: Maxime Perrotin Date: Fri, 21 Nov 2014 09:42:35 +0100 Subject: [PATCH] Add preliminary support of condition boxes --- msccore/__init__.py | 3 +- msccore/io/msccoretotext.py | 9 +- msccore/msccondition.py | 74 ++++++++ msccore/mscinstance.py | 2 + mscgraphics/basicmscgraph/__init__.py | 3 +- .../basicmscgraph/mscGraphBasicMSCScene.py | 28 ++- .../basicmscgraph/mscGraphCondition.py | 166 ++++++++++++++++++ mscgraphics/coretographics.py | 34 ++-- mscparser/msclexer.py | 1 + mscparser/mscmaker.py | 6 + mscparser/mscparser.py | 10 +- 11 files changed, 318 insertions(+), 18 deletions(-) create mode 100644 msccore/msccondition.py create mode 100644 mscgraphics/basicmscgraph/mscGraphCondition.py diff --git a/msccore/__init__.py b/msccore/__init__.py index f5b2389..72386cb 100644 --- a/msccore/__init__.py +++ b/msccore/__init__.py @@ -38,6 +38,7 @@ from mscmessage import MscMessage from mscdocument import MscDocument from mscvisitor import MscVisitor from msctimer import MscTimer +from msccondition import MscCondition from mscinstancekind import MscInstanceKind from mscmessagedecl import MscMessageDecl import mscregexp as MscRegExp @@ -46,4 +47,4 @@ __version__ = 1.0 __all__ = ['MscElement', 'MscEvent', 'MscTimer', 'MscInstanceKind', 'MscMessageDecl', 'MscRegExp', 'MscInstance', 'MscMessage', - 'BasicMsc', 'MscDocument', 'MscVisitor'] + 'BasicMsc', 'MscDocument', 'MscVisitor', 'MscCondition'] diff --git a/msccore/io/msccoretotext.py b/msccore/io/msccoretotext.py index 891b5e1..d0015cd 100644 --- a/msccore/io/msccoretotext.py +++ b/msccore/io/msccoretotext.py @@ -161,8 +161,6 @@ class CoreToText(MscVisitor): if element.instanceSender() == self.instance: receiverName = element.instanceReceiver().getName() -# print element.instanceReceiver() -# print element.parent() if element.instanceReceiver() == element.parent(): # ENV receiverName = u'env' @@ -192,6 +190,11 @@ class CoreToText(MscVisitor): self._result = timer + " {}{}\n".format(element.getName(), self.end(element)) + def visitorMscCondition(self, element): + u""" Export MscCondition to text """ + self._result = "condition '{}'{}\n".format(element.getName(), + self.end(element)) + def end(self, element): u""" Return "end" token of element. @@ -210,4 +213,4 @@ class CoreToText(MscVisitor): # print lines for i in lines[:len(lines) - 1]: text = text + u'{}{}\n'.format(self.INDENT, i) - return text \ No newline at end of file + return text diff --git a/msccore/msccondition.py b/msccore/msccondition.py new file mode 100644 index 0000000..3211d2e --- /dev/null +++ b/msccore/msccondition.py @@ -0,0 +1,74 @@ +#****************************************************************************** +# +# TASTE Msc Diagram Editor +# http://taste.tuxfamily.org/ +# +# This file is part of TASTE Msc Editor. +# +# TASTE Msc Diagram Editor is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# TASTE Msc Diagram Editor is distributed in the hope that it will be +# useful,(but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with TASTE Msc Diagram Editor. If not, see +# . +# +# Author: Angel Esquinas +# Author of this module: Maxime Perrotin maxime.perrotin@esa.int +# +# Copyright (c) 2014 European Space Agency +# +#****************************************************************************** +u""" +The :class:`msccore.MscCondition` class represents a Condition within the MSC. + +:class:`msccore.MscCondition` + +""" +from mscelement import MscElement + + +class MscCondition(MscElement): + u""" + :param unicode name: Text of the condition + :param int orderPos: Absolute pos of timer, when used as event. + :param parent: + :type parent: PySide.QtCore.QObject + """ + TYPE = u"Condition" + + def __init__(self, name, orderPos=0, parent=None): + ''' + Constructor + ''' + super(MscCondition, self).__init__(name, parent, self.TYPE) + self.setAbsPos(orderPos) + + def delete(self): + """ Delete is the destructor of MscCondition + The signal 'deleted' is emitted before destroy himself. + """ + self.deleted.emit(self) + self.deleteLater() + + #Order/Graphical Pos + def setAbsPos(self, pos): + self._absPos = pos + + def absPos(self): + return self._absPos + + def accept(self, visitor): + u""" Visitor Pattern""" + visitor.visitorMscCondition(self) + + def __str__(self): + return """<<{0}:{1}. Pos {3}>>\n + comment: {4}""".format(self.TYPE, self.getName(), + self.absPos(), self.comment()) diff --git a/msccore/mscinstance.py b/msccore/mscinstance.py index 85a020d..dcc38eb 100644 --- a/msccore/mscinstance.py +++ b/msccore/mscinstance.py @@ -175,6 +175,7 @@ class MscInstance(MscElement): * :class:`~msccore.MscMessage` * :class:`~msccore.MscTimer` + * :class:`~msccore.MscCondition` The created event is returned. @@ -251,6 +252,7 @@ class MscInstance(MscElement): * :class:`~msccore.MscMessage` * :class:`~msccore.MscTimer` + * :class:`~msccore.MscCondition` The :meth:`MscElement._contentsChanged` function is called to indicate that the content of this basic MSC are changed. diff --git a/mscgraphics/basicmscgraph/__init__.py b/mscgraphics/basicmscgraph/__init__.py index b6592ec..7451da0 100644 --- a/mscgraphics/basicmscgraph/__init__.py +++ b/mscgraphics/basicmscgraph/__init__.py @@ -25,10 +25,11 @@ # #****************************************************************************** from mscGraphTimer import MscGraphTimer +from mscGraphCondition import MscGraphCondition from mscGraphInstance import MscGraphInstance from mscGraphMessage import MscGraphMessage from mscGraphBMSC import MscGraphBMSC from mscGraphBasicMSCScene import MscGraphBasicMSCScene __all__ = ['MscGraphInstance', 'MscGraphBMSC', 'MscGraphMessage', - 'MscGraphTimer', 'MscGraphBasicMSCScene'] + 'MscGraphTimer', 'MscGraphBasicMSCScene', 'MscGraphCondition'] diff --git a/mscgraphics/basicmscgraph/mscGraphBasicMSCScene.py b/mscgraphics/basicmscgraph/mscGraphBasicMSCScene.py index 9a4614e..8f36a1e 100644 --- a/mscgraphics/basicmscgraph/mscGraphBasicMSCScene.py +++ b/mscgraphics/basicmscgraph/mscGraphBasicMSCScene.py @@ -34,6 +34,7 @@ from PySide.QtGui import QMenu from mscGraphInstance import MscGraphInstance from mscGraphTimer import MscGraphTimer +from mscGraphCondition import MscGraphCondition from mscGraphBMSC import MscGraphBMSC from mscGraphMessage import MscGraphMessage from mscgraphics import MscGraphComment @@ -45,7 +46,7 @@ logger = logging.getLogger(__name__) class MscGraphBasicMSCScene(QGraphicsScene): # Mode of the scene (MoveItem, InsertInstance, InsertMessage, InsertTimer, - InsertComment) = range(5) + InsertComment, InsertCondition) = range(6) # Signals itemInserted = Signal(MscGraphItem) @@ -226,6 +227,22 @@ class MscGraphBasicMSCScene(QGraphicsScene): comment.delete() self.removeItem(comment) + #--------------- Conditions + def addCondition(self, instance, pos=None, label=''): + u""" + Add new "condition" to the scene and position it in "pos" if any + """ + condition = MscGraphCondition(label=label, parent=self.bmsc()) + instance.addEvent(condition) + if pos is not None: + condition.setPos(pos) + condition.itemSelected.connect(self.itemSelected) + self.itemInserted.emit(condition) + return condition + + def removeCondition(self, condition): + condition.delete() + self.removeItem(condition) #************************************************************************** # Actions #************************************************************************** @@ -291,6 +308,15 @@ class MscGraphBasicMSCScene(QGraphicsScene): self.addTimer(instance, instance.mapFromScene(mouseEvent.scenePos())) + # Click to add a Condition + elif self._sceneMode == self.InsertCondition: + items = self.items(mouseEvent.scenePos()) + if len(items) > 0: + instance = items[0] + if isinstance(instance, MscGraphInstance): + self.addCondition(instance, + instance.mapFromScene(mouseEvent.scenePos())) + # Click to add Comment elif self._sceneMode == self.InsertComment: items = self.items(mouseEvent.scenePos()) diff --git a/mscgraphics/basicmscgraph/mscGraphCondition.py b/mscgraphics/basicmscgraph/mscGraphCondition.py new file mode 100644 index 0000000..b1203a5 --- /dev/null +++ b/mscgraphics/basicmscgraph/mscGraphCondition.py @@ -0,0 +1,166 @@ +#****************************************************************************** +# +# TASTE Msc Diagram Editor +# http://taste.tuxfamily.org/ +# +# This file is part of TASTE Msc Editor. +# +# TASTE Msc Diagram Editor is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# TASTE Msc Diagram Editor is distributed in the hope that it will be +# useful,(but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with TASTE Msc Diagram Editor. If not, see +# . +# +# Author: Angel Esquinas +# Author of this module: Maxime Perrotin +# +# Copyright (c) 2012 European Space Agency +# +#****************************************************************************** +from PySide.QtGui import QPainterPath, QPen +from PySide.QtCore import Qt, Slot + +from msccore import MscCondition +from mscgraphics import MscGraphItem +from mscgraphics import MscLabelable + + +class MscGraphCondition(MscGraphItem): + ''' + Class that draws a Condition symbol + ''' + + DefaultWidth = 100 + DefaultHeight = 100 + + def __init__(self, data=None, y=0, label=None, parent=None): + u""" + Initize + """ + super(MscGraphCondition, self).__init__(parent) + self.setFlag(super(MscGraphCondition, self).ItemIsMovable, True) + + if label is None: + label = "Condition" + + if data is None: + self.setMscData(MscCondition(name=label)) + else: + self.setMscData(data) + + self.initilizeGraphics() + self.addLabel() + # Signal when selected + self.setSignalWhenSelected(True) + + # Create Path + self.createPath() + + #************************************************************************** + # Data Functions + #************************************************************************** + @Slot(str) + def setDataName(self, name): + self.mscData().setName(name) + + @Slot() + def readMscData(self): + self.updateLabel() + + def delete(self): + u""" + Delete the MscGraphCondition. + Reimplemented from MscGraphItem. + """ + # Ask parentItem to delete the event + # NOTE: This could be implemented with signals + + parent = self.parentItem() + if parent != None: + parent.deleteMscEvent(self) + + #************************************************************************** + # Graphical Text + #************************************************************************** + def addLabel(self): + self.label = MscLabelable(self.mscData().getName(), self) + self.updateLabelPosition() + self.label.labelChanged.connect(self.setDataName) + + def updateLabel(self): + self.label.setTextLabel(self.mscData().getName()) + + def updateLabelPosition(self): + self.label.setPos(self.width + 10, + -self.label.boundingRect().height() / 2) + + #************************************************************************** + # Item Change + #************************************************************************** + def limitsOfItem(self, value): + u""" Check the limits of the event area of parent """ + parent = self.parentItem() + if parent == None: + return value + + upperY = parent.upperLimit().y() + bottomY = parent.bottomLimit().y() + valueY = value.y() + + if valueY < upperY: + valueY = upperY + + value.setX(self.pos().x()) + value.setY(valueY) + return value + + def itemChange(self, change, value): + if (change == super(MscGraphCondition, self).ItemPositionChange): + value = self.limitsOfItem(value) + + elif (change == super(MscGraphCondition, self).ItemPositionHasChanged): + self.mscData().setAbsPos(self.mapToScene(0, 0).y()) + self.yHasChanged.emit(self) + + return MscGraphItem.itemChange(self, change, value) + + #************************************************************************** + # Paint Functions + #************************************************************************** + def initilizeGraphics(self): + self.color = Qt.blue + self.pen = QPen(self.color, 1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) + + def updateBounding(self): + u""" + Update internal drawing coordinates + """ + self.prepareGeometryChange() + rect = self.path.boundingRect() + self.drawOriginX = rect.x() + self.drawOriginY = rect.y() + self.width = rect.width() + self.height = rect.height() + self.updateLabelPosition() + + def createPath(self): + u""" + Create the path of Timer + """ + self.path = QPainterPath() + self.path.addRect(-self.DefaultWidth / 2, 0, + self.DefaultWidth, self.DefaultHeight) + # Set correct values of x, y, width and height + self.updateBounding() + + def paint(self, painter, option, widget): + painter.setPen(self.pen) + painter.drawPath(self.path) diff --git a/mscgraphics/coretographics.py b/mscgraphics/coretographics.py index ac51fd3..80f8adc 100644 --- a/mscgraphics/coretographics.py +++ b/mscgraphics/coretographics.py @@ -36,6 +36,7 @@ from PySide.QtCore import QPointF from msccore import MscVisitor from msccore import MscMessage from msccore import MscTimer +from msccore import MscCondition from msccore import MscDocument from mscgraphics import MscGraphBMSC @@ -43,6 +44,7 @@ from mscgraphics import MscGraphBasicMSCScene from mscgraphics import MscGraphInstance from mscgraphics import MscGraphMessage from mscgraphics import MscGraphTimer +from mscgraphics import MscGraphCondition def coreToGraphics(document): @@ -145,28 +147,22 @@ class CoreToGraphicsVisitor(MscVisitor): found = False for e in graph.mscData().events(): - #print e.element event = e.element # Messages - if isinstance(e.element, MscMessage): - coreMsg = e.element - #msgHash = coreMsg.hash() - #print msgHash + if isinstance(event, MscMessage): + coreMsg = event # Look for the message in the table elem = self.incompleteMsg.pop(coreMsg, None) - if elem != None: + if elem is not None: found = True -# print 'FOUND: {0}'.format(elem) else: y = (spaceY * (coreMsg.absPos() - 1)) + startY -# print "PUNTO Y: {}".format(y) elem = MscGraphMessage(graph, graph, data=coreMsg) elem.setPos(elem.pos().x(), y) if found: - #print "FOUND {}".format(elem) if coreMsg.instanceSender() == graph.mscData(): elem.setStartInstance(graph) else: @@ -188,10 +184,9 @@ class CoreToGraphicsVisitor(MscVisitor): self.incompleteMsg[coreMsg] = elem # Timers - if isinstance(event, MscTimer): + elif isinstance(event, MscTimer): y = (spaceY * (event.absPos() - 1)) + startY -# print "PUNTO Y: {}".format(y) timer = MscGraphTimer(e.element) scene.addTimer2(graph, timer) @@ -205,5 +200,22 @@ class CoreToGraphicsVisitor(MscVisitor): pos=pos) c.setCommentText(e.element.comment()) + # Conditions + elif isinstance(event, MscCondition): + y = (spaceY * (event.absPos() - 1)) + startY + condition = MscGraphCondition(event) + + scene.addCondition(graph, condition) + condition.setPos(graph.mapFromScene(timer.pos().x(), y)) + + if event.comment() != u"": + trect = (condition.childrenBoundingRect() + | condition.boundingRect()) + pos = QPointF(trect.width() + 10, 0) + pos = elem.mapToScene(pos) + c = scene().addComment(item=condition, + pos=pos) + c.setCommentText(event.comment()) + def obtainResult(self): return self.scenes diff --git a/mscparser/msclexer.py b/mscparser/msclexer.py index f5c03b0..98130e3 100644 --- a/mscparser/msclexer.py +++ b/mscparser/msclexer.py @@ -55,6 +55,7 @@ class MscLexer: reserved = { #'begin': 'BEGIN', 'comment': 'COMMENT', + 'condition': 'CONDITION', 'data': 'DATA', 'endinstance': 'ENDINSTANCE', 'endmsc': 'ENDMSC', diff --git a/mscparser/mscmaker.py b/mscparser/mscmaker.py index cc24641..cc2ef72 100644 --- a/mscparser/mscmaker.py +++ b/mscparser/mscmaker.py @@ -30,6 +30,7 @@ import logging import msccore from msccore import BasicMsc from msccore import MscTimer +from msccore import MscCondition from msccore import MscDocument from msccore import MscInstance from msccore import MscInstanceKind @@ -567,3 +568,8 @@ class MscMaker(object): timer = MscTimer(name=name, typeTimer=tt) self.newEvent(timer) #TODO: Return Error if scope if not instance + + def newCondition(self, name): + u"""Add new Condition to instance in scope""" + condition = MscCondition(name=name) + self.newEvent(condition) diff --git a/mscparser/mscparser.py b/mscparser/mscparser.py index cb8a100..ba5b18c 100644 --- a/mscparser/mscparser.py +++ b/mscparser/mscparser.py @@ -272,7 +272,8 @@ class MscParser: def p_event(self, p): # Auxiliar rule for have all events together u"""event : message_event - | timer_statement""" + | timer_statement + | condition_statement""" #************************************************************************** # 4.2 Instance @@ -417,6 +418,13 @@ class MscParser: u"""timeout : TIMEOUT NAME""" self.makerMsc.newTimer(p[2], self.makerMsc.Timeout) + #************************************************************************** + # X.X Conditions (TODO CHECK CHAPTER IN MSC STANDARD) + #************************************************************************** + def p_condition(self, p): + u"""condition_statement : CONDITION 'NAME'""" + self.makerMsc.newCondition(p[2], self.makerMsc.Condition) + #************************************************************************* # 5.2 Syntax interface to external data languages #************************************************************************* -- GitLab