Przeglądaj źródła

renamed type attribute of nodes to typeID since type is internal identifier already, UI can now load and edit existing instance models

Lucas Heer 7 lat temu
rodzic
commit
48f03921ce

+ 53 - 3
commons.py

@@ -1,4 +1,4 @@
-import sys
+import re
 from wrappers import modelverse as mv
 
 
@@ -25,12 +25,12 @@ def all_example_models():
 def all_nodes_with_type(model, typ):
     """ Returns a list of nodes in model model with type typ """
     all_nodes = mv.all_instances(model, "Node")
-    ret = [node for node in all_nodes if mv.read_attrs(model, node)["type"] == typ]
+    ret = [node for node in all_nodes if mv.read_attrs(model, node)["typeID"] == typ]
     return ret
 
 def get_node_type(model, node):
     """ Returns the type attribute of node in model as string"""
-    return mv.read_attrs(model, node)["type"]
+    return mv.read_attrs(model, node)["typeID"]
 
 def get_associations_between(model, node1, node2):
     """ Returns a list of association IDs between the nodes node1 and node2 """
@@ -39,3 +39,53 @@ def get_associations_between(model, node1, node2):
     edges_n2 = mv.read_outgoing(model, node2, "Edge")
     edges_n2.update(mv.read_incoming(model, node2, "Edge"))
     return list(edges_n1.intersection(edges_n2))
+
+def new_instance_model():
+    """
+    Adds a new, empty instance model to the Modelverse.
+    Returns the name of the new model
+    """
+    existing_models = all_instance_models()
+    idx = 1
+    nums = []
+    for model in existing_models:
+        m = model.split("/")[-1]
+        try:
+            idx = int(re.search(r'\d+', m).group())
+            nums.append(idx)
+        except AttributeError:
+            pass
+    if nums:
+        idx = sorted(nums)[-1] + 1
+    im = "models/instance/im{}".format(idx)
+    print("Adding new instance model {}".format(im))
+    mv.model_add(im, "formalisms/graphMM")
+    mid = mv.instantiate(im, "Model")
+    mv.attr_assign(im, mid, "is_example", False)
+    mv.attr_assign(im, mid, "descr", "")
+    return im
+
+def new_example_model():
+    """
+    Adds a new, empty example model to the Modelverse.
+    Returns the name of the new model
+    """
+    existing_models = all_example_models()
+    idx = 1
+    nums = []
+    for model in existing_models:
+        m = model.split("/")[-1]
+        try:
+            idx = int(re.search(r'\d+', m).group())
+            nums.append(idx)
+        except AttributeError:
+            pass
+    if nums:
+        idx = sorted(nums)[-1] + 1
+    exm = "models/instance/ex{}".format(idx)
+    print("Adding new example model {}".format(exm))
+    mv.model_add(exm, "formalisms/graphMM")
+    mid = mv.instantiate(exm, "Model")
+    mv.attr_assign(exm, mid, "is_example", True)
+    mv.attr_assign(exm, mid, "descr", "")
+    return exm

+ 11 - 1
main.py

@@ -122,8 +122,18 @@ if __name__ == "__main__":
 
     #upload_graph_MM()
     #upload_example_models()
+    #upload_instance_model()
+
+    #sys.exit()
+
+    from commons import new_instance_model
+    im = new_instance_model()
+
 
-    run_ui()
+    #run_ui(mode="IM", model="models/instance/im1")
+    run_ui(mode="IM", model=im)
+    print("verify ... ")
+    print(mv.verify(im, "formalisms/graphMM"))
     sys.exit(0)
 
     #upload_instance_model()

+ 4 - 4
models/sketching/example_model_1.mvc

@@ -4,19 +4,19 @@ Model m1 {
 }
 
 Node n1 {
-    type = "Router"
+    typeID = "Router"
 }
 
 Node n2 {
-    type = "PC"
+    typeID = "PC"
 }
 
 Node n3 {
-    type = "PC"
+    typeID = "PC"
 }
 
 Node n4 {
-    type = "NAS"
+    typeID = "NAS"
 }
 
 Edge e1(n1, n2) {

+ 5 - 5
models/sketching/example_model_2.mvc

@@ -4,23 +4,23 @@ Model m2 {
 }
 
 Node n1 {
-    type = "Router"
+    typeID = "Router"
 }
 
 Node n2 {
-    type = "AP"
+    typeID = "AP"
 }
 
 Node n3 {
-    type = "Tablet"
+    typeID = "Tablet"
 }
 
 Node n4 {
-    type = "Phone"
+    typeID = "Phone"
 }
 
 Node n5 {
-    type = "PC"
+    typeID = "PC"
 }
 
 Attribute a1 {

+ 1 - 1
models/sketching/graphMM.mvc

@@ -38,7 +38,7 @@ Class Model {
 
 Class Node {
     name = "Node"
-    type : String
+    typeID : String
 }
 
 Association Edge(Node, Node) {

+ 5 - 5
models/sketching/instance_model.mvc

@@ -4,11 +4,11 @@ Model m1 {
 }
 
 Node n1 {
-    type = "Router"
+    typeID = "Router"
 }
 
 Node n2 {
-    type = "PC"
+    typeID = "PC"
 }
 
 Attribute a1 {
@@ -25,15 +25,15 @@ NodeAttribute na1(n2, a1) {}
 NodeAttribute na2(n1, a2) {}
 
 Node n3 {
-    type = "AP"
+    typeID = "AP"
 }
 
 Node n4 {
-    type = "Phone"
+    typeID = "Phone"
 }
 
 Node n5 {
-    type = "Phone"
+    typeID = "Phone"
 }
 
 Edge e1(n1, n2) {

+ 9 - 0
sketchUI/exm_mainwindow.py

@@ -0,0 +1,9 @@
+from PyQt5.QtWidgets import QMainWindow
+from sketchUI.ui import Ui_MainWindow
+
+
+class EXMMainWindow(QMainWindow, Ui_MainWindow):
+    def __init__(self, model):
+        QMainWindow.__init__(self)
+        self.setupUi(self)
+        self._cur_model = model

+ 4 - 0
sketchUI/custom_edge.py

@@ -1,3 +1,7 @@
+"""
+Edge representation for Qt: Same as a LineItem, but stores items it is connected to
+"""
+
 from PyQt5.QtWidgets import QGraphicsItem, QGraphicsLineItem
 
 

+ 10 - 0
sketchUI/graphics_node_item.py

@@ -0,0 +1,10 @@
+"""
+QGraphics representation class for a node
+"""
+
+from PyQt5.QtWidgets import QGraphicsSimpleTextItem
+
+class GraphicsNodeItem(QGraphicsSimpleTextItem):
+    def __init__(self, node_id):
+        QGraphicsSimpleTextItem.__init__(self)
+        self.node_id = node_id

+ 66 - 16
sketchUI/mainwindow.py

@@ -1,19 +1,20 @@
-from PyQt5.QtWidgets import QMainWindow, QGraphicsItem,\
-     QGraphicsSimpleTextItem, QAction, QActionGroup, QAbstractItemView
+from PyQt5.QtWidgets import QMainWindow, QGraphicsItem, QAction, QActionGroup
 from PyQt5.QtGui import QIcon
 from PyQt5.QtCore import QStateMachine, QState
-from ui import Ui_MainWindow
-from custom_scene import CustomScene, Mode
-from custom_edge import GraphicsEdgeItem
-import mvops
+from sketchUI.ui import Ui_MainWindow
+from sketchUI.im_scene import CustomScene, Mode
+from sketchUI import mvops
+from sketchUI.graphics_node_item import GraphicsNodeItem
+from wrappers.modelverse import element_list_nice
 
 
-class MainWindow(QMainWindow, Ui_MainWindow):
-    def __init__(self):
+class IMMainWindow(QMainWindow, Ui_MainWindow):
+    def __init__(self, model):
         QMainWindow.__init__(self)
         self.setupUi(self)
+        self._cur_model = model
 
-        self._scene = CustomScene()
+        self._scene = CustomScene(model)
         self._scene.set_mode(Mode.SELECT)
         self._scene.setSceneRect(0, 0, 200, 200)
         self.graphicsView.setScene(self._scene)
@@ -24,7 +25,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
         self.setup_toolbar()
         self.setup_state_machine()
 
-        self._cur_model = mvops.new_instance_model()
+        # load the model
+        self._load_model()
 
         #lastly, start the state machine
         self._statemachine.start()
@@ -58,6 +60,34 @@ class MainWindow(QMainWindow, Ui_MainWindow):
         self._statemachine.addState(state_connect)
         self._statemachine.setInitialState(state_select)
 
+    def _load_model(self):
+        """
+        Load the model from the mv and render to screen
+        Likely to be replaced later since rendering ideally already happens in the mv
+        """
+        model = element_list_nice(self._cur_model)
+        if not model:
+            # emtpy model
+            return
+
+        for item in model:
+            typ = item["type"]
+            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:
+            typ = item["type"]
+            if typ == "Edge":
+                target = item["__target"]
+                src = item["__source"]
+                self._add_edge_to_scene(src, target)
+
+
     def _state_connect_entered(self):
         self._scene.set_mode(Mode.CONNECT)
         self._make_items_movable(False)
@@ -75,6 +105,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
         for item in self._scene.items():
             try:
                 # hacky hack because of pythons isinstance fails due to import chaos
+                # edges are not selectable or movable
                 item.__hack__()
                 item.setFlag(QGraphicsItem.ItemIsMovable, False)
                 item.setFlag(QGraphicsItem.ItemIsSelectable, False)
@@ -84,14 +115,33 @@ class MainWindow(QMainWindow, Ui_MainWindow):
             item.setFlag(QGraphicsItem.ItemIsMovable, movable)
             item.setFlag(QGraphicsItem.ItemIsSelectable, movable)
 
-    def _add_text_to_scene(self, text):
-        item = self._scene.addSimpleText(text)
-        item.setPos(0, 0)
+    def _add_node_to_scene(self, node_id, node_type, x=0, y=0):
+        item = GraphicsNodeItem(node_id)
+        item.setText(node_type)
+        item.setPos(x, y)
         item.setFlag(QGraphicsItem.ItemIsMovable, True)
         item.setFlag(QGraphicsItem.ItemIsSelectable, True)
+        self._scene.addItem(item)
+
+    def _add_edge_to_scene(self, from_id, to_id):
+        from_item = None
+        to_item = None
+        for item in self._scene.items():
+            try:
+                node_id = item.node_id
+            except AttributeError:
+                # no node item, continue
+                continue
+            if node_id == from_id:
+                from_item = item
+                continue
+            if node_id == to_id:
+                to_item = item
+
+        self._scene.draw_edge(from_item, to_item, is_new=False)
 
     def _on_list_item_clicked(self, action):
-        # render to scene
-        self._add_text_to_scene(action.text())
         # add new node to model in mv
-        mvops.add_node(self._cur_model, action.text())
+        node_id = mvops.add_node(self._cur_model, action.text())
+        # render to scene
+        self._add_node_to_scene(node_id, action.text())

+ 16 - 10
sketchUI/custom_scene.py

@@ -1,8 +1,8 @@
 from enum import Enum
 from PyQt5.QtWidgets import QGraphicsScene, QGraphicsItem
 from PyQt5.Qt import Qt, QTransform
-from custom_edge import GraphicsEdgeItem
-import mvops
+from sketchUI.graphics_edge_item import GraphicsEdgeItem
+from sketchUI import mvops
 
 
 class Mode(Enum):
@@ -11,10 +11,11 @@ class Mode(Enum):
 
 
 class CustomScene(QGraphicsScene):
-    def __init__(self):
+    def __init__(self, model):
         QGraphicsScene.__init__(self)
         self._mode = None  # set by mainwindow at start
         self._from_item = None  # store the item to draw the connecting line from
+        self._cur_model = model  # the mv model we operate on
 
     def set_mode(self, mode):
         self._mode = mode
@@ -24,7 +25,6 @@ class CustomScene(QGraphicsScene):
             item = self.itemAt(event.scenePos(), QTransform())
             if not item:
                 return
-            print("Clicked on {}".format(item))
             self._from_item = item
 
         QGraphicsScene.mousePressEvent(self, event)
@@ -34,8 +34,7 @@ class CustomScene(QGraphicsScene):
             item = self.itemAt(event.scenePos(), QTransform())
             if not item:
                 return
-            print("Released on {}".format(item))
-            self._draw_edge(self._from_item, item)
+            self.draw_edge(self._from_item, item, is_new=True)
 
         if event.button() == Qt.LeftButton and self._mode == Mode.SELECT:
             item = self.itemAt(event.scenePos(), QTransform())
@@ -50,14 +49,21 @@ class CustomScene(QGraphicsScene):
 
         QGraphicsScene.mouseReleaseEvent(self, event)
 
-    def _draw_edge(self, from_item, to_item):
-        if not mvops.is_edge_supported(from_item.text(), to_item.text()):
-            print("edge not supported")
-            return
+    def draw_edge(self, from_item, to_item, is_new):
+        from_type = from_item.text()
+        to_type = to_item.text()
+        if is_new:
+            if not mvops.is_edge_supported(from_type, to_type):
+                print("edge not supported between {} and {}".format(from_type, to_type))
+                return
 
         line = GraphicsEdgeItem(from_item, to_item)
         line.setFlag(QGraphicsItem.ItemIsMovable, False)
         line.setFlag(QGraphicsItem.ItemIsSelectable, False)
         self.addItem(line)
         line.redraw()
+
+        if is_new:
+            mvops.add_edge(self._cur_model, from_item.node_id, to_item.node_id)
+
         self._from_item = None

+ 12 - 4
sketchUI/main.py

@@ -1,11 +1,18 @@
 import sys
 from PyQt5.QtWidgets import QApplication
-from mainwindow import MainWindow
+from sketchUI.im_mainwindow import IMMainWindow
+from sketchUI.exm_mainwindow import EXMMainWindow
 
 
-def run_ui():
+def run_ui(mode, model):
     app = QApplication(sys.argv)
-    win = MainWindow()
+    if mode == "IM":
+        win = IMMainWindow(model)
+    elif mode == "EXM":
+        win = EXMMainWindow(model)
+    else:
+        print("run_ui: unknown mode {}".format(mode))
+        return
     win.show()
     app.exec_()
     app.deleteLater()
@@ -14,4 +21,5 @@ def run_ui():
 
 if __name__ == "__main__":
     sys.path.append(".")
-    sys.exit(run())
+    sys.exit(run_ui(mode="IM", model="models/instance/im1"))
+

+ 11 - 22
sketchUI/mvops.py

@@ -4,7 +4,6 @@ Modelverse operations used by the UI
 
 import sys
 sys.path.append("..")
-import re
 import wrappers.modelverse as mv
 import commons
 
@@ -19,7 +18,6 @@ def get_available_types():
             types.append(typ)
     return list(set(types))
 
-
 def is_edge_supported(from_type, to_type):
     """
     True if an association between from_type and to_type exists in any example model
@@ -36,25 +34,16 @@ def is_edge_supported(from_type, to_type):
                     return True
     return False
 
-def new_instance_model():
-    """
-    Adds a new, empty instance model to the Modelverse.
-    Returns the name if the new model
-    """
-    existing_models = commons.all_instance_models()
-    idx = 1
-    for model in existing_models:
-        m = model.split("/")[-1]
-        try:
-            idx = int(re.search(r'\d+', m).group()) + 1
-        except AttributeError:
-            idx = 0
-    im = "models/instance/im{}".format(idx)
-    print("Adding new instance model {}".format(im))
-    mv.model_add(im, "formalisms/graphMM")
-    return im
-
 def add_node(model, node_type):
-    # add new node to model "model" with type attribute "node_type"
+    """ Adds new node to model "model" with type attribute "node_type" """
     node_id = mv.instantiate(model, "Node")
-    mv.attr_assign(model, node_id, "type", node_type)
+    mv.attr_assign(model, node_id, "typeID", node_type)
+    print("Added node with id {} and type {} to model {}".format(node_id, node_type, model))
+    return node_id
+
+def add_edge(model, from_id, to_id, directed=False):
+    """ Adds an edge to model "model" between the nodes "from_id" and "to_id" """
+    edge_id = mv.instantiate(model, "Edge", (from_id, to_id))
+    mv.attr_assign(model, edge_id, "directed", directed)
+    print("Added edge between {} and {} to model".format(from_id, to_id))
+    return edge_id

+ 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:   Sat Apr 14 12:19:59 2018
+Date:   Wed Apr 18 12:31:05 2018
 
 Model author: Yentl Van Tendeloo
 Model name:   MvK Server