mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-04-29 10:17:27 +02:00
[ui] GraphEditor: use entire node selection when handling '_ from here' operations
This commit is contained in:
parent
e8a5491178
commit
fdfabf0066
3 changed files with 56 additions and 39 deletions
|
@ -1132,11 +1132,6 @@ class Graph(BaseObject):
|
||||||
for node in self.nodes:
|
for node in self.nodes:
|
||||||
node.clearSubmittedChunks()
|
node.clearSubmittedChunks()
|
||||||
|
|
||||||
@Slot(Node)
|
|
||||||
def clearDataFrom(self, startNode):
|
|
||||||
for node in self.dfsOnDiscover(startNodes=[startNode], reverse=True, dependenciesOnly=True)[0]:
|
|
||||||
node.clearData()
|
|
||||||
|
|
||||||
def iterChunksByStatus(self, status):
|
def iterChunksByStatus(self, status):
|
||||||
""" Iterate over NodeChunks with the given status """
|
""" Iterate over NodeChunks with the given status """
|
||||||
for node in self.nodes:
|
for node in self.nodes:
|
||||||
|
|
|
@ -541,21 +541,19 @@ class UIGraph(QObject):
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
self.push(commands.RemoveNodeCommand(self._graph, node))
|
self.push(commands.RemoveNodeCommand(self._graph, node))
|
||||||
|
|
||||||
@Slot(Node)
|
@Slot(QObject)
|
||||||
def removeNodesFrom(self, startNode):
|
def removeNodesFrom(self, nodes):
|
||||||
"""
|
"""
|
||||||
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.
|
||||||
"""
|
"""
|
||||||
if not startNode:
|
with self.groupedGraphModification("Remove Nodes From Selected Nodes"):
|
||||||
return
|
nodesToRemove, _ = self._graph.dfsOnDiscover(startNodes=nodes, reverse=True, dependenciesOnly=True)
|
||||||
with self.groupedGraphModification("Remove Nodes from {}".format(startNode.name)):
|
|
||||||
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.
|
||||||
self.removeNodes(list(reversed(nodes)))
|
self.removeNodes(list(reversed(nodesToRemove)))
|
||||||
|
|
||||||
@Slot(QObject, result="QVariantList")
|
@Slot(QObject, result="QVariantList")
|
||||||
def duplicateNodes(self, nodes):
|
def duplicateNodes(self, nodes):
|
||||||
|
@ -579,29 +577,38 @@ class UIGraph(QObject):
|
||||||
self.moveNode(n, Position(n.x, bbox[3] + self.layout.gridSpacing + n.y))
|
self.moveNode(n, Position(n.x, bbox[3] + self.layout.gridSpacing + n.y))
|
||||||
return duplicates
|
return duplicates
|
||||||
|
|
||||||
@Slot(Node, result="QVariantList")
|
@Slot(QObject, result="QVariantList")
|
||||||
def duplicateNodesFrom(self, startNode):
|
def duplicateNodesFrom(self, nodes):
|
||||||
"""
|
"""
|
||||||
Duplicate all nodes starting from 'startNode' to graph leaves.
|
Duplicate all nodes starting from 'nodes' to graph leaves.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
startNode (Node): the node to start from.
|
nodes (list[Node]): the nodes to start from.
|
||||||
Returns:
|
Returns:
|
||||||
list[Node]: the list of duplicated nodes
|
list[Node]: the list of duplicated nodes
|
||||||
"""
|
"""
|
||||||
if not startNode:
|
with self.groupedGraphModification("Duplicate Nodes From Selected Nodes"):
|
||||||
return
|
nodesToDuplicate, _ = self._graph.dfsOnDiscover(startNodes=nodes, reverse=True, dependenciesOnly=True)
|
||||||
with self.groupedGraphModification("Duplicate Nodes from {}".format(startNode.name)):
|
duplicates = self.duplicateNodes(nodesToDuplicate)
|
||||||
nodes, _ = self._graph.dfsOnDiscover(startNodes=[startNode], reverse=True, dependenciesOnly=True)
|
|
||||||
duplicates = self.duplicateNodes(nodes)
|
|
||||||
return duplicates
|
return duplicates
|
||||||
|
|
||||||
@Slot(QObject)
|
@Slot(QObject)
|
||||||
def clearData(self, nodes):
|
def clearData(self, nodes):
|
||||||
|
""" Clear data from 'nodes'. """
|
||||||
nodes = self.filterNodes(nodes)
|
nodes = self.filterNodes(nodes)
|
||||||
for n in nodes:
|
for n in nodes:
|
||||||
n.clearData()
|
n.clearData()
|
||||||
|
|
||||||
|
@Slot(QObject)
|
||||||
|
def clearDataFrom(self, nodes):
|
||||||
|
"""
|
||||||
|
Clear data from all nodes starting from 'nodes' to graph leaves.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
nodes (list[Node]): the nodes to start from.
|
||||||
|
"""
|
||||||
|
self.clearData(self._graph.dfsOnDiscover(startNodes=nodes, reverse=True, dependenciesOnly=True)[0])
|
||||||
|
|
||||||
@Slot(Attribute, Attribute)
|
@Slot(Attribute, Attribute)
|
||||||
def addEdge(self, src, dst):
|
def addEdge(self, src, dst):
|
||||||
if isinstance(dst, ListAttribute) and not isinstance(src, ListAttribute):
|
if isinstance(dst, ListAttribute) and not isinstance(src, ListAttribute):
|
||||||
|
@ -664,22 +671,37 @@ class UIGraph(QObject):
|
||||||
|
|
||||||
@Slot(Node)
|
@Slot(Node)
|
||||||
def appendSelection(self, node):
|
def appendSelection(self, node):
|
||||||
|
""" Append 'node' to the selection if it is not already part of the selection. """
|
||||||
if not self._selectedNodes.contains(node):
|
if not self._selectedNodes.contains(node):
|
||||||
self._selectedNodes.append(node)
|
self._selectedNodes.append(node)
|
||||||
|
|
||||||
|
@Slot("QVariantList")
|
||||||
|
def selectNodes(self, nodes):
|
||||||
|
""" Append 'nodes' to the selection. """
|
||||||
|
for node in nodes:
|
||||||
|
self.appendSelection(node)
|
||||||
|
self.selectedNodesChanged.emit()
|
||||||
|
|
||||||
@Slot(Node)
|
@Slot(Node)
|
||||||
def selectFollowing(self, node):
|
def selectFollowing(self, node):
|
||||||
for n in self._graph.dfsOnDiscover(startNodes=[node], reverse=True, dependenciesOnly=True)[0]:
|
""" Select all the nodes the depend on 'node'. """
|
||||||
self.appendSelection(n)
|
self.selectNodes(self._graph.dfsOnDiscover(startNodes=[node], reverse=True, dependenciesOnly=True)[0])
|
||||||
self.selectedNodesChanged.emit()
|
|
||||||
|
|
||||||
@Slot(QObject, QObject)
|
@Slot(QObject, QObject)
|
||||||
def boxSelect(self, selection, draggable):
|
def boxSelect(self, selection, draggable):
|
||||||
|
"""
|
||||||
|
Select nodes that overlap with 'selection'.
|
||||||
|
Takes into account the zoom and position of 'draggable'.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
selection: the rectangle selection widget.
|
||||||
|
draggable: the parent widget that has position and scale data.
|
||||||
|
"""
|
||||||
x = selection.x() - draggable.x()
|
x = selection.x() - draggable.x()
|
||||||
y = selection.y() - draggable.y()
|
y = selection.y() - draggable.y()
|
||||||
otherX = x + selection.width()
|
otherX = x + selection.width()
|
||||||
otherY = y + selection.height()
|
otherY = y + selection.height()
|
||||||
x, y, otherX, otherY = [ i / j for i, j in zip([x, y, otherX, otherY], [draggable.scale()] * 4) ]
|
x, y, otherX, otherY = [ i / draggable.scale() for i in [x, y, otherX, otherY] ]
|
||||||
if x == otherX or y == otherY:
|
if x == otherX or y == otherY:
|
||||||
return
|
return
|
||||||
for n in self._graph.nodes:
|
for n in self._graph.nodes:
|
||||||
|
@ -692,7 +714,7 @@ class UIGraph(QObject):
|
||||||
@Slot()
|
@Slot()
|
||||||
def clearNodeSelection(self):
|
def clearNodeSelection(self):
|
||||||
""" Clear all node selection. """
|
""" Clear all node selection. """
|
||||||
self.selectedNode = None
|
self._selectedNode = None
|
||||||
self._selectedNodes.clear()
|
self._selectedNodes.clear()
|
||||||
self.selectedNodeChanged.emit()
|
self.selectedNodeChanged.emit()
|
||||||
self.selectedNodesChanged.emit()
|
self.selectedNodesChanged.emit()
|
||||||
|
@ -718,11 +740,11 @@ class UIGraph(QObject):
|
||||||
lockedChanged = Signal()
|
lockedChanged = Signal()
|
||||||
|
|
||||||
selectedNodeChanged = Signal()
|
selectedNodeChanged = Signal()
|
||||||
# Currently selected node
|
# Current main selected node
|
||||||
selectedNode = makeProperty(QObject, "_selectedNode", selectedNodeChanged, resetOnDestroy=True)
|
selectedNode = makeProperty(QObject, "_selectedNode", selectedNodeChanged, resetOnDestroy=True)
|
||||||
|
|
||||||
selectedNodesChanged = Signal()
|
selectedNodesChanged = Signal()
|
||||||
# Currently selected nodes to drag
|
# Currently selected nodes
|
||||||
selectedNodes = makeProperty(QObject, "_selectedNodes", selectedNodesChanged, resetOnDestroy=True)
|
selectedNodes = makeProperty(QObject, "_selectedNodes", selectedNodesChanged, resetOnDestroy=True)
|
||||||
|
|
||||||
hoveredNodeChanged = Signal()
|
hoveredNodeChanged = Signal()
|
||||||
|
|
|
@ -62,15 +62,15 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Duplicate a node and optionnally all the following ones
|
/// Duplicate a node and optionnally all the following ones
|
||||||
function duplicateNode(node, duplicateFollowingNodes) {
|
function duplicateNode(duplicateFollowingNodes) {
|
||||||
if (duplicateFollowingNodes) {
|
if (duplicateFollowingNodes) {
|
||||||
var nodes = uigraph.duplicateNodesFrom(node)
|
var nodes = uigraph.duplicateNodesFrom(uigraph.selectedNodes)
|
||||||
} else {
|
} else {
|
||||||
var nodes = uigraph.duplicateNodes(uigraph.selectedNodes)
|
var nodes = uigraph.duplicateNodes(uigraph.selectedNodes)
|
||||||
}
|
}
|
||||||
uigraph.clearNodeSelection()
|
uigraph.clearNodeSelection()
|
||||||
selectNode(nodes[0])
|
uigraph.selectedNode = nodes[0]
|
||||||
uigraph.selectFollowing(nodes[0])
|
uigraph.selectNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -79,11 +79,11 @@ Item {
|
||||||
fit()
|
fit()
|
||||||
if(event.key === Qt.Key_Delete)
|
if(event.key === Qt.Key_Delete)
|
||||||
if(event.modifiers == Qt.AltModifier)
|
if(event.modifiers == Qt.AltModifier)
|
||||||
uigraph.removeNodesFrom(uigraph.selectedNode)
|
uigraph.removeNodesFrom(uigraph.selectedNodes)
|
||||||
else
|
else
|
||||||
uigraph.removeNodes(uigraph.selectedNodes)
|
uigraph.removeNodes(uigraph.selectedNodes)
|
||||||
if(event.key === Qt.Key_D)
|
if(event.key === Qt.Key_D)
|
||||||
duplicateNode(uigraph.selectedNode, event.modifiers == Qt.AltModifier)
|
duplicateNode(event.modifiers == Qt.AltModifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
@ -338,14 +338,14 @@ Item {
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: "Duplicate Node(s)" + (duplicateFollowingButton.hovered ? " From Here" : "")
|
text: "Duplicate Node(s)" + (duplicateFollowingButton.hovered ? " From Here" : "")
|
||||||
enabled: true
|
enabled: true
|
||||||
onTriggered: duplicateNode(nodeMenu.currentNode, false)
|
onTriggered: duplicateNode(false)
|
||||||
MaterialToolButton {
|
MaterialToolButton {
|
||||||
id: duplicateFollowingButton
|
id: duplicateFollowingButton
|
||||||
height: parent.height
|
height: parent.height
|
||||||
anchors { right: parent.right; rightMargin: parent.padding }
|
anchors { right: parent.right; rightMargin: parent.padding }
|
||||||
text: MaterialIcons.fast_forward
|
text: MaterialIcons.fast_forward
|
||||||
onClicked: {
|
onClicked: {
|
||||||
duplicateNode(nodeMenu.currentNode, true);
|
duplicateNode(true);
|
||||||
nodeMenu.close();
|
nodeMenu.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -360,7 +360,7 @@ Item {
|
||||||
anchors { right: parent.right; rightMargin: parent.padding }
|
anchors { right: parent.right; rightMargin: parent.padding }
|
||||||
text: MaterialIcons.fast_forward
|
text: MaterialIcons.fast_forward
|
||||||
onClicked: {
|
onClicked: {
|
||||||
uigraph.removeNodesFrom(nodeMenu.currentNode);
|
uigraph.removeNodesFrom(uigraph.selectedNodes);
|
||||||
nodeMenu.close();
|
nodeMenu.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -419,7 +419,7 @@ Item {
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
if(deleteFollowing)
|
if(deleteFollowing)
|
||||||
graph.clearDataFrom(node);
|
uigraph.clearDataFrom(uigraph.selectedNodes);
|
||||||
else
|
else
|
||||||
uigraph.clearData(uigraph.selectedNodes);
|
uigraph.clearData(uigraph.selectedNodes);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue