Meshroom/meshroom/ui/commands.py
Yann Lanthony a81aa53a6b [processGraph] Attribute.value as an "hybrid" property
Use "hybrid" property to simplify naming of properties. No matter the backend used (qt or core), the property is always accessible via its name, without differently named getter or setter methods.
2017-10-02 17:47:39 +02:00

130 lines
4.3 KiB
Python

from PySide2.QtWidgets import QUndoCommand, QUndoStack
from PySide2.QtCore import Property, Signal
from meshroom.processGraph.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) -> bool:
pass
def undoImpl(self) -> 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: UndoCommand):
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.nodeDesc = node.toDict()
self.nodeName = node.getName()
self.setText("Remove Node {}".format(self.nodeName))
self.edges = {}
def redoImpl(self):
self.edges = self.graph.removeNode(self.nodeName)
return True
def undoImpl(self):
node = self.graph.addNode(Node(nodeDesc=self.nodeDesc["nodeType"],
parent=self.graph, **self.nodeDesc["attributes"]
), self.nodeName)
assert (node.getName() == self.nodeName)
# recreate edges deleted on node removal
for key, value in self.edges.items():
iNode, iAttr = key.split(".")
oNode, oAttr = value.split(".")
self.graph.addEdge(self.graph.node(oNode).attribute(oAttr),
self.graph.node(iNode).attribute(iAttr))
node.updateInternals()
class SetAttributeCommand(GraphCommand):
def __init__(self, graph, attribute, value, parent=None):
super(SetAttributeCommand, self).__init__(graph, parent)
self.nodeName = attribute.node.getName()
self.attrName = attribute.getName()
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