Bladeren bron

capture concrete syntax when typing a group and store it in a CS model

Lucas Heer 7 jaren geleden
bovenliggende
commit
5f30d83283

+ 7 - 4
main.py

@@ -1,7 +1,7 @@
 import sys
 import sys
 import argparse
 import argparse
 import wrappers.modelverse as mv
 import wrappers.modelverse as mv
-from commons import new_instance_model, new_example_model
+from commons import new_instance_model, new_example_model, all_models
 from sketchUI.main import run_ui
 from sketchUI.main import run_ui
 from evolution import upload_ops
 from evolution import upload_ops
 
 
@@ -92,7 +92,7 @@ if __name__ == "__main__":
     mode = None
     mode = None
     if args.im and args.exm or (not args.im and not args.exm):
     if args.im and args.exm or (not args.im and not args.exm):
         print("Choose either im or exm")
         print("Choose either im or exm")
-        sys.exit()
+        sys.exit(0)
     if args.im:
     if args.im:
         mode = "IM"
         mode = "IM"
     else:
     else:
@@ -101,7 +101,10 @@ if __name__ == "__main__":
     # open existing model?
     # open existing model?
     if args.m:
     if args.m:
         model = args.m
         model = args.m
-        # TODO: Check if this model exists
+        print("Checking if model {} exists ...".format(model))
+        if model not in all_models():
+            print("No such model: {}".format(model))
+            sys.exit(-1)
     else:
     else:
         # no model to open, create an empty one
         # no model to open, create an empty one
         if mode == "IM":
         if mode == "IM":
@@ -109,8 +112,8 @@ if __name__ == "__main__":
         else:
         else:
             model = new_example_model()
             model = new_example_model()
 
 
+    print("Starting UI ...")
     run_ui(mode, model)
     run_ui(mode, model)
 
 
     print("verify ... ")
     print("verify ... ")
     print(mv.verify(model, "formalisms/graphMM"))
     print(mv.verify(model, "formalisms/graphMM"))
-    sys.exit(0)

+ 0 - 11
models/sketching/ConsynMM.mvc

@@ -42,10 +42,6 @@ Class Image {
     data : String
     data : String
 }
 }
 
 
-Class PrimitiveGroup {
-    name = "PrimitiveGroup"
-}
-
 Class Primitive {
 Class Primitive {
     name = "Primitive"
     name = "Primitive"
 }
 }
@@ -73,10 +69,3 @@ Class Line : Primitive {
     end_x : Integer
     end_x : Integer
     end_y : Integer
     end_y : Integer
 }
 }
-
-Association PrimitiveGroupPrimitive(PrimitiveGroup, Primitive) {
-    name = "PrimitiveGroupPrimitive"
-    source_lower_cardinality = 1
-    source_upper_cardinality = 1
-    target_lower_cardinality = 1
-}

+ 0 - 5
models/sketching/cs_router.mvc

@@ -3,8 +3,6 @@ Icon i1 {
     is_primitive = True
     is_primitive = True
 }
 }
 
 
-PrimitiveGroup grp {}
-
 Rectangle r1 {
 Rectangle r1 {
     x = 0
     x = 0
     y = 0
     y = 0
@@ -18,6 +16,3 @@ Ellipse c1 {
     width = 10
     width = 10
     height = 10
     height = 10
 }
 }
-
-PrimitiveGroupPrimitive pgp1(r1, grp) {}
-PrimitiveGroupPrimitive pgp2(c1, grp) {}

+ 1 - 1
sketchUI/exm_mainwindow.py

@@ -73,7 +73,7 @@ class EXMMainWindow(QMainWindow, Ui_MainWindow):
 
 
         self.pen_action = QAction("Pen", self)
         self.pen_action = QAction("Pen", self)
         self.pen_action.setIcon(QIcon("sketchUI/icons/pen.svg"))
         self.pen_action.setIcon(QIcon("sketchUI/icons/pen.svg"))
-        self.pen_action.setCheckable(True)
+        self.pen_action.setCheckable(False)
 
 
         action_group = QActionGroup(self)
         action_group = QActionGroup(self)
         action_group.setExclusive(True)
         action_group.setExclusive(True)

+ 24 - 6
sketchUI/exm_scene.py

@@ -1,9 +1,11 @@
 from enum import Enum
 from enum import Enum
 from PyQt5.QtWidgets import QGraphicsScene, QGraphicsItem, QGraphicsLineItem, QGraphicsRectItem, \
 from PyQt5.QtWidgets import QGraphicsScene, QGraphicsItem, QGraphicsLineItem, QGraphicsRectItem, \
-    QGraphicsEllipseItem, QInputDialog, QGraphicsItemGroup, QTableWidgetItem
+    QGraphicsEllipseItem, QInputDialog, QTableWidgetItem
 from PyQt5.Qt import Qt, QPointF, QPen, QTransform, QApplication
 from PyQt5.Qt import Qt, QPointF, QPen, QTransform, QApplication
 from sketchUI.graphics_edge_item import GraphicsEdgeItem
 from sketchUI.graphics_edge_item import GraphicsEdgeItem
 from sketchUI.graphics_node_item import GraphicsNodeItem, IconType
 from sketchUI.graphics_node_item import GraphicsNodeItem, IconType
+from sketchUI.graphics_sketch_group import SketchGroup
+from sketchUI.graphics_sketch_line import SketchedLineItem
 from sketchUI import mvops
 from sketchUI import mvops
 from evolution.node_ops import NodeAdd, NodeDelete, NodeRetype
 from evolution.node_ops import NodeAdd, NodeDelete, NodeRetype
 import commons
 import commons
@@ -115,7 +117,7 @@ class SketchScene(QGraphicsScene):
             end_point = event.scenePos()
             end_point = event.scenePos()
 
 
             if self._mode == Mode.LINE:
             if self._mode == Mode.LINE:
-                line = QGraphicsLineItem(self._orig_point.x(), self._orig_point.y(),
+                line = SketchedLineItem(self._orig_point.x(), self._orig_point.y(),
                                          end_point.x(), end_point.y())
                                          end_point.x(), end_point.y())
                 line.setPen(QPen(Qt.black, 2, Qt.SolidLine))
                 line.setPen(QPen(Qt.black, 2, Qt.SolidLine))
                 self.addItem(line)
                 self.addItem(line)
@@ -186,11 +188,15 @@ class SketchScene(QGraphicsScene):
             selected = self.selectedItems()
             selected = self.selectedItems()
             self.clearSelection()
             self.clearSelection()
 
 
-            group = self.createItemGroup(selected)
+            group = SketchGroup()
+            for item in selected:
+                group.addToGroup(item)
+
             bb_rect = QGraphicsRectItem(group.boundingRect())
             bb_rect = QGraphicsRectItem(group.boundingRect())
             bb_rect.setData(0, "groupBBox")  # identifier for "does not belong to the actual sketch"
             bb_rect.setData(0, "groupBBox")  # identifier for "does not belong to the actual sketch"
             bb_rect.setPen(QPen(Qt.gray, 1, Qt.DashLine))
             bb_rect.setPen(QPen(Qt.gray, 1, Qt.DashLine))
             group.addToGroup(bb_rect)
             group.addToGroup(bb_rect)
+            self.addItem(group)
             group.setFlag(QGraphicsItem.ItemIsSelectable, True)
             group.setFlag(QGraphicsItem.ItemIsSelectable, True)
             group.setFlag(QGraphicsItem.ItemIsMovable, True)
             group.setFlag(QGraphicsItem.ItemIsMovable, True)
 
 
@@ -201,7 +207,7 @@ class SketchScene(QGraphicsScene):
             if len(selected) != 1:
             if len(selected) != 1:
                 return
                 return
             item = selected[0]
             item = selected[0]
-            if isinstance(item, QGraphicsItemGroup):
+            if isinstance(item, SketchGroup):
                 self._handle_keypress_type_on_group(item)
                 self._handle_keypress_type_on_group(item)
             elif isinstance(item, GraphicsNodeItem):
             elif isinstance(item, GraphicsNodeItem):
                 self._handle_keypress_type_on_node(item)
                 self._handle_keypress_type_on_node(item)
@@ -257,7 +263,7 @@ class SketchScene(QGraphicsScene):
         QApplication.restoreOverrideCursor()
         QApplication.restoreOverrideCursor()
 
 
     def _handle_keypress_type_on_group(self, group):
     def _handle_keypress_type_on_group(self, group):
-        # type: (QGraphicsItemGroup) -> None
+        # type: (SketchGroup) -> None
         """
         """
         type the selected group = make a real node out of it and store it in the model
         type the selected group = make a real node out of it and store it in the model
         also capture its concrete syntax and store it in the modelverse
         also capture its concrete syntax and store it in the modelverse
@@ -289,6 +295,7 @@ class SketchScene(QGraphicsScene):
         nodeid = commons.all_nodes_with_type(self._cur_model, node_type)[0]
         nodeid = commons.all_nodes_with_type(self._cur_model, node_type)[0]
 
 
         self._parent.plainTextEdit.appendPlainText("Capturing concrete syntax of group ...")
         self._parent.plainTextEdit.appendPlainText("Capturing concrete syntax of group ...")
+        self._parent.plainTextEdit.repaint()
         # create concrete syntax model for the sketched elements
         # create concrete syntax model for the sketched elements
         csm = mvops.new_concrete_syntax_model(node_type, IconType.PRIMITIVE)
         csm = mvops.new_concrete_syntax_model(node_type, IconType.PRIMITIVE)
         if not csm:
         if not csm:
@@ -300,8 +307,19 @@ class SketchScene(QGraphicsScene):
             if item.data(0) == "groupBBox":
             if item.data(0) == "groupBBox":
                 # just the bounding box from the group, ignore
                 # just the bounding box from the group, ignore
                 continue
                 continue
+            if isinstance(item, QGraphicsRectItem):
+                rect = group.get_item_coord_relative(item)
+                mvops.add_rect_to_cs(csm, rect)
+            elif isinstance(item, QGraphicsEllipseItem):
+                rect = group.get_item_coord_relative(item)
+                mvops.add_ellipse_to_cs(csm, rect)
+            elif isinstance(item, SketchedLineItem):
+                p1, p2 = group.get_item_coord_relative(item)
+                mvops.add_line_to_cs(csm, p1, p2)
+            else:
+                print("Dont know how to capture CS of item {}".format(item))
 
 
-        # update view
+        # update view: replace group by actual node item with newly populated CS
         csm_content = mvops.get_consyn_of(node_type)
         csm_content = mvops.get_consyn_of(node_type)
         nodeitem = GraphicsNodeItem(nodeid, node_type, csm_content)
         nodeitem = GraphicsNodeItem(nodeid, node_type, csm_content)
         nodeitem.setPos(group.scenePos())
         nodeitem.setPos(group.scenePos())

+ 2 - 2
sketchUI/graphics_node_item.py

@@ -93,8 +93,8 @@ class GraphicsNodeItem(QGraphicsItem):
             # a set of primitives
             # a set of primitives
             for item in self._consyn:
             for item in self._consyn:
                 item_type = item["type"]
                 item_type = item["type"]
-                # ignore the "useless"
-                if item_type in ["Icon", "PrimitiveGroup", "PrimitiveGroupPrimitive"]:
+                # ignore the "useless" Icon class
+                if item_type == "Icon":
                     continue
                     continue
 
 
                 if item_type == "Rectangle":
                 if item_type == "Rectangle":

+ 50 - 0
sketchUI/graphics_sketch_group.py

@@ -0,0 +1,50 @@
+from PyQt5.QtWidgets import QGraphicsItemGroup, QGraphicsRectItem, QGraphicsEllipseItem
+from PyQt5.QtCore import QRect
+from graphics_sketch_line import SketchedLineItem
+
+
+class SketchGroup(QGraphicsItemGroup):
+    def __init__(self):
+        QGraphicsItemGroup.__init__(self)
+        self._prev_pos = self.scenePos()  # empty initially (why?)
+
+    def get_item_coord_relative(self, item):
+        """
+        Returns the item coordinate description, relative to the group.
+        Depending on the type of item, the return type will differ (rectangle for rectangle and ellipse, two points
+        for line)
+        """
+        if item not in self.childItems():
+            return None
+
+        sketch_brect = self.sceneBoundingRect()
+        if isinstance(item, QGraphicsRectItem) or isinstance(item, QGraphicsEllipseItem):
+            item_brect = item.sceneBoundingRect()
+            top_left = (item_brect.topLeft() - sketch_brect.topLeft()).toPoint()
+            return QRect(top_left.x(), top_left.y(), int(item_brect.width()), int(item_brect.height()))
+        elif isinstance(item, SketchedLineItem):
+            return (item.get_p1() - sketch_brect.topLeft()).toPoint(), (item.get_p2() - sketch_brect.topLeft()).toPoint()
+        else:
+            return None
+
+    def mousePressEvent(self, event):
+        self._prev_pos = self.scenePos()
+        #print("Mouse pressed on group, coord {}".format(self._prev_pos))
+        QGraphicsItemGroup.mousePressEvent(self, event)
+
+    def mouseReleaseEvent(self, event):
+        cur_pos = self.scenePos()
+        #print("Mouse released on group, coord {}".format(cur_pos))
+        diff_point = cur_pos - self._prev_pos
+        #print("Diff is {}".format(diff_point))
+
+        # update line coordinates since we moved the group
+        for item in self.childItems():
+            if isinstance(item, SketchedLineItem):
+                #print("Updating coordinates of line")
+                new_p1 = item.get_p1() + diff_point
+                new_p2 = item.get_p2() + diff_point
+                item.set_p1(new_p1)
+                item.set_p2(new_p2)
+
+        QGraphicsItemGroup.mouseReleaseEvent(self, event)

+ 32 - 0
sketchUI/graphics_sketch_line.py

@@ -0,0 +1,32 @@
+"""
+A QGraphicsLineItem, augmented with the two points it is defined by.
+Required since, when in a group, the QGraphicsScene does not update the internal
+points of the underlying QLine object, so we update them manually whenever the group is moved.
+"""
+
+from PyQt5.QtWidgets import QGraphicsLineItem
+from PyQt5.Qt import QPointF
+
+
+class SketchedLineItem(QGraphicsLineItem):
+    def __init__(self, p1_x, p1_y, p2_x, p2_y):
+        QGraphicsLineItem.__init__(self, p1_x, p1_y, p2_x, p2_y)
+        # store away scene coordinates of points
+        self._p1 = QPointF(p1_x, p1_y)
+        self._p2 = QPointF(p2_x, p2_y)
+
+    def set_p1(self, point):
+        # type: (QPointF) -> None
+        self._p1 = point
+
+    def set_p2(self, point):
+        # type: (QPointF) -> None
+        self._p2 = point
+
+    def get_p1(self):
+        # type: () -> QPointF
+        return self._p1
+
+    def get_p2(self):
+        # type: () -> QPointF
+        return self._p2

+ 5 - 1
sketchUI/im_mainwindow.py

@@ -84,12 +84,16 @@ class IMMainWindow(QMainWindow, Ui_MainWindow):
             # empty model
             # empty model
             return
             return
 
 
+        x_pos = -150
+        y_pos = 70
         for item in model:
         for item in model:
             typ = item["type"]
             typ = item["type"]
             if typ == "Node":
             if typ == "Node":
                 # first, draw all nodes
                 # first, draw all nodes
                 node_type = item["typeID"]
                 node_type = item["typeID"]
-                self._add_node_to_scene(item["id"], node_type)
+                self._add_node_to_scene(item["id"], node_type, x=x_pos, y=y_pos)
+                x_pos += 80
+                y_pos *= -1
 
 
         for item in model:
         for item in model:
             # now the edges
             # now the edges

+ 5 - 0
sketchUI/im_scene.py

@@ -100,8 +100,10 @@ class CustomScene(QGraphicsScene):
         from_type = from_item.get_type()
         from_type = from_item.get_type()
         to_type = to_item.get_type()
         to_type = to_item.get_type()
         if is_new:
         if is_new:
+            QApplication.setOverrideCursor(Qt.WaitCursor)
             if not commons.is_edge_supported(from_type, to_type):
             if not commons.is_edge_supported(from_type, to_type):
                 self._parent.plainTextEdit.appendPlainText("Error: Edge not supported between types {} and {}".format(from_type, to_type))
                 self._parent.plainTextEdit.appendPlainText("Error: Edge not supported between types {} and {}".format(from_type, to_type))
+                QApplication.restoreOverrideCursor()
                 return
                 return
 
 
         line = GraphicsEdgeItem(from_item, to_item, edge_id)
         line = GraphicsEdgeItem(from_item, to_item, edge_id)
@@ -115,6 +117,9 @@ class CustomScene(QGraphicsScene):
             line.edge_id = edge_id
             line.edge_id = edge_id
             self._parent.plainTextEdit.appendPlainText("Added edge between {} and {} to model".format(from_type, to_type))
             self._parent.plainTextEdit.appendPlainText("Added edge between {} and {} to model".format(from_type, to_type))
 
 
+    QApplication.restoreOverrideCursor()
+
+
     def _handle_keypress_delete(self, selected):
     def _handle_keypress_delete(self, selected):
         if not len(selected) == 1:
         if not len(selected) == 1:
             return
             return

+ 26 - 1
sketchUI/mvops.py

@@ -7,6 +7,7 @@ sys.path.append("..")
 import wrappers.modelverse as mv
 import wrappers.modelverse as mv
 import commons
 import commons
 from sketchUI.graphics_node_item import IconType
 from sketchUI.graphics_node_item import IconType
+from PyQt5.Qt import QRect, QPoint
 
 
 
 
 def add_node(model, node_type):
 def add_node(model, node_type):
@@ -51,14 +52,38 @@ def new_concrete_syntax_model(type_id, icon_type):
         return ""
         return ""
 
 
     icon = mv.instantiate(csm, "Icon")
     icon = mv.instantiate(csm, "Icon")
+    mv.attr_assign(csm, icon, "typeID", type_id)
     if icon_type == IconType.PRIMITIVE:
     if icon_type == IconType.PRIMITIVE:
         mv.attr_assign(csm, icon, "is_primitive", True)
         mv.attr_assign(csm, icon, "is_primitive", True)
-        mv.instantiate(csm, "PrimitiveGroup")
     else:
     else:
         mv.attr_assign(csm, icon, "is_primitive", False)
         mv.attr_assign(csm, icon, "is_primitive", False)
 
 
     return csm
     return csm
 
 
+def add_rect_to_cs(csm, rect):
+    # type: (str, QRect) -> None
+    rect_id = mv.instantiate(csm, "Rectangle")
+    mv.attr_assign(csm, rect_id, "x", rect.topLeft().x())
+    mv.attr_assign(csm, rect_id, "y", rect.topLeft().y())
+    mv.attr_assign(csm, rect_id, "width", rect.width())
+    mv.attr_assign(csm, rect_id, "height", rect.height())
+
+def add_ellipse_to_cs(csm, rect):
+    # type: (str, QRect) -> None
+    elp_id = mv.instantiate(csm, "Ellipse")
+    mv.attr_assign(csm, elp_id, "x", rect.topLeft().x())
+    mv.attr_assign(csm, elp_id, "y", rect.topLeft().y())
+    mv.attr_assign(csm, elp_id, "width", rect.width())
+    mv.attr_assign(csm, elp_id, "height", rect.height())
+
+def add_line_to_cs(csm, p1, p2):
+    # type: (str, QPoint, QPoint) -> None
+    line_id = mv.instantiate(csm, "Line")
+    mv.attr_assign(csm, line_id, "start_x", p1.x())
+    mv.attr_assign(csm, line_id, "start_y", p1.y())
+    mv.attr_assign(csm, line_id, "end_x", p2.x())
+    mv.attr_assign(csm, line_id, "end_y", p2.y())
+
 def add_attribute(model, node_id, key, val):
 def add_attribute(model, node_id, key, val):
     """
     """
     Adds an attribute to node_id in model with key and value
     Adds an attribute to node_id in model with key and value

+ 1 - 1
wrappers/modelverse_SCCD.py

@@ -1,7 +1,7 @@
 """
 """
 Generated by Statechart compiler by Glenn De Jonghe, Joeri Exelmans, Simon Van Mierlo, and Yentl Van Tendeloo (for the inspiration)
 Generated by Statechart compiler by Glenn De Jonghe, Joeri Exelmans, Simon Van Mierlo, and Yentl Van Tendeloo (for the inspiration)
 
 
-Date:   Thu Apr 26 21:43:31 2018
+Date:   Fri Apr 27 20:13:57 2018
 
 
 Model author: Yentl Van Tendeloo
 Model author: Yentl Van Tendeloo
 Model name:   MvK Server
 Model name:   MvK Server