diff --git a/meshroom/core/graph.py b/meshroom/core/graph.py index 89faefb5..c1e690f1 100644 --- a/meshroom/core/graph.py +++ b/meshroom/core/graph.py @@ -396,6 +396,15 @@ class Graph(BaseObject): return duplicates + def pasteNode(self, nodeType, **kwargs): + name = self._createUniqueNodeName(nodeType) + node = None + with GraphModification(self): + node = Node(nodeType, **kwargs) + self._addNode(node, name) + self._applyExpr() + return node + def outEdges(self, attribute): """ Return the list of edges starting from the given attribute """ # type: (Attribute,) -> [Edge] diff --git a/meshroom/ui/commands.py b/meshroom/ui/commands.py index 615d6036..b4e4734c 100755 --- a/meshroom/ui/commands.py +++ b/meshroom/ui/commands.py @@ -195,6 +195,26 @@ class DuplicateNodesCommand(GraphCommand): self.graph.removeNode(duplicate) +class PasteNodeCommand(GraphCommand): + """ + Handle node pasting in a Graph. + """ + def __init__(self, graph, nodeType, parent=None, **kwargs): + super(PasteNodeCommand, self).__init__(graph, parent) + self.nodeType = nodeType + self.nodeName = None + self.kwargs = kwargs + + def redoImpl(self): + node = self.graph.pasteNode(self.nodeType, **self.kwargs) + self.nodeName = node.name + self.setText("Paste Node {}".format(self.nodeName)) + return node + + def undoImpl(self): + self.graph.removeNode(self.nodeName) + + class SetAttributeCommand(GraphCommand): def __init__(self, graph, attribute, value, parent=None): super(SetAttributeCommand, self).__init__(graph, parent) diff --git a/meshroom/ui/components/clipboard.py b/meshroom/ui/components/clipboard.py index 2cca6e1a..4de692c9 100644 --- a/meshroom/ui/components/clipboard.py +++ b/meshroom/ui/components/clipboard.py @@ -15,6 +15,10 @@ class ClipboardHelper(QObject): def setText(self, value): self._clipboard.setText(value) + @Slot(result=str) + def getText(self): + return self._clipboard.text() + @Slot() def clear(self): self._clipboard.clear() diff --git a/meshroom/ui/graph.py b/meshroom/ui/graph.py index 661e1697..119102ec 100644 --- a/meshroom/ui/graph.py +++ b/meshroom/ui/graph.py @@ -766,6 +766,44 @@ class UIGraph(QObject): return json.dumps(node, indent=4) return '' + @Slot(str) + def pasteNode(self, clipboardContent): + """ + Parse the content of the clipboard to see whether it contains + a valid node description. If that is the case, the node described + in the clipboard is built with the available information. + Otherwise, nothing is done. + + This function does not need to be preceded by a call to "copyNodeContent". + Any clipboard content that contains at least a node type with a valid JSON + formatting (dictionary form with double quotes around the keys and values) + can be used to generate a node. + + For example, it is enough to have: + {"nodeType":"CameraInit"} + in the clipboard to create a default CameraInit node. + """ + if not clipboardContent: + return + d = {} + try: + d = json.loads(clipboardContent) + except ValueError as e: + raise ValueError(e) + + if not isinstance(d, dict): + raise ValueError("The clipboard does not contain a valid node. Cannot paste it.") + + nodeType = d.get("nodeType", None) + if not nodeType: + raise ValueError("The clipboard does not contain a valid node. Cannot paste it.") + + attributes = {} + attributes.update(d.get("inputs", {})) + attributes.update(d.get("outputs", {})) + + self.push(commands.PasteNodeCommand(self._graph, nodeType, **attributes)) + undoStack = Property(QObject, lambda self: self._undoStack, constant=True) graphChanged = Signal() graph = Property(Graph, lambda self: self._graph, notify=graphChanged) diff --git a/meshroom/ui/qml/GraphEditor/GraphEditor.qml b/meshroom/ui/qml/GraphEditor/GraphEditor.qml index 0d776fb4..c03f98c9 100755 --- a/meshroom/ui/qml/GraphEditor/GraphEditor.qml +++ b/meshroom/ui/qml/GraphEditor/GraphEditor.qml @@ -85,6 +85,12 @@ Item { } } + /// Paste content of clipboard to graph editor and create new node if valid + function pasteNode() + { + var copiedContent = Clipboard.getText() + uigraph.pasteNode(copiedContent) + } Keys.onPressed: { if (event.key === Qt.Key_F) @@ -100,6 +106,9 @@ Item { if (event.key === Qt.Key_C) if (event.modifiers == Qt.ControlModifier) copyNode() + if (event.key === Qt.Key_V) + if (event.modifiers == Qt.ControlModifier) + pasteNode() } MouseArea {