Selaa lähdekoodia

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

Lucas Heer 7 vuotta sitten
vanhempi
commit
5f30d83283

+ 7 - 4
main.py

@@ -1,7 +1,7 @@
 import sys
 import argparse
 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 evolution import upload_ops
 
@@ -92,7 +92,7 @@ if __name__ == "__main__":
     mode = None
     if args.im and args.exm or (not args.im and not args.exm):
         print("Choose either im or exm")
-        sys.exit()
+        sys.exit(0)
     if args.im:
         mode = "IM"
     else:
@@ -101,7 +101,10 @@ if __name__ == "__main__":
     # open existing model?
     if 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:
         # no model to open, create an empty one
         if mode == "IM":
@@ -109,8 +112,8 @@ if __name__ == "__main__":
         else:
             model = new_example_model()
 
+    print("Starting UI ...")
     run_ui(mode, model)
 
     print("verify ... ")
     print(mv.verify(model, "formalisms/graphMM"))
-    sys.exit(0)

+ 0 - 11
models/sketching/ConsynMM.mvc

@@ -42,10 +42,6 @@ Class Image {
     data : String
 }
 
-Class PrimitiveGroup {
-    name = "PrimitiveGroup"
-}
-
 Class Primitive {
     name = "Primitive"
 }
@@ -73,10 +69,3 @@ Class Line : Primitive {
     end_x : 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
 }
 
-PrimitiveGroup grp {}
-
 Rectangle r1 {
     x = 0
     y = 0
@@ -18,6 +16,3 @@ Ellipse c1 {
     width = 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.setIcon(QIcon("sketchUI/icons/pen.svg"))
-        self.pen_action.setCheckable(True)
+        self.pen_action.setCheckable(False)
 
         action_group = QActionGroup(self)
         action_group.setExclusive(True)

+ 24 - 6
sketchUI/exm_scene.py

@@ -1,9 +1,11 @@
 from enum import Enum
 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 sketchUI.graphics_edge_item import GraphicsEdgeItem
 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 evolution.node_ops import NodeAdd, NodeDelete, NodeRetype
 import commons
@@ -115,7 +117,7 @@ class SketchScene(QGraphicsScene):
             end_point = event.scenePos()
 
             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())
                 line.setPen(QPen(Qt.black, 2, Qt.SolidLine))
                 self.addItem(line)
@@ -186,11 +188,15 @@ class SketchScene(QGraphicsScene):
             selected = self.selectedItems()
             self.clearSelection()
 
-            group = self.createItemGroup(selected)
+            group = SketchGroup()
+            for item in selected:
+                group.addToGroup(item)
+
             bb_rect = QGraphicsRectItem(group.boundingRect())
             bb_rect.setData(0, "groupBBox")  # identifier for "does not belong to the actual sketch"
             bb_rect.setPen(QPen(Qt.gray, 1, Qt.DashLine))
             group.addToGroup(bb_rect)
+            self.addItem(group)
             group.setFlag(QGraphicsItem.ItemIsSelectable, True)
             group.setFlag(QGraphicsItem.ItemIsMovable, True)
 
@@ -201,7 +207,7 @@ class SketchScene(QGraphicsScene):
             if len(selected) != 1:
                 return
             item = selected[0]
-            if isinstance(item, QGraphicsItemGroup):
+            if isinstance(item, SketchGroup):
                 self._handle_keypress_type_on_group(item)
             elif isinstance(item, GraphicsNodeItem):
                 self._handle_keypress_type_on_node(item)
@@ -257,7 +263,7 @@ class SketchScene(QGraphicsScene):
         QApplication.restoreOverrideCursor()
 
     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
         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]
 
         self._parent.plainTextEdit.appendPlainText("Capturing concrete syntax of group ...")
+        self._parent.plainTextEdit.repaint()
         # create concrete syntax model for the sketched elements
         csm = mvops.new_concrete_syntax_model(node_type, IconType.PRIMITIVE)
         if not csm:
@@ -300,8 +307,19 @@ class SketchScene(QGraphicsScene):
             if item.data(0) == "groupBBox":
                 # just the bounding box from the group, ignore
                 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)
         nodeitem = GraphicsNodeItem(nodeid, node_type, csm_content)
         nodeitem.setPos(group.scenePos())

+ 2 - 2
sketchUI/graphics_node_item.py

@@ -93,8 +93,8 @@ class GraphicsNodeItem(QGraphicsItem):
             # a set of primitives
             for item in self._consyn:
                 item_type = item["type"]
-                # ignore the "useless"
-                if item_type in ["Icon", "PrimitiveGroup", "PrimitiveGroupPrimitive"]:
+                # ignore the "useless" Icon class
+                if item_type == "Icon":
                     continue
 
                 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
             return
 
+        x_pos = -150
+        y_pos = 70
         for item in model:
             typ = item["type"]
             if typ == "Node":
                 # first, draw all nodes
                 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:
             # now the edges

+ 5 - 0
sketchUI/im_scene.py

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

+ 26 - 1
sketchUI/mvops.py

@@ -7,6 +7,7 @@ sys.path.append("..")
 import wrappers.modelverse as mv
 import commons
 from sketchUI.graphics_node_item import IconType
+from PyQt5.Qt import QRect, QPoint
 
 
 def add_node(model, node_type):
@@ -51,14 +52,38 @@ def new_concrete_syntax_model(type_id, icon_type):
         return ""
 
     icon = mv.instantiate(csm, "Icon")
+    mv.attr_assign(csm, icon, "typeID", type_id)
     if icon_type == IconType.PRIMITIVE:
         mv.attr_assign(csm, icon, "is_primitive", True)
-        mv.instantiate(csm, "PrimitiveGroup")
     else:
         mv.attr_assign(csm, icon, "is_primitive", False)
 
     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):
     """
     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)
 
-Date:   Thu Apr 26 21:43:31 2018
+Date:   Fri Apr 27 20:13:57 2018
 
 Model author: Yentl Van Tendeloo
 Model name:   MvK Server