浏览代码

last round of evolution fixes for attributes

Lucas Heer 7 年之前
父节点
当前提交
ddde9bd983
共有 5 个文件被更改,包括 48 次插入31 次删除
  1. 3 0
      commons.py
  2. 38 29
      evolution/attribute_ops.py
  3. 1 1
      evolution/node_ops.py
  4. 5 1
      sketchUI/exm_mainwindow.py
  5. 1 0
      sketchUI/exm_scene.py

+ 3 - 0
commons.py

@@ -39,6 +39,9 @@ class Attribute(object):
             return True
         return False
 
+    def __hash__(self):
+        return hash(self.key + self.val)
+
     def __repr__(self):
         return "{}={}".format(self.key, self.val)
 

+ 38 - 29
evolution/attribute_ops.py

@@ -11,7 +11,7 @@ class AttributeAdd(object):
         self._node_id = ""
         self._node_type = ""
 
-    def execute(self, model, node_id, key, value, local, check_if_last=False):
+    def execute(self, model, node_id, key, value, local):
         """
         Add a new attribute (key, value) to node with node_id in model.
         """
@@ -25,18 +25,11 @@ class AttributeAdd(object):
         if local:
             mv.transformation_execute_MANUAL("graph_ops/add_attribute", {"gm":model}, {"gm":model},
                                              callback=self._callback)
-            if check_if_last:
-                if commons.is_attribute_mandatory(self._node_type, key):
-                    print("Attribute {} for type {} became mandatory, adding it to instance models ...".format(key, self._node_type))
-                    for im in commons.all_instance_models():
-                        nodes = commons.all_nodes_with_type(im, self._node_type)
-                        for nid in nodes:
-                            self.execute(im, nid, key, value, local=True, check_if_last=False)
         else:
             for m in commons.all_models():
                 nodes = commons.all_nodes_with_type(m, self._node_type)
                 for nid in nodes:
-                    self.execute(m, nid, key, value, local=True, check_if_last=False)
+                    self.execute(m, nid, key, value, local=True)
 
     def _callback(self, model):
         attr_id = mv.instantiate(model, "gm/Attribute")
@@ -44,6 +37,21 @@ class AttributeAdd(object):
         mv.attr_assign(model, attr_id, "value", self._value)
         mv.instantiate(model, "gm/NodeAttribute", ("gm/"+self._node_id, attr_id))
 
+    def repair(self):
+        """
+        Check if this operation invalidated any instance models by making an attribute mandatory.
+        If yes, every node with the same type must have this attribute, so add it if necessary.
+        """
+        if commons.is_attribute_mandatory(self._node_type, self._key):
+            print("Attribute {} for type {} became mandatory, adding it to instance models ...".format(self._key,
+                                                                                                       self._node_type))
+        for im in commons.all_instance_models():
+            nodes = commons.all_nodes_with_type(im, self._node_type)
+            for node_id in nodes:
+                attrs = commons.get_attributes_of_node(im, node_id)
+                if self._key not in [at.key for at in attrs]:
+                    self.execute(im, node_id, self._key, self._value, local=True)
+
 
 class AttributeDelete(object):
     def __init__(self):
@@ -51,7 +59,7 @@ class AttributeDelete(object):
         self._node_id = ""
         self._node_type = ""
 
-    def execute(self, model, node_id, key, local, check_if_last=False):
+    def execute(self, model, node_id, key, local):
         """
         Deletes an attribute identified by its key from node_id in model.
         """
@@ -64,27 +72,11 @@ class AttributeDelete(object):
         if local:
             mv.transformation_execute_MANUAL("graph_ops/del_attribute", {"gm":model}, {"gm":model},
                                              callback=self._callback)
-        if check_if_last:
-            # a local attribute delete can break the conformance for instance models if the attribute was the last in
-            # all example models -> check and correct this if necessary
-            for exm in commons.all_example_models():
-                all_attrs = commons.get_all_attributes_of_type(exm, self._node_type)
-                for attr in all_attrs:
-                    if attr.key == key:
-                        # wasn't the last, we're done here
-                        return
-            # it was the last -> delete this attribute in all instance models to maintain conformance
-            print("Attribute {} was the last, deleting it from all instance models to maintain conformance ...".format(key))
-            for im in commons.all_instance_models():
-                nodes = commons.get_nodes_with_attribute(im, key, self._node_type)
-                for node in nodes:
-                    self.execute(im, node, key, local=True, check_if_last=False)
         else:
-            # delete all attributes with key in all example models
-            for m in commons.all_models():
-                nodes = commons.all_nodes_with_type(m, self._node_type)
+            for exm in commons.all_example_models():
+                nodes = commons.get_nodes_with_attribute(exm, self._key, self._node_type)
                 for nid in nodes:
-                    self.execute(m, nid, key, local=True)
+                    self.execute(exm, nid, self._key, local=True)
 
     def _callback(self, model):
         outgoings = mv.read_outgoing(model, "gm/"+self._node_id, "gm/NodeAttribute")
@@ -95,6 +87,23 @@ class AttributeDelete(object):
                 mv.delete_element(model, attr)
                 break
 
+    def repair(self):
+        """
+        Check if this operation invalidated any instance models by making an instance model attribute invalid.
+        If yes, delete this attribute from the instance model.
+        """
+        for exm in commons.all_example_models():
+            all_attrs = commons.get_all_attributes_of_type(exm, self._node_type)
+            for attr in all_attrs:
+                if attr.key == self._key:
+                    # there still is an attribute with the key, so it is not invalid -> finished
+                    return
+        print("Attribute {} was the last, deleting it from all instance models ...".format(self._key))
+        for im in commons.all_instance_models():
+            nodes = commons.get_nodes_with_attribute(im, self._key, self._node_type)
+            for nid in nodes:
+                self.execute(im, nid, self._key, local=True)
+
 
 class AttributeChange(object):
     """

+ 1 - 1
evolution/node_ops.py

@@ -120,4 +120,4 @@ class NodeRetype(object):
                 for node in commons.all_nodes_with_type(im, self._old_type):
                     self.execute(im, node, self._new_type, local=True)
         else:
-            pass  # TODO implement
+            pass  # TODO implement

+ 5 - 1
sketchUI/exm_mainwindow.py

@@ -293,16 +293,20 @@ class EXMMainWindow(QMainWindow, Ui_MainWindow):
             scope, ok = QInputDialog.getItem(self.parent(), "Select scope", "Scope", ["Local", "Global"])
             if not ok:
                 return
+
+            QApplication.setOverrideCursor(Qt.WaitCursor)
             del_handler = AttributeDelete()
             self.plainTextEdit.appendPlainText("Deleting attribute {}".format(attr_key))
 
             if scope == "Global":
                 del_handler.execute(self._cur_model, node.node_id, attr_key, local=False)
             else:
-                del_handler.execute(self._cur_model, node.node_id, attr_key, local=True, check_if_last=True)
+                del_handler.execute(self._cur_model, node.node_id, attr_key, local=True)
+            del_handler.repair()
 
             # update view
             self.tableWidget.removeRow(row)
+            QApplication.restoreOverrideCursor()
         else:
             self.plainTextEdit.appendPlainText("Updating value of attribute {} to {}".format(attr_key, attr_val))
             mvops.update_attribute_val(self._cur_model, node.node_id, attr_key, attr_val)

+ 1 - 0
sketchUI/exm_scene.py

@@ -516,6 +516,7 @@ class SketchScene(QGraphicsScene):
         else:
             add_handler.execute(self._cur_model, item.node_id, key, "unknown", local=True)
 
+        add_handler.repair()
         # add to view
         self._parent.add_new_attribute(key, "unknown")