Meshroom/meshroom/ui/commands.py
2017-10-19 17:48:27 +02:00

166 lines
5.5 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()))
self.node._applyExpr()
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))
node.updateInternals()
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.node(self.nodeName).attribute(self.attrName).value = self.value
return True
def undoImpl(self):
self.graph.node(self.nodeName).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))