mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-05-21 04:56:28 +02:00
[ui] grapheditor: explicitly pass selected nodes argument
This commit is contained in:
parent
aef50d9bde
commit
acf1bf2116
4 changed files with 105 additions and 155 deletions
|
@ -362,45 +362,15 @@ class Graph(BaseObject):
|
||||||
child.resetValue()
|
child.resetValue()
|
||||||
return node, skippedEdges
|
return node, skippedEdges
|
||||||
|
|
||||||
def duplicateNode(self, srcNode):
|
def duplicateNodes(self, srcNodes):
|
||||||
""" Duplicate a node in the graph with its connections.
|
""" Duplicate nodes in the graph with their connections.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
srcNode: the node to duplicate
|
srcNodes: the nodes to duplicate
|
||||||
|
|
||||||
Returns:
|
|
||||||
Node: the created node
|
|
||||||
"""
|
|
||||||
node, edges = self.copyNode(srcNode, withEdges=True)
|
|
||||||
return self.addNode(node)
|
|
||||||
|
|
||||||
def duplicateNodesFromNode(self, fromNode):
|
|
||||||
"""
|
|
||||||
Duplicate 'fromNode' and all the following nodes towards graph's leaves.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
fromNode (Node): the node to start the duplication from
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
OrderedDict[Node, Node]: the source->duplicate map
|
OrderedDict[Node, Node]: the source->duplicate map
|
||||||
"""
|
"""
|
||||||
srcNodes, srcEdges = self.dfsOnDiscover(startNodes=[fromNode], reverse=True, dependenciesOnly=True)
|
|
||||||
return self.duplicateNodes(srcNodes, srcEdges)
|
|
||||||
|
|
||||||
def duplicateNodesFromList(self, nodes):
|
|
||||||
"""
|
|
||||||
Duplicate 'nodes'.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
nodes (list[Node]): the nodes to duplicate
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
OrderedDict[Node, Node]: the source->duplicate map
|
|
||||||
"""
|
|
||||||
srcEdges = [ self.nodeInEdges(n) for n in nodes ]
|
|
||||||
return self.duplicateNodes(nodes, srcEdges)
|
|
||||||
|
|
||||||
def duplicateNodes(self, srcNodes, srcEdges):
|
|
||||||
# use OrderedDict to keep duplicated nodes creation order
|
# use OrderedDict to keep duplicated nodes creation order
|
||||||
duplicates = OrderedDict()
|
duplicates = OrderedDict()
|
||||||
|
|
||||||
|
|
|
@ -159,7 +159,7 @@ class RemoveNodeCommand(GraphCommand):
|
||||||
|
|
||||||
def redoImpl(self):
|
def redoImpl(self):
|
||||||
# only keep outEdges since inEdges are serialized in nodeDict
|
# only keep outEdges since inEdges are serialized in nodeDict
|
||||||
inEdges, self.outEdges = self.graph.removeNode(self.nodeName)
|
_, self.outEdges = self.graph.removeNode(self.nodeName)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def undoImpl(self):
|
def undoImpl(self):
|
||||||
|
@ -173,55 +173,26 @@ class RemoveNodeCommand(GraphCommand):
|
||||||
self.graph.attribute(dstAttr))
|
self.graph.attribute(dstAttr))
|
||||||
|
|
||||||
|
|
||||||
class _DuplicateNodes(GraphCommand):
|
class DuplicateNodesCommand(GraphCommand):
|
||||||
def __init__(self, graph, parent=None):
|
|
||||||
super(_DuplicateNodes, self).__init__(graph, parent)
|
|
||||||
self.duplicates = []
|
|
||||||
|
|
||||||
def undoImpl(self):
|
|
||||||
# delete all the duplicated nodes
|
|
||||||
for nodeName in self.duplicates:
|
|
||||||
self.graph.removeNode(nodeName)
|
|
||||||
|
|
||||||
|
|
||||||
class DuplicateNodeCommand(_DuplicateNodes):
|
|
||||||
"""
|
|
||||||
Handle node duplication in a Graph.
|
|
||||||
"""
|
|
||||||
def __init__(self, graph, srcNode, duplicateFollowingNodes, parent=None):
|
|
||||||
super(DuplicateNodeCommand, self).__init__(graph, parent)
|
|
||||||
self.srcNodeName = srcNode.name
|
|
||||||
self.duplicateFollowingNodes = duplicateFollowingNodes
|
|
||||||
|
|
||||||
def redoImpl(self):
|
|
||||||
srcNode = self.graph.node(self.srcNodeName)
|
|
||||||
|
|
||||||
if self.duplicateFollowingNodes:
|
|
||||||
duplicates = list(self.graph.duplicateNodesFromNode(srcNode).values())
|
|
||||||
self.setText("Duplicate {} nodes from {}".format(len(duplicates), self.srcNodeName))
|
|
||||||
else:
|
|
||||||
duplicates = [self.graph.duplicateNode(srcNode)]
|
|
||||||
self.setText("Duplicate {}".format(self.srcNodeName))
|
|
||||||
|
|
||||||
self.duplicates = [n.name for n in duplicates]
|
|
||||||
return duplicates
|
|
||||||
|
|
||||||
|
|
||||||
class DuplicateNodeListCommand(_DuplicateNodes):
|
|
||||||
"""
|
"""
|
||||||
Handle node duplication in a Graph.
|
Handle node duplication in a Graph.
|
||||||
"""
|
"""
|
||||||
def __init__(self, graph, srcNodes, parent=None):
|
def __init__(self, graph, srcNodes, parent=None):
|
||||||
super(DuplicateNodeListCommand, self).__init__(graph, parent)
|
super(DuplicateNodesCommand, self).__init__(graph, parent)
|
||||||
self.srcNodeNames = [ srcNode.name for srcNode in srcNodes ]
|
self.srcNodeNames = [ n.name for n in srcNodes ]
|
||||||
self.setText("Duplicate selected nodes")
|
self.setText("Duplicate Nodes")
|
||||||
|
|
||||||
def redoImpl(self):
|
def redoImpl(self):
|
||||||
srcNodes = [ self.graph.node(srcNodeName) for srcNodeName in self.srcNodeNames ]
|
srcNodes = [ self.graph.node(i) for i in self.srcNodeNames ]
|
||||||
duplicates = list(self.graph.duplicateNodesFromList(srcNodes).values())
|
duplicates = list(self.graph.duplicateNodes(srcNodes).values())
|
||||||
self.duplicates = [ n.name for n in duplicates ]
|
self.duplicates = [ n.name for n in duplicates ]
|
||||||
return duplicates
|
return duplicates
|
||||||
|
|
||||||
|
def undoImpl(self):
|
||||||
|
# remove all duplicates
|
||||||
|
for duplicate in self.duplicates:
|
||||||
|
self.graph.removeNode(duplicate)
|
||||||
|
|
||||||
|
|
||||||
class SetAttributeCommand(GraphCommand):
|
class SetAttributeCommand(GraphCommand):
|
||||||
def __init__(self, graph, attribute, value, parent=None):
|
def __init__(self, graph, attribute, value, parent=None):
|
||||||
|
|
|
@ -500,53 +500,52 @@ class UIGraph(QObject):
|
||||||
position = Position(position.x(), position.y())
|
position = Position(position.x(), position.y())
|
||||||
return self.push(commands.AddNodeCommand(self._graph, nodeType, position=position, **kwargs))
|
return self.push(commands.AddNodeCommand(self._graph, nodeType, position=position, **kwargs))
|
||||||
|
|
||||||
@Slot(QObject, result=bool)
|
def filterNodes(self, nodes):
|
||||||
def nodeSelection(self, node):
|
"""Filter out the nodes that do not exist on the graph."""
|
||||||
""" If the node is part of the selection or not. """
|
return [ n for n in nodes if n in self._graph.nodes.values() ]
|
||||||
length = len(self._selectedNodes) > 1
|
|
||||||
return length and self._selectedNodes.contains(node) if node else length
|
|
||||||
|
|
||||||
@Slot(Node, QPoint)
|
@Slot(Node, QPoint, QObject)
|
||||||
def moveNode(self, node, position):
|
def moveNode(self, node, position, nodes=None):
|
||||||
"""
|
"""
|
||||||
Move 'node' to the given 'position'.
|
Move 'node' to the given 'position' and also update the positions of 'nodes' if neccessary.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
node (Node): the node to move
|
node (Node): the node to move
|
||||||
position (QPoint): the target position
|
position (QPoint): the target position
|
||||||
|
nodes (list[Node]): the nodes to update the position of
|
||||||
"""
|
"""
|
||||||
|
if not nodes:
|
||||||
|
nodes = [node]
|
||||||
|
nodes = self.filterNodes(nodes)
|
||||||
if isinstance(position, QPoint):
|
if isinstance(position, QPoint):
|
||||||
if self.nodeSelection(node):
|
|
||||||
self.moveSelectedNodes(position.x() - node.x, position.y() - node.y)
|
|
||||||
return
|
|
||||||
position = Position(position.x(), position.y())
|
position = Position(position.x(), position.y())
|
||||||
self.push(commands.MoveNodeCommand(self._graph, node, position))
|
deltaX = position.x - node.x
|
||||||
|
deltaY = position.y - node.y
|
||||||
def moveSelectedNodes(self, deltaX, deltaY):
|
|
||||||
with self.groupedGraphModification("Move Selected Nodes"):
|
with self.groupedGraphModification("Move Selected Nodes"):
|
||||||
for node in self._selectedNodes:
|
for n in nodes:
|
||||||
position = Position(node.x + deltaX, node.y + deltaY)
|
position = Position(n.x + deltaX, n.y + deltaY)
|
||||||
self.push(commands.MoveNodeCommand(self._graph, node, position))
|
self.push(commands.MoveNodeCommand(self._graph, n, position))
|
||||||
|
|
||||||
@Slot(Node)
|
@Slot(QObject)
|
||||||
def removeNode(self, node):
|
def removeNodes(self, nodes):
|
||||||
if self.nodeSelection(node):
|
"""
|
||||||
self.removeSelectedNodes()
|
Remove 'nodes' from the graph.
|
||||||
return
|
|
||||||
if node.locked:
|
|
||||||
return
|
|
||||||
self.push(commands.RemoveNodeCommand(self._graph, node))
|
|
||||||
|
|
||||||
def removeSelectedNodes(self):
|
Args:
|
||||||
|
nodes (list[Node]): the nodes to remove
|
||||||
|
"""
|
||||||
|
nodes = self.filterNodes(nodes)
|
||||||
|
if any([ n.locked for n in nodes ]):
|
||||||
|
return
|
||||||
with self.groupedGraphModification("Remove Selected Nodes"):
|
with self.groupedGraphModification("Remove Selected Nodes"):
|
||||||
for node in self._selectedNodes:
|
for node in nodes:
|
||||||
if not node.locked:
|
|
||||||
self.push(commands.RemoveNodeCommand(self._graph, node))
|
self.push(commands.RemoveNodeCommand(self._graph, node))
|
||||||
|
|
||||||
@Slot(Node)
|
@Slot(Node)
|
||||||
def removeNodesFrom(self, startNode):
|
def removeNodesFrom(self, startNode):
|
||||||
"""
|
"""
|
||||||
Remove all nodes starting from 'startNode' to graph leaves.
|
Remove all nodes starting from 'startNode' to graph leaves.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
startNode (Node): the node to start from.
|
startNode (Node): the node to start from.
|
||||||
"""
|
"""
|
||||||
|
@ -556,9 +555,52 @@ class UIGraph(QObject):
|
||||||
nodes, _ = self._graph.dfsOnDiscover(startNodes=[startNode], reverse=True, dependenciesOnly=True)
|
nodes, _ = self._graph.dfsOnDiscover(startNodes=[startNode], reverse=True, dependenciesOnly=True)
|
||||||
# Perform nodes removal from leaves to start node so that edges
|
# Perform nodes removal from leaves to start node so that edges
|
||||||
# can be re-created in correct order on redo.
|
# can be re-created in correct order on redo.
|
||||||
for node in reversed(nodes):
|
self.removeNodes(list(reversed(nodes)))
|
||||||
if not node.locked:
|
|
||||||
self.removeNode(node)
|
@Slot(QObject, result="QVariantList")
|
||||||
|
def duplicateNodes(self, nodes):
|
||||||
|
"""
|
||||||
|
Duplicate 'nodes'.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
nodes (list[Node]): the nodes to duplicate
|
||||||
|
Returns:
|
||||||
|
list[Node]: the list of duplicated nodes
|
||||||
|
"""
|
||||||
|
nodes = self.filterNodes(nodes)
|
||||||
|
# enable updates between duplication and layout to get correct depths during layout
|
||||||
|
with self.groupedGraphModification("Duplicate Selected Nodes", disableUpdates=False):
|
||||||
|
# disable graph updates during duplication
|
||||||
|
with self.groupedGraphModification("Node duplication", disableUpdates=True):
|
||||||
|
duplicates = self.push(commands.DuplicateNodesCommand(self._graph, nodes))
|
||||||
|
# move nodes below the bounding box formed by the duplicated node(s)
|
||||||
|
bbox = self._layout.boundingBox(duplicates)
|
||||||
|
for n in duplicates:
|
||||||
|
self.moveNode(n, Position(n.x, bbox[3] + self.layout.gridSpacing + n.y))
|
||||||
|
return duplicates
|
||||||
|
|
||||||
|
@Slot(Node, result="QVariantList")
|
||||||
|
def duplicateNodesFrom(self, startNode):
|
||||||
|
"""
|
||||||
|
Duplicate all nodes starting from 'startNode' to graph leaves.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
startNode (Node): the node to start from.
|
||||||
|
Returns:
|
||||||
|
list[Node]: the list of duplicated nodes
|
||||||
|
"""
|
||||||
|
if not startNode:
|
||||||
|
return
|
||||||
|
with self.groupedGraphModification("Duplicate Nodes from {}".format(startNode.name)):
|
||||||
|
nodes, _ = self._graph.dfsOnDiscover(startNodes=[startNode], reverse=True, dependenciesOnly=True)
|
||||||
|
duplicates = self.duplicateNodes(nodes)
|
||||||
|
return duplicates
|
||||||
|
|
||||||
|
@Slot(QObject)
|
||||||
|
def clearData(self, nodes):
|
||||||
|
nodes = self.filterNodes(nodes)
|
||||||
|
for n in nodes:
|
||||||
|
n.clearData()
|
||||||
|
|
||||||
@Slot(Attribute, Attribute)
|
@Slot(Attribute, Attribute)
|
||||||
def addEdge(self, src, dst):
|
def addEdge(self, src, dst):
|
||||||
|
@ -587,45 +629,6 @@ class UIGraph(QObject):
|
||||||
""" Reset 'attribute' to its default value """
|
""" Reset 'attribute' to its default value """
|
||||||
self.push(commands.SetAttributeCommand(self._graph, attribute, attribute.defaultValue()))
|
self.push(commands.SetAttributeCommand(self._graph, attribute, attribute.defaultValue()))
|
||||||
|
|
||||||
@Slot(Node, bool, result="QVariantList")
|
|
||||||
def duplicateNode(self, srcNode, duplicateFollowingNodes=False):
|
|
||||||
"""
|
|
||||||
Duplicate a node and optionally all the following nodes to graph leaves.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
srcNode (Node): node to start the duplication from
|
|
||||||
duplicateFollowingNodes (bool): whether to duplicate all the following nodes to graph leaves
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
[Nodes]: the list of duplicated nodes
|
|
||||||
"""
|
|
||||||
if duplicateFollowingNodes: title = "Duplicate Nodes from {}"
|
|
||||||
elif self.nodeSelection(srcNode): title = "Duplicate selected nodes"
|
|
||||||
else: title = "Duplicate {}"
|
|
||||||
# enable updates between duplication and layout to get correct depths during layout
|
|
||||||
with self.groupedGraphModification(title.format(srcNode.name), disableUpdates=False):
|
|
||||||
# disable graph updates during duplication
|
|
||||||
with self.groupedGraphModification("Node duplication", disableUpdates=True):
|
|
||||||
if self.nodeSelection(srcNode) and not duplicateFollowingNodes:
|
|
||||||
command = commands.DuplicateNodeListCommand(self._graph, self._selectedNodes)
|
|
||||||
else:
|
|
||||||
command = commands.DuplicateNodeCommand(self._graph, srcNode, duplicateFollowingNodes)
|
|
||||||
duplicates = self.push(command)
|
|
||||||
# move nodes below the bounding box formed by the duplicated node(s)
|
|
||||||
bbox = self._layout.boundingBox(duplicates)
|
|
||||||
for n in duplicates:
|
|
||||||
self.moveNode(n, Position(n.x, bbox[3] + self.layout.gridSpacing + n.y))
|
|
||||||
|
|
||||||
return duplicates
|
|
||||||
|
|
||||||
@Slot(QObject)
|
|
||||||
def clearData(self, node):
|
|
||||||
if self.nodeSelection(node):
|
|
||||||
for n in self._selectedNodes:
|
|
||||||
n.clearData()
|
|
||||||
return
|
|
||||||
node.clearData()
|
|
||||||
|
|
||||||
@Slot(CompatibilityNode, result=Node)
|
@Slot(CompatibilityNode, result=Node)
|
||||||
def upgradeNode(self, node):
|
def upgradeNode(self, node):
|
||||||
""" Upgrade a CompatibilityNode. """
|
""" Upgrade a CompatibilityNode. """
|
||||||
|
|
|
@ -55,11 +55,19 @@ Item {
|
||||||
function selectNode(node)
|
function selectNode(node)
|
||||||
{
|
{
|
||||||
uigraph.selectedNode = node
|
uigraph.selectedNode = node
|
||||||
|
if (!uigraph.selectedNodes.contains(node) && node !== null) {
|
||||||
|
uigraph.selectedNodes.append(node)
|
||||||
|
uigraph.selectedNodesChanged()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Duplicate a node and optionnally all the following ones
|
/// Duplicate a node and optionnally all the following ones
|
||||||
function duplicateNode(node, duplicateFollowingNodes) {
|
function duplicateNode(node, duplicateFollowingNodes) {
|
||||||
var nodes = uigraph.duplicateNode(node, duplicateFollowingNodes)
|
if (duplicateFollowingNodes) {
|
||||||
|
var nodes = uigraph.duplicateNodesFrom(node)
|
||||||
|
} else {
|
||||||
|
var nodes = uigraph.duplicateNodes(uigraph.selectedNodes)
|
||||||
|
}
|
||||||
selectNode(nodes[0])
|
selectNode(nodes[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +79,7 @@ Item {
|
||||||
if(event.modifiers == Qt.AltModifier)
|
if(event.modifiers == Qt.AltModifier)
|
||||||
uigraph.removeNodesFrom(uigraph.selectedNode)
|
uigraph.removeNodesFrom(uigraph.selectedNode)
|
||||||
else
|
else
|
||||||
uigraph.removeNode(uigraph.selectedNode)
|
uigraph.removeNodes(uigraph.selectedNodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
@ -288,6 +296,7 @@ Item {
|
||||||
property bool canComputeNode: currentNode != null && uigraph.graph.canCompute(currentNode)
|
property bool canComputeNode: currentNode != null && uigraph.graph.canCompute(currentNode)
|
||||||
//canSubmitOrCompute: return int n : 0 >= n <= 3 | n=0 cannot submit or compute | n=1 can compute | n=2 can submit | n=3 can compute & submit
|
//canSubmitOrCompute: return int n : 0 >= n <= 3 | n=0 cannot submit or compute | n=1 can compute | n=2 can submit | n=3 can compute & submit
|
||||||
property int canSubmitOrCompute: currentNode != null && uigraph.graph.canSubmitOrCompute(currentNode)
|
property int canSubmitOrCompute: currentNode != null && uigraph.graph.canSubmitOrCompute(currentNode)
|
||||||
|
width: 220
|
||||||
onClosed: currentNode = null
|
onClosed: currentNode = null
|
||||||
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
|
@ -324,7 +333,7 @@ Item {
|
||||||
}
|
}
|
||||||
MenuSeparator {}
|
MenuSeparator {}
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: "Duplicate Node" + (duplicateFollowingButton.hovered ? "s From Here" : uigraph.nodeSelection(nodeMenu.currentNode) ? "s" : "")
|
text: "Duplicate Node(s)" + (duplicateFollowingButton.hovered ? " From Here" : "")
|
||||||
enabled: true
|
enabled: true
|
||||||
onTriggered: duplicateNode(nodeMenu.currentNode, false)
|
onTriggered: duplicateNode(nodeMenu.currentNode, false)
|
||||||
MaterialToolButton {
|
MaterialToolButton {
|
||||||
|
@ -339,9 +348,9 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: "Remove Node" + (removeFollowingButton.hovered ? "s From Here" : uigraph.nodeSelection(nodeMenu.currentNode) ? "s" : "")
|
text: "Remove Node(s)" + (removeFollowingButton.hovered ? " From Here" : "")
|
||||||
enabled: nodeMenu.currentNode ? !nodeMenu.currentNode.locked : false
|
enabled: nodeMenu.currentNode ? !nodeMenu.currentNode.locked : false
|
||||||
onTriggered: uigraph.removeNode(nodeMenu.currentNode)
|
onTriggered: uigraph.removeNodes(uigraph.selectedNodes)
|
||||||
MaterialToolButton {
|
MaterialToolButton {
|
||||||
id: removeFollowingButton
|
id: removeFollowingButton
|
||||||
height: parent.height
|
height: parent.height
|
||||||
|
@ -409,7 +418,7 @@ Item {
|
||||||
if(deleteFollowing)
|
if(deleteFollowing)
|
||||||
graph.clearDataFrom(node);
|
graph.clearDataFrom(node);
|
||||||
else
|
else
|
||||||
uigraph.clearData(node);
|
uigraph.clearData(uigraph.selectedNodes);
|
||||||
}
|
}
|
||||||
onClosed: destroy()
|
onClosed: destroy()
|
||||||
}
|
}
|
||||||
|
@ -447,9 +456,6 @@ Item {
|
||||||
uigraph.selectedNodesChanged()
|
uigraph.selectedNodesChanged()
|
||||||
selectNode(null)
|
selectNode(null)
|
||||||
return
|
return
|
||||||
} else if (!selected) {
|
|
||||||
uigraph.selectedNodes.append(node)
|
|
||||||
uigraph.selectedNodesChanged()
|
|
||||||
}
|
}
|
||||||
} else if (mouse.modifiers & Qt.AltModifier) {
|
} else if (mouse.modifiers & Qt.AltModifier) {
|
||||||
duplicateNode(node, true)
|
duplicateNode(node, true)
|
||||||
|
@ -468,7 +474,7 @@ Item {
|
||||||
|
|
||||||
onDoubleClicked: root.nodeDoubleClicked(mouse, node)
|
onDoubleClicked: root.nodeDoubleClicked(mouse, node)
|
||||||
|
|
||||||
onMoved: uigraph.moveNode(node, position)
|
onMoved: uigraph.moveNode(node, position, uigraph.selectedNodes)
|
||||||
|
|
||||||
onEntered: uigraph.hoveredNode = node
|
onEntered: uigraph.hoveredNode = node
|
||||||
onExited: uigraph.hoveredNode = null
|
onExited: uigraph.hoveredNode = null
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue