[core] Handle attribute valueChanged callback at the Node level

Move the attributeChanged callback logic within Node, as this
is a Node level mecanism (callbacks being declared on the node desc).

This commit also moves the attribute valueChanged signal outside of
the Attribute's constructor.
This seems to be the root cause to undesired side-effects breaking Qt's evaluation
system, data model access and consequently Meshroom's UI.
This commit is contained in:
Yann Lanthony 2024-10-25 19:12:21 +02:00
parent e73e8f2fd7
commit 7fdb5cc734
2 changed files with 35 additions and 15 deletions

View file

@ -25,11 +25,14 @@ def attributeFactory(description, value, isOutput, node, root=None, parent=None)
root: (optional) parent Attribute (must be ListAttribute or GroupAttribute) root: (optional) parent Attribute (must be ListAttribute or GroupAttribute)
parent (BaseObject): (optional) the parent BaseObject if any parent (BaseObject): (optional) the parent BaseObject if any
""" """
attr = description.instanceType(node, description, isOutput, root, parent) attr: Attribute = description.instanceType(node, description, isOutput, root, parent)
if value is not None: if value is not None:
attr._set_value(value, emitSignals=False) attr._set_value(value, emitSignals=False)
else: else:
attr.resetToDefaultValue(emitSignals=False) attr.resetToDefaultValue(emitSignals=False)
attr.valueChanged.connect(lambda attr=attr: node._onAttributeChanged(attr))
return attr return attr
@ -67,7 +70,6 @@ class Attribute(BaseObject):
self._value = None self._value = None
self.initValue() self.initValue()
self.valueChanged.connect(self.onChanged)
@property @property
def node(self): def node(self):

View file

@ -14,6 +14,7 @@ import types
import uuid import uuid
from collections import namedtuple from collections import namedtuple
from enum import Enum from enum import Enum
from typing import Callable, Optional
import meshroom import meshroom
from meshroom.common import Signal, Variant, Property, BaseObject, Slot, ListModel, DictModel from meshroom.common import Signal, Variant, Property, BaseObject, Slot, ListModel, DictModel
@ -929,25 +930,42 @@ class BaseNode(BaseObject):
def _updateChunks(self): def _updateChunks(self):
pass pass
def onAttributeChanged(self, attr): def _getAttributeChangedCallback(self, attr: Attribute) -> Optional[Callable]:
""" When an attribute changed, a specific function can be defined in the descriptor and be called. """Get the node descriptor-defined value changed callback associated to `attr` if any."""
attrCapitalizedName = attr.name[:1].upper() + attr.name[1:]
callbackName = f"on{attrCapitalizedName}Changed"
callback = getattr(self.nodeDesc, callbackName, None)
return callback if callback and callable(callback) else None
def _onAttributeChanged(self, attr: Attribute):
"""
When an attribute value has changed, a specific function can be defined in the descriptor and be called.
Args: Args:
attr (Attribute): attribute that has changed attr: The Attribute that has changed.
""" """
# Call the specific function if it exists in the node implementation
paramName = attr.name[:1].upper() + attr.name[1:] if self.isCompatibilityNode:
methodName = f'on{paramName}Changed' # Compatibility nodes are not meant to be updated.
if hasattr(self.nodeDesc, methodName): return
m = getattr(self.nodeDesc, methodName)
if callable(m): if attr.isOutput and not self.isInputNode:
m(self) # Ignore changes on output attributes for non-input nodes
# as they are updated during the node's computation.
# And we do not want notifications during the graph processing.
return
callback = self._getAttributeChangedCallback(attr)
if callback:
callback(self)
if self.graph: if self.graph:
# If we are in a graph, propagate the notification to the connected output attributes # If we are in a graph, propagate the notification to the connected output attributes
outEdges = self.graph.outEdges(attr) for edge in self.graph.outEdges(attr):
for edge in outEdges: edge.dst.node._onAttributeChanged(edge.dst)
edge.dst.onChanged()
def onAttributeClicked(self, attr): def onAttributeClicked(self, attr):
""" When an attribute is clicked, a specific function can be defined in the descriptor and be called. """ When an attribute is clicked, a specific function can be defined in the descriptor and be called.