[Bf-blender-cvs] [d67aab6] object_nodes: Fix for re-entrant update problems, using a base class with a context manager.

Lukas Tönne noreply at git.blender.org
Sun Dec 6 15:24:00 CET 2015


Commit: d67aab6467da5696e289b940721df8e0bd6f4513
Author: Lukas Tönne
Date:   Sun Dec 6 15:21:50 2015 +0100
Branches: object_nodes
https://developer.blender.org/rBd67aab6467da5696e289b940721df8e0bd6f4513

Fix for re-entrant update problems, using a base class with a context manager.

===================================================================

M	release/scripts/nodes/common_nodes.py
M	release/scripts/nodes/forcefield_nodes.py
M	release/scripts/nodes/geometry_nodes.py
M	release/scripts/nodes/group_nodes.py
M	release/scripts/nodes/object_nodes.py

===================================================================

diff --git a/release/scripts/nodes/common_nodes.py b/release/scripts/nodes/common_nodes.py
index 0377118..8ee2ef0 100644
--- a/release/scripts/nodes/common_nodes.py
+++ b/release/scripts/nodes/common_nodes.py
@@ -18,7 +18,7 @@
 
 # <pep8-80 compliant>
 
-import bpy
+import bpy, contextlib
 import nodeitems_utils
 from bpy.types import Operator, ObjectNode
 from bpy.props import *
@@ -51,10 +51,24 @@ class NodeTreeBase():
             dst = (blink.to_node, blink.to_socket)
             compiler.link(output_map[src], input_map[dst])
 
+class NodeBase():
+    # XXX used to prevent reentrant updates due to RNA calls
+    # this should be fixed in future by avoiding low-level update recursion on the RNA side
+    is_updating = BoolProperty(options={'HIDDEN'})
+
+    @contextlib.contextmanager
+    def update_lock(self):
+        self.is_updating = True
+        try:
+            yield
+        finally:
+            self.is_updating = False
+
+
 ###############################################################################
 # Generic Nodes
 
-class CommonNodeBase():
+class CommonNodeBase(NodeBase):
     @classmethod
     def poll(cls, ntree):
         return isinstance(ntree, NodeTreeBase)
diff --git a/release/scripts/nodes/forcefield_nodes.py b/release/scripts/nodes/forcefield_nodes.py
index 8245f8a..1e3cb61 100644
--- a/release/scripts/nodes/forcefield_nodes.py
+++ b/release/scripts/nodes/forcefield_nodes.py
@@ -24,7 +24,7 @@ from bpy.types import Operator, ObjectNode, NodeTree, Node, NodeSocket
 from bpy.props import *
 from nodeitems_utils import NodeCategory, NodeItem
 from mathutils import *
-from common_nodes import NodeTreeBase
+from common_nodes import NodeTreeBase, NodeBase
 
 ###############################################################################
 
@@ -50,7 +50,7 @@ class ForceFieldNodeTree(NodeTreeBase, NodeTree):
         return False
 
 
-class ForceNodeBase():
+class ForceNodeBase(NodeBase):
     @classmethod
     def poll(cls, ntree):
         return ntree.bl_idname == 'ForceFieldNodeTree'
diff --git a/release/scripts/nodes/geometry_nodes.py b/release/scripts/nodes/geometry_nodes.py
index 8a162d3..9ff7432 100644
--- a/release/scripts/nodes/geometry_nodes.py
+++ b/release/scripts/nodes/geometry_nodes.py
@@ -24,7 +24,7 @@ from bpy.types import Operator, ObjectNode, NodeTree, Node
 from bpy.props import *
 from nodeitems_utils import NodeCategory, NodeItem
 from mathutils import *
-from common_nodes import NodeTreeBase
+from common_nodes import NodeTreeBase, NodeBase
 import group_nodes
 
 ###############################################################################
@@ -52,7 +52,7 @@ class GeometryNodeTree(NodeTreeBase, NodeTree):
         return False
 
 
-class GeometryNodeBase():
+class GeometryNodeBase(NodeBase):
     @classmethod
     def poll(cls, ntree):
         return ntree.bl_idname == 'GeometryNodeTree'
@@ -94,39 +94,45 @@ class GeometryMeshCombineNode(GeometryNodeBase, ObjectNode):
         return socket
 
     def init(self, context):
-        self.add_extender()
-        self.outputs.new('GeometrySocket', "")
+        if self.is_updating:
+            return
+        with self.update_lock():
+            self.add_extender()
+            self.outputs.new('GeometrySocket', "")
 
     def update_inputs(self, insert=None):
-        ntree = self.id_data
-
-        # build map of connected inputs
-        input_links = dict()
-        for link in ntree.links:
-            if link.to_node == self:
-                input_links[link.to_socket] = (link, link.from_socket)
-
-        # remove unconnected sockets
-        for socket in self.inputs:
-            if socket not in input_links and socket != insert:
-                self.inputs.remove(socket)
-            else:
-                socket.is_placeholder = False
-
-        # shift sockets to make room for a new link
-        if insert is not None:
-            self.inputs.new('GeometrySocket', "")
-            nsocket = self.inputs[-1]
-            for socket in reversed(self.inputs[:-1]):
-                link, from_socket = input_links.get(socket, (None, None))
-                if link is not None:
-                    ntree.links.remove(link)
-                    ntree.links.new(from_socket, nsocket)
-                nsocket = socket
-                if socket == insert:
-                    break
-
-        self.add_extender()
+        if self.is_updating:
+            return
+        with self.update_lock():
+            ntree = self.id_data
+
+            # build map of connected inputs
+            input_links = dict()
+            for link in ntree.links:
+                if link.to_node == self:
+                    input_links[link.to_socket] = (link, link.from_socket)
+
+            # remove unconnected sockets
+            for socket in self.inputs:
+                if socket not in input_links and socket != insert:
+                    self.inputs.remove(socket)
+                else:
+                    socket.is_placeholder = False
+
+            # shift sockets to make room for a new link
+            if insert is not None:
+                self.inputs.new('GeometrySocket', "")
+                nsocket = self.inputs[-1]
+                for socket in reversed(self.inputs[:-1]):
+                    link, from_socket = input_links.get(socket, (None, None))
+                    if link is not None:
+                        ntree.links.remove(link)
+                        ntree.links.new(from_socket, nsocket)
+                    nsocket = socket
+                    if socket == insert:
+                        break
+
+            self.add_extender()
 
     def update(self):
         self.update_inputs()
diff --git a/release/scripts/nodes/group_nodes.py b/release/scripts/nodes/group_nodes.py
index d69bd92..2f2d04e 100644
--- a/release/scripts/nodes/group_nodes.py
+++ b/release/scripts/nodes/group_nodes.py
@@ -277,10 +277,6 @@ def make_node_group_types(prefix, treetype, node_base):
         bl_ntree_idname = ntree_idname
         bl_id_property_type = 'NODETREE'
 
-        # XXX used to prevent reentrant updates due to RNA calls
-        # this should be fixed in future by avoiding low-level update recursion on the RNA side
-        is_updating_nodegroup = BoolProperty(options={'HIDDEN'})
-
         def bl_id_property_poll(self, ntree):
             if not isinstance(ntree, treetype):
                 return False
@@ -303,13 +299,13 @@ def make_node_group_types(prefix, treetype, node_base):
             layout.template_ID(self, "id", new="object_nodes.geometry_nodes_new")
 
         def update(self):
-            if not self.is_updating_nodegroup:
-                self.is_updating_nodegroup = True
+            if self.is_updating:
+                return
+            with self.update_lock():
                 gtree = self.id
                 if gtree:
                     node_sockets_sync(self.inputs, gtree.inputs)
                     node_sockets_sync(self.outputs, gtree.outputs)
-                self.is_updating_nodegroup = False
 
         def compile(self, compiler):
             if self.id is not None:
@@ -320,16 +316,11 @@ def make_node_group_types(prefix, treetype, node_base):
         bl_idname = '%sGroupInputNode' % prefix
         bl_label = 'Group Inputs'
 
-        # XXX used to prevent reentrant updates due to RNA calls
-        # this should be fixed in future by avoiding low-level update recursion on the RNA side
-        is_updating_nodegroup = BoolProperty(options={'HIDDEN'})
-
         def update(self):
-            if not self.is_updating_nodegroup:
-                self.is_updating_nodegroup = True
+            if self.is_updating:
+                return
+            with self.update_lock():
                 node_sockets_sync(self.outputs, self.id_data.inputs)
-                self.is_updating_nodegroup = False
-            pass
 
         def compile(self, compiler):
             gtree = self.id_data
@@ -343,16 +334,11 @@ def make_node_group_types(prefix, treetype, node_base):
         bl_idname = '%sGroupOutputNode' % prefix
         bl_label = 'Group Outputs'
 
-        # XXX used to prevent reentrant updates due to RNA calls
-        # this should be fixed in future by avoiding low-level update recursion on the RNA side
-        is_updating_nodegroup = BoolProperty(options={'HIDDEN'})
-
         def update(self):
-            if not self.is_updating_nodegroup:
-                self.is_updating_nodegroup = True
+            if self.is_updating:
+                return
+            with self.update_lock():
                 node_sockets_sync(self.inputs, self.id_data.outputs)
-                self.is_updating_nodegroup = False
-            pass
 
         def compile(self, compiler):
             gtree = self.id_data
diff --git a/release/scripts/nodes/object_nodes.py b/release/scripts/nodes/object_nodes.py
index 1581b9a..fde964f 100644
--- a/release/scripts/nodes/object_nodes.py
+++ b/release/scripts/nodes/object_nodes.py
@@ -24,7 +24,7 @@ from bpy.types import Operator, ObjectNode, NodeTree, Node, NodeSocket
 from bpy.props import *
 from nodeitems_utils import NodeCategory, NodeItem
 from mathutils import *
-from common_nodes import NodeTreeBase
+from common_nodes import NodeTreeBase, NodeBase
 
 ###############################################################################
 
@@ -53,7 +53,7 @@ class ObjectNodeTree(NodeTreeBase, NodeTree):
             return None, None, None
 
 
-class ObjectNodeBase():
+class ObjectNodeBase(NodeBase):
     @classmethod
     def poll(cls, ntree):
         return ntree.bl_idname == 'ObjectNodeTree'




More information about the Bf-blender-cvs mailing list