mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-05-19 20:16:30 +02:00
[core] fix memory management
* parent Nodes, Edges and Attributes to their respective models * use weakrefs for those objects to avoid cyclic references * add 'root' property on Attribute for parenting to List/GroupAttribute (parent still exists for Qt-style parenting) * UI: update commands to match those changes
This commit is contained in:
parent
f029a573b2
commit
e38f112c55
2 changed files with 54 additions and 38 deletions
|
@ -5,8 +5,7 @@ import collections
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import psutil
|
import weakref
|
||||||
import re
|
|
||||||
import shutil
|
import shutil
|
||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
|
@ -82,7 +81,7 @@ def hash(v):
|
||||||
return hashObject.hexdigest()
|
return hashObject.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
def attribute_factory(description, value, isOutput, node, parent=None):
|
def attribute_factory(description, value, isOutput, node, root=None, parent=None):
|
||||||
# type: (desc.Attribute, (), bool, Node, Attribute) -> Attribute
|
# type: (desc.Attribute, (), bool, Node, Attribute) -> Attribute
|
||||||
"""
|
"""
|
||||||
Create an Attribute based on description type.
|
Create an Attribute based on description type.
|
||||||
|
@ -92,7 +91,8 @@ def attribute_factory(description, value, isOutput, node, parent=None):
|
||||||
value: value of the Attribute. Will be set if not None.
|
value: value of the Attribute. Will be set if not None.
|
||||||
isOutput: whether is Attribute is an output attribute.
|
isOutput: whether is Attribute is an output attribute.
|
||||||
node: node owning the Attribute. Note that the created Attribute is not added to Node's attributes
|
node: node owning the Attribute. Note that the created Attribute is not added to Node's attributes
|
||||||
parent: (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
|
||||||
"""
|
"""
|
||||||
if isinstance(description, meshroom.core.desc.GroupAttribute):
|
if isinstance(description, meshroom.core.desc.GroupAttribute):
|
||||||
cls = GroupAttribute
|
cls = GroupAttribute
|
||||||
|
@ -100,7 +100,7 @@ def attribute_factory(description, value, isOutput, node, parent=None):
|
||||||
cls = ListAttribute
|
cls = ListAttribute
|
||||||
else:
|
else:
|
||||||
cls = Attribute
|
cls = Attribute
|
||||||
attr = cls(node, description, isOutput, parent=parent)
|
attr = cls(node, description, isOutput, root, parent)
|
||||||
if value is not None:
|
if value is not None:
|
||||||
attr.value = value
|
attr.value = value
|
||||||
return attr
|
return attr
|
||||||
|
@ -110,10 +110,21 @@ class Attribute(BaseObject):
|
||||||
"""
|
"""
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, node, attributeDesc, isOutput, parent=None):
|
def __init__(self, node, attributeDesc, isOutput, root=None, parent=None):
|
||||||
|
"""
|
||||||
|
Attribute constructor
|
||||||
|
|
||||||
|
Args:
|
||||||
|
node (Node): the Node hosting this Attribute
|
||||||
|
attributeDesc (desc.Attribute): the description of this Attribute
|
||||||
|
isOutput (bool): whether this Attribute is an output of the Node
|
||||||
|
root (Attribute): (optional) the root Attribute (List or Group) containing this one
|
||||||
|
parent (BaseObject): (optional) the parent BaseObject
|
||||||
|
"""
|
||||||
super(Attribute, self).__init__(parent)
|
super(Attribute, self).__init__(parent)
|
||||||
self._name = attributeDesc.name
|
self._name = attributeDesc.name
|
||||||
self.node = node # type: Node
|
self._root = None if root is None else weakref.ref(root)
|
||||||
|
self._node = weakref.ref(node)
|
||||||
self.attributeDesc = attributeDesc
|
self.attributeDesc = attributeDesc
|
||||||
self._isOutput = isOutput
|
self._isOutput = isOutput
|
||||||
self._value = attributeDesc.value
|
self._value = attributeDesc.value
|
||||||
|
@ -122,15 +133,22 @@ class Attribute(BaseObject):
|
||||||
# invalidation value for output attributes
|
# invalidation value for output attributes
|
||||||
self._invalidationValue = ""
|
self._invalidationValue = ""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def node(self):
|
||||||
|
return self._node()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def root(self):
|
||||||
|
return self._root() if self._root else None
|
||||||
def absoluteName(self):
|
def absoluteName(self):
|
||||||
return '{}.{}.{}'.format(self.node.graph.name, self.node.name, self._name)
|
return '{}.{}.{}'.format(self.node.graph.name, self.node.name, self._name)
|
||||||
|
|
||||||
def fullName(self):
|
def fullName(self):
|
||||||
""" Name inside the Graph: nodeName.name """
|
""" Name inside the Graph: nodeName.name """
|
||||||
if isinstance(self.parent(), ListAttribute):
|
if isinstance(self.root, ListAttribute):
|
||||||
return '{}[{}]'.format(self.parent().fullName(), self.parent().index(self))
|
return '{}[{}]'.format(self.root.fullName(), self.root.index(self))
|
||||||
elif isinstance(self.parent(), GroupAttribute):
|
elif isinstance(self.root, GroupAttribute):
|
||||||
return '{}.{}'.format(self.parent().fullName(), self._name)
|
return '{}.{}'.format(self.root.fullName(), self._name)
|
||||||
return '{}.{}'.format(self.node.name, self._name)
|
return '{}.{}'.format(self.node.name, self._name)
|
||||||
|
|
||||||
def getName(self):
|
def getName(self):
|
||||||
|
@ -234,8 +252,8 @@ class Attribute(BaseObject):
|
||||||
|
|
||||||
class ListAttribute(Attribute):
|
class ListAttribute(Attribute):
|
||||||
|
|
||||||
def __init__(self, node, attributeDesc, isOutput, parent=None):
|
def __init__(self, node, attributeDesc, isOutput, root=None, parent=None):
|
||||||
super(ListAttribute, self).__init__(node, attributeDesc, isOutput, parent)
|
super(ListAttribute, self).__init__(node, attributeDesc, isOutput, root, parent)
|
||||||
self._value = ListModel(parent=self)
|
self._value = ListModel(parent=self)
|
||||||
|
|
||||||
def __getitem__(self, item):
|
def __getitem__(self, item):
|
||||||
|
@ -251,19 +269,17 @@ class ListAttribute(Attribute):
|
||||||
def append(self, value):
|
def append(self, value):
|
||||||
self.extend([value])
|
self.extend([value])
|
||||||
|
|
||||||
def insert(self, value, index):
|
def insert(self, index, value):
|
||||||
attr = attribute_factory(self.attributeDesc.elementDesc, value, self.isOutput, self.node, self)
|
values = value if isinstance(value, list) else [value]
|
||||||
self._value.insert(index, [attr])
|
attrs = [attribute_factory(self.attributeDesc.elementDesc, v, self.isOutput, self.node, self) for v in values]
|
||||||
|
self._value.insert(index, attrs)
|
||||||
|
|
||||||
def index(self, item):
|
def index(self, item):
|
||||||
return self._value.indexOf(item)
|
return self._value.indexOf(item)
|
||||||
|
|
||||||
def extend(self, values):
|
def extend(self, values):
|
||||||
childAttributes = []
|
values = [attribute_factory(self.attributeDesc.elementDesc, v, self.isOutput, self.node, self) for v in values]
|
||||||
for value in values:
|
self._value.extend(values)
|
||||||
attr = attribute_factory(self.attributeDesc.elementDesc, value, self.isOutput, self.node, parent=self)
|
|
||||||
childAttributes.append(attr)
|
|
||||||
self._value.extend(childAttributes)
|
|
||||||
|
|
||||||
def remove(self, index):
|
def remove(self, index):
|
||||||
self._value.removeAt(index)
|
self._value.removeAt(index)
|
||||||
|
@ -288,13 +304,13 @@ class ListAttribute(Attribute):
|
||||||
|
|
||||||
class GroupAttribute(Attribute):
|
class GroupAttribute(Attribute):
|
||||||
|
|
||||||
def __init__(self, node, attributeDesc, isOutput, parent=None):
|
def __init__(self, node, attributeDesc, isOutput, root=None, parent=None):
|
||||||
super(GroupAttribute, self).__init__(node, attributeDesc, isOutput, parent)
|
super(GroupAttribute, self).__init__(node, attributeDesc, isOutput, root, parent)
|
||||||
self._value = DictModel(keyAttrName='name', parent=self)
|
self._value = DictModel(keyAttrName='name', parent=self)
|
||||||
|
|
||||||
subAttributes = []
|
subAttributes = []
|
||||||
for subAttrDesc in self.attributeDesc.groupDesc:
|
for subAttrDesc in self.attributeDesc.groupDesc:
|
||||||
childAttr = attribute_factory(subAttrDesc, None, self.isOutput, self.node, parent=self)
|
childAttr = attribute_factory(subAttrDesc, None, self.isOutput, self.node, self)
|
||||||
subAttributes.append(childAttr)
|
subAttributes.append(childAttr)
|
||||||
|
|
||||||
self._value.reset(subAttributes)
|
self._value.reset(subAttributes)
|
||||||
|
@ -335,16 +351,17 @@ class Edge(BaseObject):
|
||||||
|
|
||||||
def __init__(self, src, dst, parent=None):
|
def __init__(self, src, dst, parent=None):
|
||||||
super(Edge, self).__init__(parent)
|
super(Edge, self).__init__(parent)
|
||||||
self._src = src
|
self._src = weakref.ref(src)
|
||||||
self._dst = dst
|
self._dst = weakref.ref(dst)
|
||||||
|
self._repr = "<Edge> {} -> {}".format(self._src(), self._dst())
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def src(self):
|
def src(self):
|
||||||
return self._src
|
return self._src()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def dst(self):
|
def dst(self):
|
||||||
return self._dst
|
return self._dst()
|
||||||
|
|
||||||
src = Property(Attribute, src.fget, constant=True)
|
src = Property(Attribute, src.fget, constant=True)
|
||||||
dst = Property(Attribute, dst.fget, constant=True)
|
dst = Property(Attribute, dst.fget, constant=True)
|
||||||
|
@ -477,11 +494,11 @@ class Node(BaseObject):
|
||||||
|
|
||||||
for attrDesc in self.nodeDesc.inputs:
|
for attrDesc in self.nodeDesc.inputs:
|
||||||
assert isinstance(attrDesc, meshroom.core.desc.Attribute)
|
assert isinstance(attrDesc, meshroom.core.desc.Attribute)
|
||||||
self._attributes.add(attribute_factory(attrDesc, None, False, self, self))
|
self._attributes.add(attribute_factory(attrDesc, None, False, self))
|
||||||
|
|
||||||
for attrDesc in self.nodeDesc.outputs:
|
for attrDesc in self.nodeDesc.outputs:
|
||||||
assert isinstance(attrDesc, meshroom.core.desc.Attribute)
|
assert isinstance(attrDesc, meshroom.core.desc.Attribute)
|
||||||
self._attributes.add(attribute_factory(attrDesc, None, True, self, self))
|
self._attributes.add(attribute_factory(attrDesc, None, True, self))
|
||||||
|
|
||||||
# List attributes per uid
|
# List attributes per uid
|
||||||
for attr in self._attributes:
|
for attr in self._attributes:
|
||||||
|
@ -771,7 +788,7 @@ class Graph(BaseObject):
|
||||||
for nodeName, nodeData in graphData.items():
|
for nodeName, nodeData in graphData.items():
|
||||||
if not isinstance(nodeData, dict):
|
if not isinstance(nodeData, dict):
|
||||||
raise RuntimeError('loadGraph error: Node is not a dict. File: {}'.format(filepath))
|
raise RuntimeError('loadGraph error: Node is not a dict. File: {}'.format(filepath))
|
||||||
n = Node(nodeData['nodeType'], parent=self, **nodeData['attributes'])
|
n = Node(nodeData['nodeType'], **nodeData['attributes'])
|
||||||
# Add node to the graph with raw attributes values
|
# Add node to the graph with raw attributes values
|
||||||
self._addNode(n, nodeName)
|
self._addNode(n, nodeName)
|
||||||
|
|
||||||
|
@ -851,8 +868,7 @@ class Graph(BaseObject):
|
||||||
:return:
|
:return:
|
||||||
:rtype: Node
|
:rtype: Node
|
||||||
"""
|
"""
|
||||||
node = self.addNode(Node(nodeDesc=nodeType, parent=self, **kwargs))
|
return self.addNode(Node(nodeDesc=nodeType, **kwargs))
|
||||||
return node
|
|
||||||
|
|
||||||
def _createUniqueNodeName(self, inputName):
|
def _createUniqueNodeName(self, inputName):
|
||||||
i = 1
|
i = 1
|
||||||
|
@ -917,11 +933,10 @@ class Graph(BaseObject):
|
||||||
def removeEdge(self, dstAttr):
|
def removeEdge(self, dstAttr):
|
||||||
if dstAttr not in self.edges.keys():
|
if dstAttr not in self.edges.keys():
|
||||||
raise RuntimeError('Attribute "{}" is not connected'.format(dstAttr.fullName()))
|
raise RuntimeError('Attribute "{}" is not connected'.format(dstAttr.fullName()))
|
||||||
edge = self.edges.pop(dstAttr)
|
self.edges.pop(dstAttr)
|
||||||
dstAttr.valueChanged.emit()
|
dstAttr.valueChanged.emit()
|
||||||
dstAttr.isLinkChanged.emit()
|
dstAttr.isLinkChanged.emit()
|
||||||
self.update()
|
self.update()
|
||||||
return edge
|
|
||||||
|
|
||||||
def getDepth(self, node):
|
def getDepth(self, node):
|
||||||
# TODO: would be better to use bfs instead of recursive function
|
# TODO: would be better to use bfs instead of recursive function
|
||||||
|
|
|
@ -101,7 +101,7 @@ class RemoveNodeCommand(GraphCommand):
|
||||||
|
|
||||||
def undoImpl(self):
|
def undoImpl(self):
|
||||||
node = self.graph.addNode(Node(nodeDesc=self.nodeDict["nodeType"],
|
node = self.graph.addNode(Node(nodeDesc=self.nodeDict["nodeType"],
|
||||||
parent=self.graph, **self.nodeDict["attributes"]
|
**self.nodeDict["attributes"]
|
||||||
), self.nodeName)
|
), self.nodeName)
|
||||||
assert (node.getName() == self.nodeName)
|
assert (node.getName() == self.nodeName)
|
||||||
# recreate out edges deleted on node removal
|
# recreate out edges deleted on node removal
|
||||||
|
@ -186,7 +186,7 @@ class ListAttributeAppendCommand(GraphCommand):
|
||||||
class ListAttributeRemoveCommand(GraphCommand):
|
class ListAttributeRemoveCommand(GraphCommand):
|
||||||
def __init__(self, graph, attribute, parent=None):
|
def __init__(self, graph, attribute, parent=None):
|
||||||
super(ListAttributeRemoveCommand, self).__init__(graph, parent)
|
super(ListAttributeRemoveCommand, self).__init__(graph, parent)
|
||||||
listAttribute = attribute.parent()
|
listAttribute = attribute.root
|
||||||
assert isinstance(listAttribute, ListAttribute)
|
assert isinstance(listAttribute, ListAttribute)
|
||||||
self.listAttrName = listAttribute.fullName()
|
self.listAttrName = listAttribute.fullName()
|
||||||
self.index = listAttribute.index(attribute)
|
self.index = listAttribute.index(attribute)
|
||||||
|
@ -200,5 +200,6 @@ class ListAttributeRemoveCommand(GraphCommand):
|
||||||
|
|
||||||
def undoImpl(self):
|
def undoImpl(self):
|
||||||
listAttribute = self.graph.attribute(self.listAttrName)
|
listAttribute = self.graph.attribute(self.listAttrName)
|
||||||
listAttribute.insert(self.value, self.index)
|
listAttribute.insert(self.index, self.value)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue