Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions meshroom/common/PySignal.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ class ClassSignal:
"""
_map = {}

def __init__(self, *args, **kwargs):
self._args = args
self._kwargs = kwargs

def __get__(self, instance, owner):
if instance is None:
# When we access ClassSignal element on the class object without any instance,
Expand Down
66 changes: 43 additions & 23 deletions meshroom/core/attribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from collections.abc import Iterable, Sequence
from string import Template
from meshroom.common import BaseObject, Property, Variant, Signal, ListModel, DictModel, Slot
from meshroom.core.desc.validators import NotEmptyValidator
from meshroom.core import desc, hashValue

from typing import TYPE_CHECKING
Expand Down Expand Up @@ -73,7 +74,6 @@
self._isOutput: bool = isOutput
self._label: str = attributeDesc.label
self._enabled: bool = True
self._validValue: bool = True
self._description: str = attributeDesc.description
self._invalidate = False if self._isOutput else attributeDesc.invalidate

Expand Down Expand Up @@ -171,24 +171,6 @@
""" Value for which the attribute should be ignored during the UID computation. """
return self.attributeDesc.uidIgnoreValue

def getValidValue(self):
"""
Get the status of _validValue:
- If it is a function, execute it and return the result
- Otherwise, simply return its value
"""
if isinstance(self.desc.validValue, types.FunctionType):
try:
return self.desc.validValue(self.node)
except Exception:
return True
return self._validValue

def setValidValue(self, value):
if self._validValue == value:
return
self._validValue = value

def validateValue(self, value):
return self.desc.validateValue(value)

Expand Down Expand Up @@ -226,7 +208,6 @@
self.requestNodeUpdate()

self.valueChanged.emit()
self.validValueChanged.emit()

@Slot()
def _onValueChanged(self):
Expand Down Expand Up @@ -459,7 +440,7 @@
def updateInternals(self):
# Emit if the enable status has changed
self.setEnabled(self.getEnabled())

def _is3D(self) -> bool:
""" Return True if the current attribute is considered as a 3d file """

Expand All @@ -481,6 +462,43 @@

return next((imageSemantic for imageSemantic in Attribute.VALID_IMAGE_SEMANTICS if self.desc.semantic == imageSemantic), None) is not None

def getErrorMessages(self) -> list[str]:
""" Execute the validators and aggregate the eventual error messages"""

result = []

for validator in self.desc.validators:
isValid, errorMessages = validator(self.node, self)

if isValid:
continue

for errorMessage in errorMessages:
result.append(errorMessage)

return result

def _isValid(self) -> bool:
""" Check the validation and return False if any validator return (False, erorrs)
"""

for validator in self.desc.validators:
isValid, _ = validator(self.node, self)

if not isValid:
return False

return True

def _isMandatory(self) -> bool:
""" An attribute is considered as mandatory it contain a NotEmptyValidator """

for validator in self.desc.validators:
if isinstance(validator, NotEmptyValidator):
return True

return False

Check warning on line 500 in meshroom/core/attribute.py

View check run for this annotation

Codecov / codecov/patch

meshroom/core/attribute.py#L500

Added line #L500 was not covered by tests

name = Property(str, getName, constant=True)
fullName = Property(str, getFullName, constant=True)
fullNameToNode = Property(str, getFullNameToNode, constant=True)
Expand Down Expand Up @@ -528,10 +546,12 @@
enabled = Property(bool, getEnabled, setEnabled, notify=enabledChanged)
invalidate = Property(bool, lambda self: self._invalidate, constant=True)
uidIgnoreValue = Property(Variant, getUidIgnoreValue, constant=True)
validValueChanged = Signal()
validValue = Property(bool, getValidValue, setValidValue, notify=validValueChanged)
root = Property(BaseObject, root.fget, constant=True)

errorMessageChanged = Signal()
errorMessages = Property(Variant, lambda self: self.getErrorMessages(), notify=errorMessageChanged)
isMandatory = Property(bool, _isMandatory, constant=True )
isValid = Property(bool, _isValid, constant=True)

def raiseIfLink(func):
""" If Attribute instance is a link, raise a RuntimeError. """
Expand Down
91 changes: 54 additions & 37 deletions meshroom/core/desc/attribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,24 @@

from meshroom.common import BaseObject, JSValue, Property, Variant, VariantList

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from meshroom.core.desc.validators import AttributeValidator

Check warning on line 12 in meshroom/core/desc/attribute.py

View check run for this annotation

Codecov / codecov/patch

meshroom/core/desc/attribute.py#L12

Added line #L12 was not covered by tests


class Attribute(BaseObject):
"""
"""

def __init__(self, name, label, description, value, advanced, semantic, group, enabled, invalidate=True,
uidIgnoreValue=None, validValue=True, errorMessage="", visible=True, exposed=False):
def __init__(self, name, label, description, value, advanced, semantic, group, enabled,
invalidate=True,
uidIgnoreValue=None,
visible=True,
exposed=False,
validators:list["AttributeValidator"]=None
):

super(Attribute, self).__init__()
self._name = name
self._label = label
Expand All @@ -24,15 +35,20 @@
self._invalidate = invalidate
self._semantic = semantic
self._uidIgnoreValue = uidIgnoreValue
self._validValue = validValue
self._errorMessage = errorMessage
self._visible = visible
self._exposed = exposed
self._isExpression = (isinstance(self._value, str) and "{" in self._value) \
or isinstance(self._value, types.FunctionType)
self._isDynamicValue = (self._value is None)
self._valueType = None

if validators is None:
self._validators = []
elif isinstance(validators, (list, tuple)):
self._validators = validators
else:
raise RuntimeError(f"Validators should be of type 'list[AttributeValidator]', the type '{type(validators)}' is not supported.")

Check warning on line 50 in meshroom/core/desc/attribute.py

View check run for this annotation

Codecov / codecov/patch

meshroom/core/desc/attribute.py#L50

Added line #L50 was not covered by tests

def getInstanceType(self):
""" Return the correct Attribute instance corresponding to the description. """
# Import within the method to prevent cyclic dependencies
Expand Down Expand Up @@ -70,7 +86,11 @@
except ValueError:
return False
return True


@property
def validators(self):
return self._validators

name = Property(str, lambda self: self._name, constant=True)
label = Property(str, lambda self: self._label, constant=True)
description = Property(str, lambda self: self._description, constant=True)
Expand All @@ -89,8 +109,6 @@
invalidate = Property(Variant, lambda self: self._invalidate, constant=True)
semantic = Property(str, lambda self: self._semantic, constant=True)
uidIgnoreValue = Property(Variant, lambda self: self._uidIgnoreValue, constant=True)
validValue = Property(Variant, lambda self: self._validValue, constant=True)
errorMessage = Property(str, lambda self: self._errorMessage, constant=True)
# visible:
# The attribute is not displayed in the Graph Editor if False but still visible in the Node Editor.
# This property is useful to hide some attributes that are not relevant for the user.
Expand All @@ -108,15 +126,15 @@
class ListAttribute(Attribute):
""" A list of Attributes """
def __init__(self, elementDesc, name, label, description, group="allParams", advanced=False, semantic="",
enabled=True, joinChar=" ", visible=True, exposed=False):
enabled=True, joinChar=" ", visible=True, exposed=False, validators=None):
"""
:param elementDesc: the Attribute description of elements to store in that list
"""
self._elementDesc = elementDesc
self._joinChar = joinChar
super(ListAttribute, self).__init__(name=name, label=label, description=description, value=[],
invalidate=False, group=group, advanced=advanced, semantic=semantic,
enabled=enabled, visible=visible, exposed=exposed)
enabled=enabled, visible=visible, exposed=exposed, validators=validators)

def getInstanceType(self):
# Import within the method to prevent cyclic dependencies
Expand Down Expand Up @@ -160,7 +178,7 @@
class GroupAttribute(Attribute):
""" A macro Attribute composed of several Attributes """
def __init__(self, groupDesc, name, label, description, group="allParams", advanced=False, semantic="",
enabled=True, joinChar=" ", brackets=None, visible=True, exposed=False):
enabled=True, joinChar=" ", brackets=None, visible=True, exposed=False, validators=None):
"""
:param groupDesc: the description of the Attributes composing this group
"""
Expand All @@ -169,7 +187,7 @@
self._brackets = brackets
super(GroupAttribute, self).__init__(name=name, label=label, description=description, value={},
group=group, advanced=advanced, invalidate=False, semantic=semantic,
enabled=enabled, visible=visible, exposed=exposed)
enabled=enabled, visible=visible, exposed=exposed, validators=validators)

def getInstanceType(self):
# Import within the method to prevent cyclic dependencies
Expand Down Expand Up @@ -267,21 +285,21 @@
"""
"""
def __init__(self, name, label, description, value, group, advanced, semantic, enabled, invalidate=True,
uidIgnoreValue=None, validValue=True, errorMessage="", visible=True, exposed=False):
uidIgnoreValue=None, visible=True, exposed=False, validators=None):
super(Param, self).__init__(name=name, label=label, description=description, value=value,
group=group, advanced=advanced, enabled=enabled, invalidate=invalidate,
semantic=semantic, uidIgnoreValue=uidIgnoreValue, validValue=validValue,
errorMessage=errorMessage, visible=visible, exposed=exposed)
semantic=semantic, uidIgnoreValue=uidIgnoreValue, visible=visible, exposed=exposed,
validators=validators)


class File(Attribute):
"""
"""
def __init__(self, name, label, description, value, group="allParams", advanced=False, invalidate=True,
semantic="", enabled=True, visible=True, exposed=True):
semantic="", enabled=True, visible=True, exposed=True, validators=None):
super(File, self).__init__(name=name, label=label, description=description, value=value, group=group,
advanced=advanced, enabled=enabled, invalidate=invalidate, semantic=semantic,
visible=visible, exposed=exposed)
visible=visible, exposed=exposed, validators=validators)
self._valueType = str

def validateValue(self, value):
Expand All @@ -304,10 +322,10 @@
"""
"""
def __init__(self, name, label, description, value, group="allParams", advanced=False, enabled=True,
invalidate=True, semantic="", visible=True, exposed=False):
invalidate=True, semantic="", visible=True, exposed=False, validators=None):
super(BoolParam, self).__init__(name=name, label=label, description=description, value=value,
group=group, advanced=advanced, enabled=enabled, invalidate=invalidate,
semantic=semantic, visible=visible, exposed=exposed)
semantic=semantic, visible=visible, exposed=exposed, validators=validators)
self._valueType = bool

def validateValue(self, value):
Expand All @@ -332,12 +350,12 @@
"""
"""
def __init__(self, name, label, description, value, range=None, group="allParams", advanced=False, enabled=True,
invalidate=True, semantic="", validValue=True, errorMessage="", visible=True, exposed=False):
invalidate=True, semantic="", visible=True, exposed=False, validators=None):
self._range = range
super(IntParam, self).__init__(name=name, label=label, description=description, value=value,
group=group, advanced=advanced, enabled=enabled, invalidate=invalidate,
semantic=semantic, validValue=validValue, errorMessage=errorMessage,
visible=visible, exposed=exposed)
semantic=semantic,
visible=visible, exposed=exposed, validators=validators)
self._valueType = int

def validateValue(self, value):
Expand All @@ -362,12 +380,12 @@
"""
"""
def __init__(self, name, label, description, value, range=None, group="allParams", advanced=False, enabled=True,
invalidate=True, semantic="", validValue=True, errorMessage="", visible=True, exposed=False):
invalidate=True, semantic="", visible=True, exposed=False, validators=None):
self._range = range
super(FloatParam, self).__init__(name=name, label=label, description=description, value=value,
group=group, advanced=advanced, enabled=enabled, invalidate=invalidate,
semantic=semantic, validValue=validValue, errorMessage=errorMessage,
visible=visible, exposed=exposed)
semantic=semantic,
visible=visible, exposed=exposed, validators=validators)
self._valueType = float

def validateValue(self, value):
Expand All @@ -391,10 +409,10 @@
"""
"""
def __init__(self, name, label, description, group="allParams", advanced=False, enabled=True,
invalidate=True, semantic="", visible=True, exposed=False):
invalidate=True, semantic="", visible=True, exposed=False, validators=None):
super(PushButtonParam, self).__init__(name=name, label=label, description=description, value=None,
group=group, advanced=advanced, enabled=enabled, invalidate=invalidate,
semantic=semantic, visible=visible, exposed=exposed)
semantic=semantic, visible=visible, exposed=exposed, validators=validators)
self._valueType = None

def getInstanceType(self):
Expand Down Expand Up @@ -428,14 +446,13 @@
_OVERRIDE_SERIALIZATION_KEY_VALUES = "__ChoiceParam_values__"

def __init__(self, name: str, label: str, description: str, value, values, exclusive=True, saveValuesOverride=False,
group="allParams", joinChar=" ", advanced=False, enabled=True, invalidate=True, semantic="",
validValue=True, errorMessage="",
visible=True, exposed=False):
group="allParams", joinChar=" ", advanced=False, enabled=True, invalidate=True, semantic="",
visible=True, exposed=False, validators=None):

super(ChoiceParam, self).__init__(name=name, label=label, description=description, value=value,
group=group, advanced=advanced, enabled=enabled, invalidate=invalidate,
semantic=semantic, validValue=validValue, errorMessage=errorMessage,
visible=visible, exposed=exposed)
semantic=semantic,
visible=visible, exposed=exposed, validators=validators)
self._values = values
self._saveValuesOverride = saveValuesOverride
self._exclusive = exclusive
Expand Down Expand Up @@ -508,12 +525,12 @@
"""
"""
def __init__(self, name, label, description, value, group="allParams", advanced=False, enabled=True,
invalidate=True, semantic="", uidIgnoreValue=None, validValue=True, errorMessage="", visible=True,
exposed=False):
invalidate=True, semantic="", uidIgnoreValue=None, visible=True,
exposed=False, validators=None):
super(StringParam, self).__init__(name=name, label=label, description=description, value=value,
group=group, advanced=advanced, enabled=enabled, invalidate=invalidate,
semantic=semantic, uidIgnoreValue=uidIgnoreValue, validValue=validValue,
errorMessage=errorMessage, visible=visible, exposed=exposed)
semantic=semantic, uidIgnoreValue=uidIgnoreValue,
visible=visible, exposed=exposed, validators=validators)
self._valueType = str

def validateValue(self, value):
Expand All @@ -534,10 +551,10 @@
"""
"""
def __init__(self, name, label, description, value, group="allParams", advanced=False, enabled=True,
invalidate=True, semantic="", visible=True, exposed=False):
invalidate=True, semantic="", visible=True, exposed=False, validators=None):
super(ColorParam, self).__init__(name=name, label=label, description=description, value=value,
group=group, advanced=advanced, enabled=enabled, invalidate=invalidate,
semantic=semantic, visible=visible, exposed=exposed)
semantic=semantic, visible=visible, exposed=exposed, validators=validators)
self._valueType = str

def validateValue(self, value):
Expand Down
Loading