Browse Source

added node highlighting for verify and select

Lucas Heer 7 years ago
parent
commit
6fb919a04e

+ 6 - 2
sketchUI/exm_mainwindow.py

@@ -1,6 +1,7 @@
 from PyQt5.QtWidgets import QMainWindow, QAction, QActionGroup, QGraphicsItem, QGraphicsView, QTableWidgetItem
 from PyQt5.QtWidgets import QMainWindow, QAction, QActionGroup, QGraphicsItem, QGraphicsView, QTableWidgetItem
 from PyQt5.QtGui import QIcon
 from PyQt5.QtGui import QIcon
 from PyQt5.QtCore import QStateMachine, QState, Qt
 from PyQt5.QtCore import QStateMachine, QState, Qt
+from PyQt5.Qt import QApplication
 from sketchUI.ui import Ui_MainWindow
 from sketchUI.ui import Ui_MainWindow
 from sketchUI.exm_scene import SketchScene, Mode
 from sketchUI.exm_scene import SketchScene, Mode
 from sketchUI import mvops
 from sketchUI import mvops
@@ -300,15 +301,17 @@ class EXMMainWindow(QMainWindow, Ui_MainWindow):
         mvops.add_attribute(self._cur_model, selected_node.node_id, key, val)
         mvops.add_attribute(self._cur_model, selected_node.node_id, key, val)
 
 
     def _on_delete_model_clicked(self, event):
     def _on_delete_model_clicked(self, event):
-        # delete the open example model
+        # delete the open example model by iteratively deleting all nodes
         self.plainTextEdit.appendPlainText("Deleting model ...")
         self.plainTextEdit.appendPlainText("Deleting model ...")
         self.plainTextEdit.repaint()
         self.plainTextEdit.repaint()
 
 
+        QApplication.setOverrideCursor(Qt.WaitCursor)
         delhander = NodeDelete()
         delhander = NodeDelete()
         for item in self._scene.items():
         for item in self._scene.items():
             if not isinstance(item, GraphicsNodeItem):
             if not isinstance(item, GraphicsNodeItem):
                 continue
                 continue
-            print("Deleting node {}".format(item.node_id))
+            self.plainTextEdit.appendPlainText("Deleting node {}".format(item.node_id))
+            self.plainTextEdit.repaint()
             delhander.execute(self._cur_model, item.node_id, local=True, check_if_last=True)
             delhander.execute(self._cur_model, item.node_id, local=True, check_if_last=True)
 
 
         # delete the empty model from the Modelverse
         # delete the empty model from the Modelverse
@@ -316,3 +319,4 @@ class EXMMainWindow(QMainWindow, Ui_MainWindow):
             self.close()
             self.close()
         else:
         else:
             self.plainTextEdit.appendPlainText("Error: Delete failed")
             self.plainTextEdit.appendPlainText("Error: Delete failed")
+            QApplication.restoreOverrideCursor()

+ 12 - 1
sketchUI/exm_scene.py

@@ -76,10 +76,11 @@ class SketchScene(QGraphicsScene):
                 return
                 return
 
 
             elif isinstance(item, GraphicsNodeItem):
             elif isinstance(item, GraphicsNodeItem):
+                self._parent.plainTextEdit.appendPlainText("Selected node of type {}".format(item.get_type()))
+                self.highlight_node(item.node_id, Qt.blue)
                 # load attributes for selected node
                 # load attributes for selected node
                 self._parent.tableWidget.setRowCount(0)
                 self._parent.tableWidget.setRowCount(0)
                 self._parent.tableWidget.blockSignals(True)
                 self._parent.tableWidget.blockSignals(True)
-                self._parent.plainTextEdit.appendPlainText("Selected node of type {}".format(item.get_type()))
                 attrs = commons.get_attributes_of_node(self._cur_model, item.node_id)
                 attrs = commons.get_attributes_of_node(self._cur_model, item.node_id)
                 for attr in attrs:
                 for attr in attrs:
                     table_item_key = QTableWidgetItem(attr.key)
                     table_item_key = QTableWidgetItem(attr.key)
@@ -431,3 +432,13 @@ class SketchScene(QGraphicsScene):
                 return
                 return
 
 
         self._parent.add_new_attribute(key)
         self._parent.add_new_attribute(key)
+
+    def highlight_node(self, node_id, color):
+        for item in self.items():
+            if not isinstance(item, GraphicsNodeItem):
+                continue
+            if item.node_id == node_id:
+                item.set_highlighted(True, color)
+            else:
+                item.set_highlighted(False, color)
+            item.update(item.boundingRect())

+ 11 - 0
sketchUI/graphics_node_item.py

@@ -21,6 +21,8 @@ class GraphicsNodeItem(QGraphicsItem):
         self.node_id = node_id
         self.node_id = node_id
         self._type = node_type
         self._type = node_type
         self._consyn = consyn
         self._consyn = consyn
+        self._is_highlighted = False
+        self._highlight_color = None
 
 
         self._icon_type = None
         self._icon_type = None
         self._font = QFont()
         self._font = QFont()
@@ -43,19 +45,28 @@ class GraphicsNodeItem(QGraphicsItem):
         bbox = QRectF(0, 0, 120, 120)
         bbox = QRectF(0, 0, 120, 120)
         return bbox
         return bbox
 
 
+    def set_highlighted(self, highlight, color):
+        self._is_highlighted = highlight
+        self._highlight_color = color
+
     def paint(self, painter, option, widget=None):
     def paint(self, painter, option, widget=None):
         # type: (QPainter, QStyleOptionGraphicsItem, QWidget) -> None
         # type: (QPainter, QStyleOptionGraphicsItem, QWidget) -> None
         # draw type text and bounding rectangle
         # draw type text and bounding rectangle
         painter.setFont(self._font)
         painter.setFont(self._font)
 
 
+        # draw bounding rectangle
         painter.setBrush(QBrush(Qt.white))
         painter.setBrush(QBrush(Qt.white))
+        if self._is_highlighted:
+            painter.setPen(self._highlight_color)
         painter.drawRect(self.boundingRect())
         painter.drawRect(self.boundingRect())
 
 
         text_box = QRectF(0, 100, 120, 20)
         text_box = QRectF(0, 100, 120, 20)
         painter.drawRect(text_box)
         painter.drawRect(text_box)
+        painter.setPen(Qt.black)
         painter.setBrush(QBrush(QColor(0, 0, 0, 0)))
         painter.setBrush(QBrush(QColor(0, 0, 0, 0)))
         painter.drawText(text_box, Qt.AlignCenter | Qt.TextDontClip, self._type)
         painter.drawText(text_box, Qt.AlignCenter | Qt.TextDontClip, self._type)
 
 
+
         # draw concrete syntax
         # draw concrete syntax
         if not self._consyn:
         if not self._consyn:
             # nothing to draw, so leave empty
             # nothing to draw, so leave empty

+ 1 - 0
sketchUI/im_mainwindow.py

@@ -208,6 +208,7 @@ class IMMainWindow(QMainWindow, Ui_MainWindow):
         ret = verify.verify_attributes()
         ret = verify.verify_attributes()
         if not ret["OK"]:
         if not ret["OK"]:
             self.plainTextEdit.appendPlainText("Error: {}".format(str(ret["error"])))
             self.plainTextEdit.appendPlainText("Error: {}".format(str(ret["error"])))
+            self._scene.highlight_node(ret["affected"][0], Qt.red)
             QApplication.restoreOverrideCursor()
             QApplication.restoreOverrideCursor()
             return
             return
         else:
         else:

+ 13 - 1
sketchUI/im_scene.py

@@ -36,9 +36,11 @@ class CustomScene(QGraphicsScene):
             if not item:
             if not item:
                 return
                 return
             elif isinstance(item, GraphicsNodeItem):
             elif isinstance(item, GraphicsNodeItem):
+                self._parent.plainTextEdit.appendPlainText("Selected node of type {}".format(item.get_type()))
+                self.highlight_node(item.node_id, Qt.blue)
+                # load node attributes and display them in table
                 self._parent.tableWidget.setRowCount(0)
                 self._parent.tableWidget.setRowCount(0)
                 self._parent.tableWidget.blockSignals(True)
                 self._parent.tableWidget.blockSignals(True)
-                self._parent.plainTextEdit.appendPlainText("Selected node of type {}".format(item.get_type()))
                 attrs = commons.get_attributes_of_node(self._cur_model, item.node_id)
                 attrs = commons.get_attributes_of_node(self._cur_model, item.node_id)
                 for attr in attrs:
                 for attr in attrs:
                     table_item_key = QTableWidgetItem(attr.key)
                     table_item_key = QTableWidgetItem(attr.key)
@@ -176,3 +178,13 @@ class CustomScene(QGraphicsScene):
                 return
                 return
 
 
         self._parent.add_new_attribute(key)
         self._parent.add_new_attribute(key)
+
+    def highlight_node(self, node_id, color):
+        for item in self.items():
+            if not isinstance(item, GraphicsNodeItem):
+                continue
+            if item.node_id == node_id:
+                item.set_highlighted(True, color)
+            else:
+                item.set_highlighted(False, color)
+            item.update(item.boundingRect())

+ 12 - 8
verifier.py

@@ -59,7 +59,7 @@ class Verifier(object):
             if node_typ not in self._available_types:
             if node_typ not in self._available_types:
                 return {"OK": False, "error": "Type {} from instance model not in example models".format(node_typ)}
                 return {"OK": False, "error": "Type {} from instance model not in example models".format(node_typ)}
 
 
-        return {"OK":True, "error":None}
+        return {"OK":True, "error":None, "affected":[]}
 
 
     def verify_node_multiplicity(self):
     def verify_node_multiplicity(self):
         """
         """
@@ -78,7 +78,7 @@ class Verifier(object):
             if not commons.count_occurences(el, self._instance_model) >= min_occ:
             if not commons.count_occurences(el, self._instance_model) >= min_occ:
                 return {"OK": False, "error": "Node with type {} needs to occur at least {} times".format(el, min_occ)}
                 return {"OK": False, "error": "Node with type {} needs to occur at least {} times".format(el, min_occ)}
 
 
-        return {"OK": True, "error": None}
+        return {"OK": True, "error": None, "affected":[]}
 
 
     def _get_attributes_of_all_types(self):
     def _get_attributes_of_all_types(self):
         """
         """
@@ -140,7 +140,8 @@ class Verifier(object):
                         break
                         break
                 else:
                 else:
                     # loop completed without break, so we did not find any key in example models
                     # loop completed without break, so we did not find any key in example models
-                    return {"OK": False, "error": "No key {} for type {} in example models".format(attr.key, node_typ)}
+                    return {"OK": False, "error": "No key {} for type {} in example models".format(attr.key, node_typ),
+                            "affected":[node]}
 
 
         # Check if mandatory attributes are present in instance model
         # Check if mandatory attributes are present in instance model
         attr_mandatory = {node_type:{} for node_type in self._available_types}
         attr_mandatory = {node_type:{} for node_type in self._available_types}
@@ -165,9 +166,10 @@ class Verifier(object):
             for attr, mand in attr_mand.iteritems():
             for attr, mand in attr_mand.iteritems():
                 if mand:
                 if mand:
                     if not attr in [x.key for x in node_attrs]:
                     if not attr in [x.key for x in node_attrs]:
-                        return {"OK": False, "error":"Attribute {} for type {} mandatory".format(attr, node_type)}
+                        return {"OK": False, "error":"Attribute {} for type {} mandatory".format(attr, node_type),
+                                "affected":[node]}
 
 
-        return {"OK": True, "error": None}
+        return {"OK": True, "error": None, "affected":[]}
 
 
     def verify_associations(self):
     def verify_associations(self):
         """
         """
@@ -200,7 +202,8 @@ class Verifier(object):
             # instance model contains both types
             # instance model contains both types
             for node in commons.all_nodes_with_type(self._instance_model, edge.n1):
             for node in commons.all_nodes_with_type(self._instance_model, edge.n1):
                 if not commons.has_edge_to_type(self._instance_model, node, edge.n2):
                 if not commons.has_edge_to_type(self._instance_model, node, edge.n2):
-                    return {"OK":False, "error": "Edge between {} and {} mandatory".format(edge.n1, edge.n2)}
+                    return {"OK":False, "error": "Edge between {} and {} mandatory".format(edge.n1, edge.n2),
+                            "affected":[]}
 
 
         # lastly, check if all edges in the instance model are actually supported
         # lastly, check if all edges in the instance model are actually supported
         all_edges = mv.all_instances(self._instance_model, "Edge")
         all_edges = mv.all_instances(self._instance_model, "Edge")
@@ -210,6 +213,7 @@ class Verifier(object):
             src_type = commons.get_node_type(self._instance_model, src_id)
             src_type = commons.get_node_type(self._instance_model, src_id)
             dest_type = commons.get_node_type(self._instance_model, dest_id)
             dest_type = commons.get_node_type(self._instance_model, dest_id)
             if not commons.is_edge_supported(src_type, dest_type):
             if not commons.is_edge_supported(src_type, dest_type):
-                return {"OK":False, "error": "Edge between {} and {} not supported".format(edge.n1, edge.n2)}
+                return {"OK":False, "error": "Edge between {} and {} not supported".format(edge.n1, edge.n2),
+                        "affected":[src_id, dest_id]}
 
 
-        return {"OK": True, "error": None}
+        return {"OK": True, "error": None, "affected":[]}

+ 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:   Sat Apr 28 11:35:47 2018
+Date:   Sat Apr 28 14:38:25 2018
 
 
 Model author: Yentl Van Tendeloo
 Model author: Yentl Van Tendeloo
 Model name:   MvK Server
 Model name:   MvK Server