[graph] handle ListAttribute and GroupAttribute

This commit is contained in:
Yann Lanthony 2017-10-23 19:20:43 +02:00
parent d0c0a28d5d
commit e42e04fd9d

View file

@ -17,7 +17,7 @@ import logging
from . import stats from . import stats
from . import desc from . import desc
from meshroom import core as pg from meshroom import core as pg
from meshroom.common import BaseObject, Model, Slot, Signal, Property from meshroom.common import BaseObject, DictModel, Slot, Signal, Property, Variant, ListModel
# Replace default encoder to support Enums # Replace default encoder to support Enums
DefaultJSONEncoder = json.JSONEncoder # store the original one DefaultJSONEncoder = json.JSONEncoder # store the original one
@ -53,6 +53,30 @@ def hash(v):
return hashObject.hexdigest() return hashObject.hexdigest()
def attribute_factory(description, name, value, node, parent=None):
# type: (desc.Attribute, str, (), Node, Attribute) -> Attribute
"""
Create an Attribute based on description type.
:param description: the Attribute description
:param name: name of the Attribute
:param value: value of the Attribute. Will be set if not None.
:param node: node owning the Attribute. Note that the created Attribute is not added to Node's attributes
:param parent: (optional) parent Attribute (must be ListAttribute or GroupAttribute)
:return:
"""
if isinstance(description, pg.desc.GroupAttribute):
cls = GroupAttribute
elif isinstance(description, pg.desc.ListAttribute):
cls = ListAttribute
else:
cls = Attribute
attr = cls(name, node, description, parent=parent)
if value is not None:
attr.value = value
return attr
class Attribute(BaseObject): class Attribute(BaseObject):
""" """
""" """
@ -71,6 +95,10 @@ class Attribute(BaseObject):
def fullName(self): def fullName(self):
""" Name inside the Graph: nodeName.name """ """ Name inside the Graph: nodeName.name """
if isinstance(self.parent(), ListAttribute):
return '{}[{}]'.format(self.parent().fullName(), self.parent().index(self))
elif isinstance(self.parent(), GroupAttribute):
return '{}.{}'.format(self.parent().fullName(), self._name)
return '{}.{}'.format(self.node.name, self._name) return '{}.{}'.format(self.node.name, self._name)
def getName(self): def getName(self):
@ -80,12 +108,10 @@ class Attribute(BaseObject):
def getLabel(self): def getLabel(self):
return self._label return self._label
@property def _get_value(self):
def value(self): return self.getLinkParam().value if self.isLink else self._value
return self._value
@value.setter def _set_value(self, value):
def value(self, value):
if self._value == value: if self._value == value:
return return
self._value = value self._value = value
@ -153,12 +179,89 @@ class Attribute(BaseObject):
label = Property(str, getLabel, constant=True) label = Property(str, getLabel, constant=True)
desc = Property(desc.Attribute, lambda self: self.attributeDesc, constant=True) desc = Property(desc.Attribute, lambda self: self.attributeDesc, constant=True)
valueChanged = Signal() valueChanged = Signal()
value = Property("QVariant", value.fget, value.fset, notify=valueChanged) value = Property(Variant, _get_value, _set_value, notify=valueChanged)
isOutput = Property(bool, isOutput.fget, constant=True) isOutput = Property(bool, isOutput.fget, constant=True)
isLinkChanged = Signal() isLinkChanged = Signal()
isLink = Property(bool, isLink.fget, notify=isLinkChanged) isLink = Property(bool, isLink.fget, notify=isLinkChanged)
class ListAttribute(Attribute):
def __init__(self, name, node, attributeDesc, parent=None):
super(ListAttribute, self).__init__(name, node, attributeDesc, parent)
self._value = ListModel(parent=self)
def __getitem__(self, item):
return self._value.at(item)
def __len__(self):
return len(self._value)
def _set_value(self, value):
self._value.clear()
self.extend(value)
def append(self, value):
self.extend([value])
def insert(self, value, index):
attr = attribute_factory(self.attributeDesc.elementDesc, "", value, self.node, self)
self._value.insert(index, [attr])
def index(self, item):
return self._value.indexOf(item)
def extend(self, values):
childAttributes = []
for value in values:
attr = attribute_factory(self.attributeDesc.elementDesc, "", value, self.node, parent=self)
childAttributes.append(attr)
self._value.extend(childAttributes)
def remove(self, index):
self._value.removeAt(index)
def getExportValue(self):
return [attr.getExportValue() for attr in self._value]
# Override value property setter
value = Property(Variant, Attribute._get_value, _set_value, notify=Attribute.valueChanged)
class GroupAttribute(Attribute):
def __init__(self, name, node, attributeDesc, parent=None):
super(GroupAttribute, self).__init__(name, node, attributeDesc, parent)
self._value = DictModel(keyAttrName='name', parent=self)
subAttributes = []
for name, subAttrDesc in self.attributeDesc.groupDesc.items():
childAttr = attribute_factory(subAttrDesc, name, None, self.node, parent=self)
subAttributes.append(childAttr)
self._value.reset(subAttributes)
def __getattr__(self, key):
try:
return super(GroupAttribute, self).__getattr__(key)
except AttributeError:
try:
return self._value.get(key)
except KeyError:
raise AttributeError(key)
def _set_value(self, exportedValue):
# set individual child attribute values
for key, value in exportedValue.items():
self._value.get(key).value = value
def getExportValue(self):
return {key: attr.getExportValue() for key, attr in self._value.objects.items()}
# Override value property
value = Property(Variant, Attribute._get_value, _set_value, notify=Attribute.valueChanged)
class Edge(BaseObject): class Edge(BaseObject):
def __init__(self, src, dst, parent=None): def __init__(self, src, dst, parent=None):
@ -217,6 +320,10 @@ class Node(BaseObject):
""" """
""" """
# Regexp handling complex attribute names with recursive understanding of Lists and Groups
# i.e: a.b, a[0], a[0].b.c[1]
attributeRE = re.compile(r'\.?(?P<name>\w+)(?:\[(?P<index>\d+)\])?')
def __init__(self, nodeDesc, parent=None, **kwargs): def __init__(self, nodeDesc, parent=None, **kwargs):
super(Node, self).__init__(parent) super(Node, self).__init__(parent)
self._name = None # type: str self._name = None # type: str
@ -227,7 +334,7 @@ class Node(BaseObject):
self.attributesPerUid = defaultdict(set) self.attributesPerUid = defaultdict(set)
self._initFromDesc() self._initFromDesc()
for k, v in kwargs.items(): for k, v in kwargs.items():
self.attribute(k)._value = v self.attribute(k).value = v
self.status = StatusData(self.name, self.nodeType()) self.status = StatusData(self.name, self.nodeType())
self.statistics = stats.Statistics() self.statistics = stats.Statistics()
self._subprocess = None self._subprocess = None
@ -248,20 +355,36 @@ class Node(BaseObject):
@Slot(str, result=Attribute) @Slot(str, result=Attribute)
def attribute(self, name): def attribute(self, name):
return self._attributes.get(name) att = None
# Complex name indicating group or list attribute
if '[' in name or '.' in name:
p = self.attributeRE.findall(name)
for n, idx in p:
# first step: get root attribute
if att is None:
att = self._attributes.get(n)
else:
# get child Attribute in Group
assert isinstance(att, GroupAttribute)
att = att.value.get(n)
if idx != '':
# get child Attribute in List
assert isinstance(att, ListAttribute)
att = att.value.at(int(idx))
else:
att = self._attributes.get(name)
return att
def getAttributes(self): def getAttributes(self):
return self._attributes return self._attributes
def _initFromDesc(self): def _initFromDesc(self):
# Init from class members # Init from class and instance members
for name, desc in self.nodeDesc.__class__.__dict__.items(): for name, desc in vars(self.nodeDesc.__class__).items() + vars(self.nodeDesc).items():
if issubclass(desc.__class__, pg.desc.Attribute): if issubclass(desc.__class__, pg.desc.Attribute):
self._attributes.add(Attribute(name, self, desc)) self._attributes.add(attribute_factory(desc, name, None, self))
# Init from instance members
for name, desc in self.nodeDesc.__dict__.items():
if issubclass(desc.__class__, pg.desc.Attribute):
self._attributes.add(Attribute(name, self, desc))
# List attributes per uid # List attributes per uid
for attr in self._attributes: for attr in self._attributes:
for uidIndex in attr.attributeDesc.uid: for uidIndex in attr.attributeDesc.uid: