diff --git a/meshroom/core/attribute.py b/meshroom/core/attribute.py index 81bff521..6b51664d 100644 --- a/meshroom/core/attribute.py +++ b/meshroom/core/attribute.py @@ -7,7 +7,7 @@ import weakref import types import logging -from collections.abc import Sequence +from collections.abc import Iterable, Sequence from string import Template from meshroom.common import BaseObject, Property, Variant, Signal, ListModel, DictModel, Slot from meshroom.core import desc, hashValue @@ -29,6 +29,8 @@ def attributeFactory(description, value, isOutput, node, root=None, parent=None) cls = GroupAttribute elif isinstance(description, desc.ListAttribute): cls = ListAttribute + elif isinstance(description, desc.ChoiceParam): + cls = ChoiceParam else: cls = Attribute attr = cls(node, description, isOutput, root, parent) @@ -170,6 +172,9 @@ class Attribute(BaseObject): return self._validValue = value + def validateValue(self, value): + return self.desc.validateValue(value) + def _get_value(self): if self.isLink: return self.getLinkParam().value @@ -185,8 +190,10 @@ class Attribute(BaseObject): else: # if we set a new value, we use the attribute descriptor validator to check the validity of the value # and apply some conversion if needed - convertedValue = self.desc.validateValue(value) + convertedValue = self.validateValue(value) self._value = convertedValue + + self.node.onAttributeChanged(self) # Request graph update when input parameter value is set # and parent node belongs to a graph # Output attributes value are set internally during the update process, @@ -392,6 +399,43 @@ def raiseIfLink(func): return wrapper +class ChoiceParam(Attribute): + + def __init__(self, node, attributeDesc, isOutput, root=None, parent=None): + super(ChoiceParam, self).__init__(node, attributeDesc, isOutput, root, parent) + self._values = None + + def getValues(self): + return self._values if self._values is not None else self.desc._values + + def conformValue(self, val): + """ Conform 'val' to the correct type and check for its validity """ + return self.desc._valueType(val) + + def validateValue(self, value): + if self.desc.exclusive: + return self.conformValue(value) + + if isinstance(value, str): + value = value.split(',') + + if not isinstance(value, Iterable): + raise ValueError('Non exclusive ChoiceParam value should be iterable (param:{}, value:{}, type:{})'.format(self.name, value, type(value))) + return [self.conformValue(v) for v in value] + + def setValues(self, values): + if values == self._values: + return + self._values = values + self.valuesChanged.emit() + + def __len__(self): + return len(self.getValues()) + + valuesChanged = Signal() + values = Property(Variant, getValues, setValues, notify=valuesChanged) + + class ListAttribute(Attribute): def __init__(self, node, attributeDesc, isOutput, root=None, parent=None): @@ -567,7 +611,7 @@ class GroupAttribute(Attribute): raise AttributeError(key) def _set_value(self, exportedValue): - value = self.desc.validateValue(exportedValue) + value = self.validateValue(exportedValue) if isinstance(value, dict): # set individual child attribute values for key, v in value.items(): @@ -581,7 +625,7 @@ class GroupAttribute(Attribute): raise AttributeError("Failed to set on GroupAttribute: {}".format(str(value))) def upgradeValue(self, exportedValue): - value = self.desc.validateValue(exportedValue) + value = self.validateValue(exportedValue) if isinstance(value, dict): # set individual child attribute values for key, v in value.items(): diff --git a/meshroom/core/desc.py b/meshroom/core/desc.py index 6d8cc2fa..79d7b3bf 100644 --- a/meshroom/core/desc.py +++ b/meshroom/core/desc.py @@ -313,12 +313,11 @@ class ChoiceParam(Param): super(ChoiceParam, self).__init__(name=name, label=label, description=description, value=value, uid=uid, group=group, advanced=advanced, semantic=semantic, enabled=enabled, validValue=validValue, errorMessage=errorMessage) - def conformValue(self, val): + def conformValue(self, value): """ Conform 'val' to the correct type and check for its validity """ - val = self._valueType(val) - if val not in self.values: - raise ValueError('ChoiceParam value "{}" is not in "{}".'.format(val, str(self.values))) - return val + if not isinstance(value, str): + raise ValueError('ChoiceParam value should be a string (param:{}, value:{}, type:{})'.format(self.name, value, type(value))) + return value def validateValue(self, value): if self.exclusive: @@ -573,7 +572,6 @@ class Node(object): BaseNode.updateInternals """ pass - @classmethod def postUpdate(cls, node): """ Method call after node's internal update on invalidation. diff --git a/meshroom/core/node.py b/meshroom/core/node.py index 247ba3c6..27a7982f 100644 --- a/meshroom/core/node.py +++ b/meshroom/core/node.py @@ -879,6 +879,14 @@ class BaseNode(BaseObject): def _updateChunks(self): pass + def onAttributeChanged(self, attr): + paramName = attr.name[:1].upper() + attr.name[1:] + methodName = f'on{paramName}Changed' + if hasattr(self.nodeDesc, methodName): + m = getattr(self.nodeDesc, methodName) + if callable(m): + m(self) + def updateInternals(self, cacheDir=None): """ Update Node's internal parameters and output attributes. @@ -1245,6 +1253,13 @@ class Node(BaseNode): self.attributesPerUid[uidIndex].add(attr) self.setAttributeValues(kwargs) + self.callDesc("onNodeCreated") + + def callDesc(self, methodName, *args, **kwargs): + if hasattr(self.nodeDesc, methodName): + m = getattr(self.nodeDesc, methodName) + if callable(m): + m(self, *args, **kwargs) def setAttributeValues(self, values): # initialize attribute values diff --git a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml index cb24a08e..b5347ba4 100644 --- a/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml +++ b/meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml @@ -345,7 +345,7 @@ RowLayout { ComboBox { id: combo enabled: root.editable - model: attribute.desc.values + model: attribute.values Component.onCompleted: currentIndex = find(attribute.value) onActivated: _reconstruction.setAttribute(attribute, currentText) Connections {