mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-04-29 18:27:23 +02:00
* Core Graph is now updated internally when needed * addNewNode already calls applyExpr on newly created node
204 lines
6.9 KiB
Python
204 lines
6.9 KiB
Python
from PySide2.QtWidgets import QUndoCommand, QUndoStack
|
|
from PySide2.QtCore import Property, Signal
|
|
from meshroom.core.graph import Node
|
|
|
|
|
|
class UndoCommand(QUndoCommand):
|
|
def __init__(self, parent=None):
|
|
super(UndoCommand, self).__init__(parent)
|
|
self._enabled = True
|
|
|
|
def setEnabled(self, enabled):
|
|
self._enabled = enabled
|
|
|
|
def redo(self):
|
|
if not self._enabled:
|
|
return
|
|
self.redoImpl()
|
|
|
|
def undo(self):
|
|
if not self._enabled:
|
|
return
|
|
self.undoImpl()
|
|
|
|
def redoImpl(self):
|
|
# type: () -> bool
|
|
pass
|
|
|
|
def undoImpl(self):
|
|
# type: () -> bool
|
|
pass
|
|
|
|
|
|
class UndoStack(QUndoStack):
|
|
def __init__(self, parent=None):
|
|
super(UndoStack, self).__init__(parent)
|
|
# connect QUndoStack signal to UndoStack's ones
|
|
self.cleanChanged.connect(self._cleanChanged)
|
|
self.canUndoChanged.connect(self._canUndoChanged)
|
|
self.canRedoChanged.connect(self._canRedoChanged)
|
|
self.undoTextChanged.connect(self._undoTextChanged)
|
|
self.redoTextChanged.connect(self._redoTextChanged)
|
|
|
|
def tryAndPush(self, command):
|
|
# type: (UndoCommand) -> bool
|
|
if command.redoImpl():
|
|
command.setEnabled(False)
|
|
self.push(command) # takes ownership
|
|
command.setEnabled(True)
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
# Redeclare QUndoStack signal since original ones can not be used for properties notifying
|
|
_cleanChanged = Signal()
|
|
_canUndoChanged = Signal()
|
|
_canRedoChanged = Signal()
|
|
_undoTextChanged = Signal()
|
|
_redoTextChanged = Signal()
|
|
|
|
clean = Property(bool, QUndoStack.isClean, notify=_cleanChanged)
|
|
canUndo = Property(bool, QUndoStack.canUndo, notify=_canRedoChanged)
|
|
canRedo = Property(bool, QUndoStack.canRedo, notify=_canUndoChanged)
|
|
undoText = Property(str, QUndoStack.undoText, notify=_undoTextChanged)
|
|
redoText = Property(str, QUndoStack.redoText, notify=_redoTextChanged)
|
|
|
|
|
|
class GraphCommand(UndoCommand):
|
|
def __init__(self, graph, parent=None):
|
|
super(GraphCommand, self).__init__(parent)
|
|
self.graph = graph
|
|
|
|
|
|
class AddNodeCommand(GraphCommand):
|
|
def __init__(self, graph, nodeType, parent=None):
|
|
super(AddNodeCommand, self).__init__(graph, parent)
|
|
self.nodeType = nodeType
|
|
self.node = None
|
|
|
|
def redoImpl(self):
|
|
self.node = self.graph.addNewNode(self.nodeType)
|
|
self.setText("Add Node {}".format(self.node.getName()))
|
|
return True
|
|
|
|
def undoImpl(self):
|
|
self.graph.removeNode(self.node.getName())
|
|
self.node = None
|
|
|
|
|
|
class RemoveNodeCommand(GraphCommand):
|
|
def __init__(self, graph, node, parent=None):
|
|
super(RemoveNodeCommand, self).__init__(graph, parent)
|
|
self.nodeDict = node.toDict()
|
|
self.nodeName = node.getName()
|
|
self.setText("Remove Node {}".format(self.nodeName))
|
|
self.outEdges = {}
|
|
|
|
def redoImpl(self):
|
|
# only keep outEdges since inEdges are serialized in nodeDict
|
|
inEdges, self.outEdges = self.graph.removeNode(self.nodeName)
|
|
return True
|
|
|
|
def undoImpl(self):
|
|
node = self.graph.addNode(Node(nodeDesc=self.nodeDict["nodeType"],
|
|
parent=self.graph, **self.nodeDict["attributes"]
|
|
), self.nodeName)
|
|
assert (node.getName() == self.nodeName)
|
|
# recreate out edges deleted on node removal
|
|
for dstAttr, srcAttr in self.outEdges.items():
|
|
self.graph.addEdge(self.graph.attribute(srcAttr),
|
|
self.graph.attribute(dstAttr))
|
|
|
|
|
|
class SetAttributeCommand(GraphCommand):
|
|
def __init__(self, graph, attribute, value, parent=None):
|
|
super(SetAttributeCommand, self).__init__(graph, parent)
|
|
self.attrName = attribute.fullName()
|
|
self.value = value
|
|
self.oldValue = attribute.value
|
|
self.setText("Set Attribute '{}'".format(attribute.fullName()))
|
|
|
|
def redoImpl(self):
|
|
if self.value == self.oldValue:
|
|
return False
|
|
self.graph.attribute(self.attrName).value = self.value
|
|
return True
|
|
|
|
def undoImpl(self):
|
|
self.graph.attribute(self.attrName).value = self.oldValue
|
|
|
|
|
|
class AddEdgeCommand(GraphCommand):
|
|
def __init__(self, graph, src, dst, parent=None):
|
|
super(AddEdgeCommand, self).__init__(graph, parent)
|
|
self.srcAttr = src.fullName()
|
|
self.dstAttr = dst.fullName()
|
|
self.setText("Connect '{}'->'{}'".format(self.srcAttr, self.dstAttr))
|
|
|
|
def redoImpl(self):
|
|
try:
|
|
self.graph.addEdge(self.graph.attribute(self.srcAttr),
|
|
self.graph.attribute(self.dstAttr))
|
|
except RuntimeError:
|
|
return False
|
|
return True
|
|
|
|
def undoImpl(self):
|
|
self.graph.removeEdge(self.graph.attribute(self.dstAttr))
|
|
|
|
|
|
class RemoveEdgeCommand(GraphCommand):
|
|
def __init__(self, graph, edge, parent=None):
|
|
super(RemoveEdgeCommand, self).__init__(graph, parent)
|
|
self.srcAttr = edge.src.fullName()
|
|
self.dstAttr = edge.dst.fullName()
|
|
self.setText("Disconnect '{}'->'{}'".format(self.srcAttr, self.dstAttr))
|
|
|
|
def redoImpl(self):
|
|
self.graph.removeEdge(self.graph.attribute(self.dstAttr))
|
|
return True
|
|
|
|
def undoImpl(self):
|
|
self.graph.addEdge(self.graph.attribute(self.srcAttr),
|
|
self.graph.attribute(self.dstAttr))
|
|
|
|
|
|
class ListAttributeAppendCommand(GraphCommand):
|
|
def __init__(self, graph, listAttribute, value, parent=None):
|
|
super(ListAttributeAppendCommand, self).__init__(graph, parent)
|
|
assert isinstance(listAttribute, ListAttribute)
|
|
self.attrName = listAttribute.fullName()
|
|
self.index = None
|
|
self.value = value
|
|
self.setText("Append to {}".format(self.attrName))
|
|
|
|
def redoImpl(self):
|
|
listAttribute = self.graph.attribute(self.attrName)
|
|
listAttribute.append(self.value)
|
|
self.index = len(listAttribute) - 1
|
|
return True
|
|
|
|
def undoImpl(self):
|
|
listAttribute = self.graph.attribute(self.attrName)
|
|
listAttribute.remove(self.index)
|
|
|
|
|
|
class ListAttributeRemoveCommand(GraphCommand):
|
|
def __init__(self, graph, attribute, parent=None):
|
|
super(ListAttributeRemoveCommand, self).__init__(graph, parent)
|
|
listAttribute = attribute.parent()
|
|
assert isinstance(listAttribute, ListAttribute)
|
|
self.listAttrName = listAttribute.fullName()
|
|
self.index = listAttribute.index(attribute)
|
|
self.value = attribute.getExportValue()
|
|
self.setText("Remove {}".format(attribute.fullName()))
|
|
|
|
def redoImpl(self):
|
|
listAttribute = self.graph.attribute(self.listAttrName)
|
|
listAttribute.remove(self.index)
|
|
return True
|
|
|
|
def undoImpl(self):
|
|
listAttribute = self.graph.attribute(self.listAttrName)
|
|
listAttribute.insert(self.value, self.index)
|
|
|