Bläddra i källkod

clean up code, added command line arguments to main, add/delete/retype node evolution scenarios integrated in UI

Lucas Heer 7 år sedan
förälder
incheckning
914b8d2572

+ 11 - 4
evolution/node_ops.py

@@ -7,10 +7,11 @@ from commons import *
 class NodeAdd(object):
     def __init__(self):
         self._node_type = ""
+        self._node_id = None
 
     def execute(self, model, node_type, local):
         """
-        Add a new node with ID node and type node_type to model model.
+        Add a new node with type node_type to model model.
         If local is true, the node is only added to the model.
         If local is false, the node will be added to all models.
         """
@@ -22,9 +23,15 @@ class NodeAdd(object):
             for m in all_models():
                 self.execute(m, node_type, local=True)
 
+    def get_node_id(self):
+        # attention: in case of global execution, this method returns only the last added node in the last model
+        return self._node_id
+
     def _callback(self, model):
-        ID = mv.instantiate(model, "gm/Node")
-        mv.attr_assign(model, ID, "type", self._node_type)
+        node_id = mv.instantiate(model, "gm/Node")
+        mv.attr_assign(model, node_id, "typeID", self._node_type)
+        self._node_id = node_id
+
 
 class NodeDelete(object):
     def __init__(self):
@@ -83,4 +90,4 @@ class NodeRetype(object):
                     self.execute(m, node, new_type, local=True)
 
     def _callback(self, model):
-        mv.attr_assign(model, "gm/"+self._node, "type", self._new_type)
+        mv.attr_assign(model, "gm/"+self._node, "typeID", self._new_type)

+ 0 - 0
evolution/retype.py


+ 38 - 120
main.py

@@ -1,9 +1,9 @@
+import sys
+import argparse
 import wrappers.modelverse as mv
-from verifier import verify
-from evolution.node_ops import NodeDelete, NodeRetype, NodeAdd
-from evolution.edge_ops import EdgeAdd, EdgeDel
-from evolution.upload_ops import upload_evolution_ops
+from commons import new_instance_model, new_example_model
 from sketchUI.main import run_ui
+from evolution import upload_ops
 
 
 def upload_graph_MM():
@@ -45,129 +45,47 @@ def upload_instance_model():
     mv.model_add("models/instance/im1", "formalisms/graphMM", content)
     mv.verify("models/instance/im1", "formalisms/graphMM")
 
-def upload_rename_alc():
-    print("Loading rename ALC ...")
-    try:
-        mv.model_delete("models/graph_ops/rename")
-    except mv.UnknownLocation:
-        pass
-
-    mv.transformation_add_AL({"instance": "formalisms/graphMM"}, {"rename_out": "formalisms/graphMM"},
-                             "models/graph_ops/rename", open("models/lucas/rename.alc").read())
-
-
-def exec_rename_alc():
-    print("Executing rename ALC ...")
-    try:
-        mv.model_delete("models/rename_out")
-    except mv.UnknownLocation:
-        pass
-
-    """
-    ctrl = Controller(keep_running=False)
-    thrd = Thread(target=ctrl.start)
-    thrd.daemon = True
-    thrd.start()
-    """
-
-    mv.transformation_execute_AL("models/graph_ops/rename",
-                                 {"instance": "models/instance/im1"},
-                                 {"rename_out": "models/rename_out"})
-
-    """
-    # TODO: Wait until ALC is ready to accept input, maybe there is a way to wait for output events?
-    try:
-        while True:
-            # pass data to ALC via statechart
-            inp = raw_input("Input:")
-            ctrl.addInput(Event("data_inp", "datap", [inp]))
-    except KeyboardInterrupt:
-        ctrl.addInput(Event("terminate", "inp"))
-    print("Done")
-    """
-
-"""
-def upload_verify_alc():
-    print("Loading verify ALC ...")
-    try:
-        mv.model_delete("models/graph_ops/verify")
-    except mv.UnknownError:
-        pass
-    mv.transformation_add_AL({"instance": "formalisms/graphMM"}, {"out": "formalisms/graphMM"},
-                             "models/graph_ops/verify", open("models/lucas/verify.alc").read())
-
-
-def exec_verify_alc():
-    print("Executing verify ALC ...")
-    try:
-        mv.model_delete("models/verify_out")
-    except mv.UnknownError:
-        pass
-
-    mv.transformation_execute_AL("models/graph_ops/verify",
-                                 {"instance": "models/instance/im1"},
-                                 {"out": "models/verify_out"})
-
-"""
-
-def _callback(model):
-    print("foo")
-
 
 if __name__ == "__main__":
-    import sys
+    parser = argparse.ArgumentParser()
+    parser.add_argument("-u", action="store_true", default=False, help="Upload models")
+    parser.add_argument("-im", action="store_true", default=False, help="Instance modeling")
+    parser.add_argument("-exm", action="store_true", default=False, help="Example modeling")
+    parser.add_argument("-m", help="Model to open")
+    args = parser.parse_args()
+
     mv.init()
     mv.login("admin", "admin")
 
-    upload_graph_MM()
-    #upload_example_models()
-    #upload_instance_model()
+    # load modelverse with necessary models?
+    if args.u:
+        upload_graph_MM()
+        upload_example_models()
+        upload_instance_model()
+        upload_ops.upload_evolution_ops()
+
+    # determine modeling mode
+    mode = None
+    if args.im and args.exm or (not args.im and not args.exm):
+        print("Choose either im or exm")
+        sys.exit()
+    if args.im:
+        mode = "IM"
+    else:
+        mode = "EXM"
 
-    #sys.exit()
+    # open existing model?
+    if args.m:
+        model = args.m
+    else:
+        # no model to open, create an emtpy one
+        if mode == "IM":
+            model = new_instance_model()
+        else:
+            model = new_example_model()
 
-    from commons import new_example_model
-    exm = new_example_model()
+    run_ui(mode, model)
 
-    run_ui(mode="EXM", model=exm)
-    #run_ui(mode="IM", model="models/instance/im1")
     print("verify ... ")
-    print(mv.verify(exm, "formalisms/graphMM"))
+    print(mv.verify(model, "formalisms/graphMM"))
     sys.exit(0)
-
-    #upload_instance_model()
-    # evolution with manual transformations in python code
-    #print("Uploading evolution operations ...")
-    #upload_evolution_ops()
-    #edgedel = EdgeDel()
-    #print("Performing local edge del ...")
-    #edgedel.execute("models/example/ex2", "e3", local=True, check_last=True)
-    #print(mv.element_list_nice("models/example/ex1"))
-    #print(mv.element_list_nice("models/example/ex2"))
-    #print(mv.element_list_nice("models/instance/im1"))
-
-    """
-    print("Adding manual transformation ...")
-    mv.transformation_add_MANUAL({}, {"gm":"formalisms/graphMM"}, "graph_ops/new_example_model")
-    print("Adding process model ...")
-    mv.model_add("process/my_pm", "formalisms/ProcessModel", open("models/sketching/process_model.mvc").read())
-    print("Executing process model ...")
-    mv.process_execute("process/my_pm", {"exm":"models/example/ex1"}, {"graph_ops/new_example_model":_callback})
-    """
-
-    """
-    print("Retyping node ...")
-    retype_node("models/example/ex1", "n2", "PesonalComputer", local=False)
-
-    upload_rename_alc()
-    exec_rename_alc()
-    upload_verify_alc()
-    exec_verify_alc()
-
-    # verification with pure python wrapper calls
-    print("Verifying instance model ...")
-    ret = verify("models/instance/im1")
-    if not ret["OK"]:
-        print(ret["error"])
-    else:
-        print("Verify OK")
-    """

+ 2 - 3
sketchUI/exm_mainwindow.py

@@ -8,11 +8,13 @@ from sketchUI.graphics_node_item import GraphicsNodeItem
 from wrappers.modelverse import element_list_nice
 
 
+
 class EXMMainWindow(QMainWindow, Ui_MainWindow):
     def __init__(self, model):
         QMainWindow.__init__(self)
         self.setupUi(self)
         self.setWindowTitle(model)
+        self.menuActionVerify.setDisabled(True)
         self._cur_model = model
 
         self._scene = SketchScene(model, self)
@@ -202,9 +204,6 @@ class EXMMainWindow(QMainWindow, Ui_MainWindow):
             if typ == "Node":
                 # first, draw all nodes
                 node_type = item["typeID"]
-                # workaround since get_attrs returns strings with " and list_nice doesnt
-                if not node_type.startswith("\""):
-                    node_type = "\"" + node_type + "\""
                 self._add_node_to_scene(item["id"], node_type)
 
         for item in model:

+ 107 - 20
sketchUI/exm_scene.py

@@ -1,12 +1,12 @@
 from enum import Enum
 from PyQt5.QtWidgets import QGraphicsScene, QGraphicsItem, QGraphicsLineItem, QGraphicsRectItem, \
-    QGraphicsEllipseItem, QInputDialog
+    QGraphicsEllipseItem, QInputDialog, QGraphicsItemGroup
 from PyQt5.Qt import Qt, QPointF, QPen, QTransform
-from PyQt5.QtCore import pyqtSignal
 from sketchUI.graphics_edge_item import GraphicsEdgeItem
 from sketchUI.graphics_node_item import GraphicsNodeItem
 from sketchUI import mvops
-
+from evolution.node_ops import NodeAdd, NodeDelete, NodeRetype
+from commons import all_nodes_with_type
 
 class Mode(Enum):
     SELECT = 0
@@ -149,8 +149,7 @@ class SketchScene(QGraphicsScene):
             return
         # "del" deletes all selected items
         if event.key() == Qt.Key_Delete:
-            for item in self.selectedItems():
-                self.removeItem(item)
+            self._handle_keypress_delete(self.selectedItems())
 
         # "G" groups selected items
         elif event.key() == Qt.Key_G:
@@ -164,30 +163,118 @@ class SketchScene(QGraphicsScene):
             group.setFlag(QGraphicsItem.ItemIsSelectable, True)
             group.setFlag(QGraphicsItem.ItemIsMovable, True)
 
+        # "T" lets user type selected element
         elif event.key() == Qt.Key_T:
+            # exactly one element that is a group must be selected
             selected = self.selectedItems()
             if len(selected) != 1:
                 return
             item = selected[0]
-            # "T" lets the user type the selected item
-            text, ok = QInputDialog.getText(self._parent, "Text input", "Enter type")
-            if ok and text:
-                print("Typing item {} to type {}".format(item, text))
-                if text in mvops.get_available_types():
-                    print("Already such a type: {}".format(text))
-                    return
-                self._type_item(item, text)
+            if isinstance(item, QGraphicsItemGroup):
+                self._handle_keypress_type_on_group(item)
+            elif isinstance(item, GraphicsNodeItem):
+                self._handle_keypress_type_on_node(item)
+            else:
+                print("Cannot type element {}".format(item))
+
         else:
             QGraphicsScene.keyPressEvent(self, event)
 
-    def _type_item(self, item, typ):
-        # typing means elevating it to a real node
-        nid = mvops.add_node(self._cur_model, typ)
-        nodeitem = GraphicsNodeItem(nid)
-        nodeitem.setText(typ)
-        nodeitem.setPos(item.x(), item.y())
+    def _handle_keypress_type_on_node(self, item):
+        # type a node = retype it
+        node_type, ok = QInputDialog.getText(self._parent, "Retype node", "New type", text=item.text())
+        if not ok:
+            # user canceled
+            return
+        if node_type:
+            print("Reyping item {} to type {}".format(item, node_type))
+            if node_type in mvops.get_available_types():
+                print("Error: Already such a type: {}".format(node_type))
+                return
+
+        # local or global retype?
+        scope, ok = QInputDialog.getItem(self._parent, "Select scope", "Scope", ["Local", "Global"])
+        if not ok:
+            return
+
+        retype_handler = NodeRetype()
+        if scope == "Global":
+            retype_handler.execute(self._cur_model, item.node_id, node_type, local=False)
+        else:
+            retype_handler.execute(self._cur_model, item.node_id, node_type, local=True)
+
+        # rename on screen
+        for node_item in self.items():
+            if not isinstance(node_item, GraphicsNodeItem):
+                continue
+            if item.text() == node_item.text():
+                node_item.setText(node_type)
+
+        # update list widget
+        self._parent.populate_types()
+
+    def _handle_keypress_type_on_group(self, group):
+        # type the selected group
+        # get the type from the user
+        node_type, ok = QInputDialog.getText(self._parent, "Type node", "Enter type")
+        if not ok:
+            # user canceled
+            return
+        if node_type:
+            print("Typing item {} to type {}".format(group, node_type))
+            if node_type in mvops.get_available_types():
+                print("Error: Already such a type: {}".format(node_type))
+                return
+
+        # perform add local or global?
+        scope, ok = QInputDialog.getItem(self._parent, "Select scope", "Scope", ["Local", "Global"])
+        if not ok:
+            return
+
+        # add the node to the model
+        add_handler = NodeAdd()
+        if scope == "Global":
+            add_handler.execute(self._cur_model, node_type, local=False)
+            # Hack: Get node id of newly added node in current model
+            nodeid = all_nodes_with_type(self._cur_model, node_type)[0]
+        else:
+            add_handler.execute(self._cur_model, node_type, local=True)
+            nodeid = add_handler.get_node_id()
+
+        # update view
+        nodeitem = GraphicsNodeItem(nodeid)
+        nodeitem.setText(node_type)
+        nodeitem.setPos(group.x(), group.y())
         nodeitem.setFlag(QGraphicsItem.ItemIsSelectable, True)
         nodeitem.setFlag(QGraphicsItem.ItemIsMovable, True)
-        self.removeItem(item)
+        self.removeItem(group)
         self.addItem(nodeitem)
         self._parent.populate_types()
+
+    def _handle_keypress_delete(self, selected):
+        del_hander = NodeDelete()
+        for item in selected:
+            # only delete nodes, edges are taken care of
+            if not isinstance(item, GraphicsNodeItem):
+                continue
+            # when deleting a node, local or global?
+            scope, ok = QInputDialog.getItem(self._parent, "Select scope", "Scope", ["Local", "Global"])
+            if not ok:
+                return
+            if scope == "Global":
+                # global language evolution, so delete node with same type everywhere
+                del_hander.execute(self._cur_model, item.node_id, local=False, check_if_last=False)
+            else:
+                # just local, delete from this model only
+                del_hander.execute(self._cur_model, item.node_id, local=True, check_if_last=True)
+
+            # in view, delete edges that were connected to this node as well
+            for edge in self.items():
+                if not isinstance(edge, GraphicsEdgeItem):
+                    continue
+                if edge.from_item.node_id == item.node_id or edge.to_item.node_id == item.node_id:
+                    self.removeItem(edge)
+
+            self.removeItem(item)
+            # repopulate available types in view since they might have changed
+            self._parent.populate_types()

+ 4 - 4
sketchUI/graphics_edge_item.py

@@ -8,14 +8,14 @@ from PyQt5.QtWidgets import QGraphicsItem, QGraphicsLineItem
 class GraphicsEdgeItem(QGraphicsLineItem):
     def __init__(self, from_item, to_item):
         QGraphicsLineItem.__init__(self)
-        self._from_item = from_item
-        self._to_item = to_item
+        self.from_item = from_item
+        self.to_item = to_item
         self.setFlag(QGraphicsItem.ItemIsMovable, False)
         self.setFlag(QGraphicsItem.ItemIsSelectable, False)
 
     def redraw(self):
-        from_pos = self._from_item.scenePos()
-        to_pos = self._to_item.scenePos()
+        from_pos = self.from_item.scenePos()
+        to_pos = self.to_item.scenePos()
         self.setLine(from_pos.x(), from_pos.y(), to_pos.x(), to_pos.y())
 
     # hack to work around failing isinstance check due to import chaos

+ 7 - 3
sketchUI/im_mainwindow.py

@@ -76,9 +76,6 @@ class IMMainWindow(QMainWindow, Ui_MainWindow):
             if typ == "Node":
                 # first, draw all nodes
                 node_type = item["typeID"]
-                # workaround since get_attrs returns strings with " and list_nice doesnt
-                if not node_type.startswith("\""):
-                    node_type = "\"" + node_type + "\""
                 self._add_node_to_scene(item["id"], node_type)
 
         for item in model:
@@ -96,6 +93,7 @@ class IMMainWindow(QMainWindow, Ui_MainWindow):
     def _state_select_entered(self):
         self._scene.set_mode(Mode.SELECT)
         self._make_items_movable(True)
+        self._enable_box_select(True)
         self._enable_list_widget(True)
 
     def _enable_list_widget(self, enabled):
@@ -115,6 +113,12 @@ class IMMainWindow(QMainWindow, Ui_MainWindow):
             item.setFlag(QGraphicsItem.ItemIsMovable, movable)
             item.setFlag(QGraphicsItem.ItemIsSelectable, movable)
 
+    def _enable_box_select(self, enable):
+        if enable:
+            self.graphicsView.setDragMode(QGraphicsView.RubberBandDrag)
+        else:
+            self.graphicsView.setDragMode(QGraphicsView.NoDrag)
+
     def _add_node_to_scene(self, node_id, node_type, x=0, y=0):
         item = GraphicsNodeItem(node_id)
         item.setText(node_type)

+ 21 - 2
sketchUI/ui.py

@@ -11,7 +11,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
 class Ui_MainWindow(object):
     def setupUi(self, MainWindow):
         MainWindow.setObjectName("MainWindow")
-        MainWindow.resize(755, 515)
+        MainWindow.resize(991, 678)
         self.centralwidget = QtWidgets.QWidget(MainWindow)
         self.centralwidget.setObjectName("centralwidget")
         self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
@@ -20,12 +20,19 @@ class Ui_MainWindow(object):
         self.graphicsView.setObjectName("graphicsView")
         self.horizontalLayout.addWidget(self.graphicsView)
         self.listWidget = QtWidgets.QListWidget(self.centralwidget)
+        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+        sizePolicy.setHorizontalStretch(0)
+        sizePolicy.setVerticalStretch(0)
+        sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth())
+        self.listWidget.setSizePolicy(sizePolicy)
         self.listWidget.setObjectName("listWidget")
         self.horizontalLayout.addWidget(self.listWidget)
         MainWindow.setCentralWidget(self.centralwidget)
         self.menubar = QtWidgets.QMenuBar(MainWindow)
-        self.menubar.setGeometry(QtCore.QRect(0, 0, 755, 27))
+        self.menubar.setGeometry(QtCore.QRect(0, 0, 991, 26))
         self.menubar.setObjectName("menubar")
+        self.menuFile = QtWidgets.QMenu(self.menubar)
+        self.menuFile.setObjectName("menuFile")
         MainWindow.setMenuBar(self.menubar)
         self.statusbar = QtWidgets.QStatusBar(MainWindow)
         self.statusbar.setObjectName("statusbar")
@@ -33,12 +40,24 @@ class Ui_MainWindow(object):
         self.toolBar = QtWidgets.QToolBar(MainWindow)
         self.toolBar.setObjectName("toolBar")
         MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar)
+        self.menuActionVerify = QtWidgets.QAction(MainWindow)
+        self.menuActionVerify.setObjectName("menuActionVerify")
+        self.menuActionExit = QtWidgets.QAction(MainWindow)
+        self.menuActionExit.setObjectName("menuActionExit")
+        self.menuFile.addAction(self.menuActionVerify)
+        self.menuFile.addSeparator()
+        self.menuFile.addAction(self.menuActionExit)
+        self.menubar.addAction(self.menuFile.menuAction())
 
         self.retranslateUi(MainWindow)
+        self.menuActionExit.triggered.connect(MainWindow.close)
         QtCore.QMetaObject.connectSlotsByName(MainWindow)
 
     def retranslateUi(self, MainWindow):
         _translate = QtCore.QCoreApplication.translate
         MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
+        self.menuFile.setTitle(_translate("MainWindow", "File"))
         self.toolBar.setWindowTitle(_translate("MainWindow", "toolBar"))
+        self.menuActionVerify.setText(_translate("MainWindow", "Verify"))
+        self.menuActionExit.setText(_translate("MainWindow", "Exit"))
 

+ 49 - 6
sketchUI/ui.ui

@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>755</width>
-    <height>515</height>
+    <width>991</width>
+    <height>678</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -19,7 +19,14 @@
      <widget class="QGraphicsView" name="graphicsView"/>
     </item>
     <item>
-     <widget class="QListWidget" name="listWidget"/>
+     <widget class="QListWidget" name="listWidget">
+      <property name="sizePolicy">
+       <sizepolicy hsizetype="Minimum" vsizetype="Expanding">
+        <horstretch>0</horstretch>
+        <verstretch>0</verstretch>
+       </sizepolicy>
+      </property>
+     </widget>
     </item>
    </layout>
   </widget>
@@ -28,10 +35,19 @@
     <rect>
      <x>0</x>
      <y>0</y>
-     <width>755</width>
-     <height>27</height>
+     <width>991</width>
+     <height>26</height>
     </rect>
    </property>
+   <widget class="QMenu" name="menuFile">
+    <property name="title">
+     <string>File</string>
+    </property>
+    <addaction name="menuActionVerify"/>
+    <addaction name="separator"/>
+    <addaction name="menuActionExit"/>
+   </widget>
+   <addaction name="menuFile"/>
   </widget>
   <widget class="QStatusBar" name="statusbar"/>
   <widget class="QToolBar" name="toolBar">
@@ -45,7 +61,34 @@
     <bool>false</bool>
    </attribute>
   </widget>
+  <action name="menuActionVerify">
+   <property name="text">
+    <string>Verify</string>
+   </property>
+  </action>
+  <action name="menuActionExit">
+   <property name="text">
+    <string>Exit</string>
+   </property>
+  </action>
  </widget>
  <resources/>
- <connections/>
+ <connections>
+  <connection>
+   <sender>menuActionExit</sender>
+   <signal>triggered()</signal>
+   <receiver>MainWindow</receiver>
+   <slot>close()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>-1</x>
+     <y>-1</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>495</x>
+     <y>338</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
 </ui>

+ 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:   Fri Apr 20 10:15:11 2018
+Date:   Fri Apr 20 14:37:38 2018
 
 Model author: Yentl Van Tendeloo
 Model name:   MvK Server