mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-05-21 13:06:28 +02:00
[core] Add new type of ChoiceParam that changes dynamically according to other values
Add new type of ChoiceParam that changes dynamically according to other values. When value of an attribute is changed onAttributeChanged is called, allowing to have unique reaction within node files. Also add of callDesc function to be able to have other functions such as onNodeCreated at creation of node.
This commit is contained in:
parent
bb9661a141
commit
498fd6cbd2
4 changed files with 68 additions and 11 deletions
|
@ -7,7 +7,7 @@ import weakref
|
||||||
import types
|
import types
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from collections.abc import Sequence
|
from collections.abc import Iterable, Sequence
|
||||||
from string import Template
|
from string import Template
|
||||||
from meshroom.common import BaseObject, Property, Variant, Signal, ListModel, DictModel, Slot
|
from meshroom.common import BaseObject, Property, Variant, Signal, ListModel, DictModel, Slot
|
||||||
from meshroom.core import desc, hashValue
|
from meshroom.core import desc, hashValue
|
||||||
|
@ -29,6 +29,8 @@ def attributeFactory(description, value, isOutput, node, root=None, parent=None)
|
||||||
cls = GroupAttribute
|
cls = GroupAttribute
|
||||||
elif isinstance(description, desc.ListAttribute):
|
elif isinstance(description, desc.ListAttribute):
|
||||||
cls = ListAttribute
|
cls = ListAttribute
|
||||||
|
elif isinstance(description, desc.ChoiceParam):
|
||||||
|
cls = ChoiceParam
|
||||||
else:
|
else:
|
||||||
cls = Attribute
|
cls = Attribute
|
||||||
attr = cls(node, description, isOutput, root, parent)
|
attr = cls(node, description, isOutput, root, parent)
|
||||||
|
@ -170,6 +172,9 @@ class Attribute(BaseObject):
|
||||||
return
|
return
|
||||||
self._validValue = value
|
self._validValue = value
|
||||||
|
|
||||||
|
def validateValue(self, value):
|
||||||
|
return self.desc.validateValue(value)
|
||||||
|
|
||||||
def _get_value(self):
|
def _get_value(self):
|
||||||
if self.isLink:
|
if self.isLink:
|
||||||
return self.getLinkParam().value
|
return self.getLinkParam().value
|
||||||
|
@ -185,8 +190,10 @@ class Attribute(BaseObject):
|
||||||
else:
|
else:
|
||||||
# if we set a new value, we use the attribute descriptor validator to check the validity of the value
|
# 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
|
# and apply some conversion if needed
|
||||||
convertedValue = self.desc.validateValue(value)
|
convertedValue = self.validateValue(value)
|
||||||
self._value = convertedValue
|
self._value = convertedValue
|
||||||
|
|
||||||
|
self.node.onAttributeChanged(self)
|
||||||
# Request graph update when input parameter value is set
|
# Request graph update when input parameter value is set
|
||||||
# and parent node belongs to a graph
|
# and parent node belongs to a graph
|
||||||
# Output attributes value are set internally during the update process,
|
# Output attributes value are set internally during the update process,
|
||||||
|
@ -392,6 +399,43 @@ def raiseIfLink(func):
|
||||||
return wrapper
|
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):
|
class ListAttribute(Attribute):
|
||||||
|
|
||||||
def __init__(self, node, attributeDesc, isOutput, root=None, parent=None):
|
def __init__(self, node, attributeDesc, isOutput, root=None, parent=None):
|
||||||
|
@ -567,7 +611,7 @@ class GroupAttribute(Attribute):
|
||||||
raise AttributeError(key)
|
raise AttributeError(key)
|
||||||
|
|
||||||
def _set_value(self, exportedValue):
|
def _set_value(self, exportedValue):
|
||||||
value = self.desc.validateValue(exportedValue)
|
value = self.validateValue(exportedValue)
|
||||||
if isinstance(value, dict):
|
if isinstance(value, dict):
|
||||||
# set individual child attribute values
|
# set individual child attribute values
|
||||||
for key, v in value.items():
|
for key, v in value.items():
|
||||||
|
@ -581,7 +625,7 @@ class GroupAttribute(Attribute):
|
||||||
raise AttributeError("Failed to set on GroupAttribute: {}".format(str(value)))
|
raise AttributeError("Failed to set on GroupAttribute: {}".format(str(value)))
|
||||||
|
|
||||||
def upgradeValue(self, exportedValue):
|
def upgradeValue(self, exportedValue):
|
||||||
value = self.desc.validateValue(exportedValue)
|
value = self.validateValue(exportedValue)
|
||||||
if isinstance(value, dict):
|
if isinstance(value, dict):
|
||||||
# set individual child attribute values
|
# set individual child attribute values
|
||||||
for key, v in value.items():
|
for key, v in value.items():
|
||||||
|
|
|
@ -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,
|
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)
|
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 """
|
""" Conform 'val' to the correct type and check for its validity """
|
||||||
val = self._valueType(val)
|
if not isinstance(value, str):
|
||||||
if val not in self.values:
|
raise ValueError('ChoiceParam value should be a string (param:{}, value:{}, type:{})'.format(self.name, value, type(value)))
|
||||||
raise ValueError('ChoiceParam value "{}" is not in "{}".'.format(val, str(self.values)))
|
return value
|
||||||
return val
|
|
||||||
|
|
||||||
def validateValue(self, value):
|
def validateValue(self, value):
|
||||||
if self.exclusive:
|
if self.exclusive:
|
||||||
|
@ -573,7 +572,6 @@ class Node(object):
|
||||||
BaseNode.updateInternals
|
BaseNode.updateInternals
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def postUpdate(cls, node):
|
def postUpdate(cls, node):
|
||||||
""" Method call after node's internal update on invalidation.
|
""" Method call after node's internal update on invalidation.
|
||||||
|
|
|
@ -879,6 +879,14 @@ class BaseNode(BaseObject):
|
||||||
def _updateChunks(self):
|
def _updateChunks(self):
|
||||||
pass
|
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):
|
def updateInternals(self, cacheDir=None):
|
||||||
""" Update Node's internal parameters and output attributes.
|
""" Update Node's internal parameters and output attributes.
|
||||||
|
|
||||||
|
@ -1245,6 +1253,13 @@ class Node(BaseNode):
|
||||||
self.attributesPerUid[uidIndex].add(attr)
|
self.attributesPerUid[uidIndex].add(attr)
|
||||||
|
|
||||||
self.setAttributeValues(kwargs)
|
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):
|
def setAttributeValues(self, values):
|
||||||
# initialize attribute values
|
# initialize attribute values
|
||||||
|
|
|
@ -345,7 +345,7 @@ RowLayout {
|
||||||
ComboBox {
|
ComboBox {
|
||||||
id: combo
|
id: combo
|
||||||
enabled: root.editable
|
enabled: root.editable
|
||||||
model: attribute.desc.values
|
model: attribute.values
|
||||||
Component.onCompleted: currentIndex = find(attribute.value)
|
Component.onCompleted: currentIndex = find(attribute.value)
|
||||||
onActivated: _reconstruction.setAttribute(attribute, currentText)
|
onActivated: _reconstruction.setAttribute(attribute, currentText)
|
||||||
Connections {
|
Connections {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue