mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-05-21 13:06:28 +02:00
[core/ui] add a locked property on nodes
Add locked property on core nodes and update UI in the same commit because both parts are very dependent for this change.
This commit is contained in:
parent
8a9499a711
commit
ff7f8b3e36
5 changed files with 69 additions and 118 deletions
|
@ -873,7 +873,7 @@ class Graph(BaseObject):
|
||||||
flowEdges.append(link)
|
flowEdges.append(link)
|
||||||
return flowEdges
|
return flowEdges
|
||||||
|
|
||||||
def nodesFromNode(self, startNode, filterTypes=None):
|
def nodesFromNode(self, startNode, filterTypes=None, reverse=True):
|
||||||
"""
|
"""
|
||||||
Return the node chain from startNode to the graph leaves.
|
Return the node chain from startNode to the graph leaves.
|
||||||
|
|
||||||
|
@ -881,6 +881,9 @@ class Graph(BaseObject):
|
||||||
startNode (Node): the node to start the visit from.
|
startNode (Node): the node to start the visit from.
|
||||||
filterTypes (str list): (optional) only return the nodes of the given types
|
filterTypes (str list): (optional) only return the nodes of the given types
|
||||||
(does not stop the visit, this is a post-process only)
|
(does not stop the visit, this is a post-process only)
|
||||||
|
reverse (bool): (optional) direction of visit.
|
||||||
|
True is for getting nodes depending on the startNode.
|
||||||
|
False is for getting nodes required for the startNode.
|
||||||
Returns:
|
Returns:
|
||||||
The list of nodes and edges, from startNode to the graph leaves following edges.
|
The list of nodes and edges, from startNode to the graph leaves following edges.
|
||||||
"""
|
"""
|
||||||
|
@ -894,22 +897,15 @@ class Graph(BaseObject):
|
||||||
|
|
||||||
visitor.discoverVertex = discoverVertex
|
visitor.discoverVertex = discoverVertex
|
||||||
visitor.examineEdge = lambda edge, graph: edges.append(edge)
|
visitor.examineEdge = lambda edge, graph: edges.append(edge)
|
||||||
self.dfs(visitor=visitor, startNodes=[startNode], reverse=True)
|
self.dfs(visitor=visitor, startNodes=[startNode], reverse=reverse)
|
||||||
return nodes, edges
|
return nodes, edges
|
||||||
|
|
||||||
@Slot(Node, result="QVariantList")
|
def nodesDependingOnNode(self, startNode, filterTypes=None):
|
||||||
def onlyNodesFromNode(self, startNode, filterType=None):
|
nodes, edges = self.nodesFromNode(startNode, filterTypes=filterTypes, reverse=True)
|
||||||
nodes = []
|
return nodes
|
||||||
edges = []
|
|
||||||
visitor = Visitor()
|
|
||||||
|
|
||||||
def discoverVertex(vertex, graph):
|
def nodesRequiredForNode(self, startNode, filterTypes=None):
|
||||||
if not filterType or vertex.nodeType == filterType:
|
nodes, edges = self.nodesFromNode(startNode, filterTypes=filterTypes, reverse=False)
|
||||||
nodes.append(vertex)
|
|
||||||
|
|
||||||
visitor.discoverVertex = discoverVertex
|
|
||||||
visitor.examineEdge = lambda edge, graph: edges.append(edge)
|
|
||||||
self.dfs(visitor=visitor, startNodes=[startNode], reverse=True)
|
|
||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
@Slot(Node, result=int)
|
@Slot(Node, result=int)
|
||||||
|
|
|
@ -462,6 +462,9 @@ class BaseNode(BaseObject):
|
||||||
self._position = position or Position()
|
self._position = position or Position()
|
||||||
self._attributes = DictModel(keyAttrName='name', parent=self)
|
self._attributes = DictModel(keyAttrName='name', parent=self)
|
||||||
self.attributesPerUid = defaultdict(set)
|
self.attributesPerUid = defaultdict(set)
|
||||||
|
self._locked = False
|
||||||
|
|
||||||
|
self.globalStatusChanged.connect(self.updateLocked)
|
||||||
|
|
||||||
def __getattr__(self, k):
|
def __getattr__(self, k):
|
||||||
try:
|
try:
|
||||||
|
@ -813,6 +816,55 @@ class BaseNode(BaseObject):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
def getLocked(self):
|
||||||
|
return self._locked
|
||||||
|
|
||||||
|
def setLocked(self, lock):
|
||||||
|
if self._locked == lock:
|
||||||
|
return
|
||||||
|
self._locked = lock
|
||||||
|
self.lockedChanged.emit()
|
||||||
|
|
||||||
|
def updateLocked(self):
|
||||||
|
currentStatus = self.getGlobalStatus()
|
||||||
|
|
||||||
|
lockedStatus = (Status.RUNNING, Status.SUBMITTED)
|
||||||
|
|
||||||
|
# Unlock required nodes if the current node changes to Error
|
||||||
|
if currentStatus == Status.ERROR:
|
||||||
|
requiredNodes = self.graph.nodesRequiredForNode(self)
|
||||||
|
for node in requiredNodes:
|
||||||
|
node.setLocked(False)
|
||||||
|
|
||||||
|
# Avoid useless travel through nodes
|
||||||
|
# For instance: when loading a scene with successful nodes
|
||||||
|
if not self._locked and currentStatus == Status.SUCCESS:
|
||||||
|
return
|
||||||
|
|
||||||
|
if currentStatus == Status.SUCCESS:
|
||||||
|
# At this moment, the node is necessarily locked because of previous if statement
|
||||||
|
requiredNodes = self.graph.nodesRequiredForNode(self)
|
||||||
|
dependentNodes = self.graph.nodesDependingOnNode(self)
|
||||||
|
stayLocked = None
|
||||||
|
|
||||||
|
# Check if at least one dependentNode is submitted or currently running
|
||||||
|
for node in dependentNodes:
|
||||||
|
if node.getGlobalStatus() in lockedStatus and node._chunks.at(0).statusNodeName == node.name:
|
||||||
|
stayLocked = True
|
||||||
|
break
|
||||||
|
if not stayLocked:
|
||||||
|
# Unlock every required node
|
||||||
|
for node in requiredNodes:
|
||||||
|
node.setLocked(False)
|
||||||
|
return
|
||||||
|
elif currentStatus in lockedStatus:
|
||||||
|
requiredNodes = self.graph.nodesRequiredForNode(self)
|
||||||
|
for node in requiredNodes:
|
||||||
|
node.setLocked(True)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.setLocked(False)
|
||||||
|
|
||||||
name = Property(str, getName, constant=True)
|
name = Property(str, getName, constant=True)
|
||||||
label = Property(str, getLabel, constant=True)
|
label = Property(str, getLabel, constant=True)
|
||||||
nodeType = Property(str, nodeType.fget, constant=True)
|
nodeType = Property(str, nodeType.fget, constant=True)
|
||||||
|
@ -834,6 +886,8 @@ class BaseNode(BaseObject):
|
||||||
globalStatusChanged = Signal()
|
globalStatusChanged = Signal()
|
||||||
globalStatus = Property(str, lambda self: self.getGlobalStatus().name, notify=globalStatusChanged)
|
globalStatus = Property(str, lambda self: self.getGlobalStatus().name, notify=globalStatusChanged)
|
||||||
isComputed = Property(bool, _isComputed, notify=globalStatusChanged)
|
isComputed = Property(bool, _isComputed, notify=globalStatusChanged)
|
||||||
|
lockedChanged = Signal()
|
||||||
|
locked = Property(bool, getLocked, setLocked, notify=lockedChanged)
|
||||||
|
|
||||||
|
|
||||||
class Node(BaseNode):
|
class Node(BaseNode):
|
||||||
|
|
|
@ -251,21 +251,7 @@ Item {
|
||||||
point2x: dst.nodeItem.x + dstAnchor.x
|
point2x: dst.nodeItem.x + dstAnchor.x
|
||||||
point2y: dst.nodeItem.y + dstAnchor.y
|
point2y: dst.nodeItem.y + dstAnchor.y
|
||||||
onPressed: {
|
onPressed: {
|
||||||
var canEdit = true
|
const canEdit = !edge.src.node.locked
|
||||||
if(_reconstruction.computing) {
|
|
||||||
if(uigraph.taskManager.nodes.contains(edge.src.node)) {
|
|
||||||
canEdit = false;
|
|
||||||
} else {
|
|
||||||
if(object.globalStatus == "SUCCESS") {
|
|
||||||
var nodes = uigraph.graph.onlyNodesFromNode(edge.src.node);
|
|
||||||
for(var i = 0; i < nodes.length; i++) {
|
|
||||||
if(["SUBMITTED", "RUNNING"].includes(nodes[i].globalStatus) && nodes[i].chunks.at(0).statusNodeName == nodes[i].name) {
|
|
||||||
canEdit = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(canEdit && event.button == Qt.RightButton)
|
if(canEdit && event.button == Qt.RightButton)
|
||||||
{
|
{
|
||||||
|
@ -325,27 +311,7 @@ Item {
|
||||||
}
|
}
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: "Remove Node" + (removeFollowingButton.hovered ? "s From Here" : "")
|
text: "Remove Node" + (removeFollowingButton.hovered ? "s From Here" : "")
|
||||||
enabled: {
|
enabled: nodeMenu.currentNode ? !nodeMenu.currentNode.locked : false
|
||||||
if(! _reconstruction.computing) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(uigraph.taskManager.nodes.contains(uigraph.selectedNode)) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
if(uigraph.selectedNode.globalStatus == "SUCCESS") {
|
|
||||||
var nodes = uigraph.graph.onlyNodesFromNode(uigraph.selectedNode);
|
|
||||||
for(var i = 0; i < nodes.length; i++) {
|
|
||||||
if(["SUBMITTED", "RUNNING"].includes(nodes[i].globalStatus) && nodes[i].chunks.at(0).statusNodeName == nodes[i].name) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
onTriggered: uigraph.removeNode(nodeMenu.currentNode)
|
onTriggered: uigraph.removeNode(nodeMenu.currentNode)
|
||||||
MaterialToolButton {
|
MaterialToolButton {
|
||||||
id: removeFollowingButton
|
id: removeFollowingButton
|
||||||
|
@ -361,26 +327,7 @@ Item {
|
||||||
MenuSeparator {}
|
MenuSeparator {}
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: "Delete Data" + (deleteFollowingButton.hovered ? " From Here" : "" ) + "..."
|
text: "Delete Data" + (deleteFollowingButton.hovered ? " From Here" : "" ) + "..."
|
||||||
enabled: {
|
enabled: nodeMenu.currentNode ? !nodeMenu.currentNode.locked : false
|
||||||
if(! _reconstruction.computing) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(uigraph.taskManager.nodes.contains(uigraph.selectedNode) || ["SUBMITTED", "RUNNING"].includes(_reconstruction.selectedNode.globalStatus)) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
if(uigraph.selectedNode.globalStatus == "SUCCESS") {
|
|
||||||
var nodes = uigraph.graph.onlyNodesFromNode(uigraph.selectedNode);
|
|
||||||
for(var i = 0; i < nodes.length; i++) {
|
|
||||||
if(["SUBMITTED", "RUNNING"].includes(nodes[i].globalStatus) && nodes[i].chunks.at(0).statusNodeName == nodes[i].name) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function showConfirmationDialog(deleteFollowing) {
|
function showConfirmationDialog(deleteFollowing) {
|
||||||
var obj = deleteDataDialog.createObject(root,
|
var obj = deleteDataDialog.createObject(root,
|
||||||
|
@ -443,26 +390,7 @@ Item {
|
||||||
|
|
||||||
node: object
|
node: object
|
||||||
width: uigraph.layout.nodeWidth
|
width: uigraph.layout.nodeWidth
|
||||||
readOnly: {
|
|
||||||
if(! uigraph.computing) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(object.globalStatus == "SUCCESS") {
|
|
||||||
var nodes = uigraph.graph.onlyNodesFromNode(object);
|
|
||||||
for(var i = 0; i < nodes.length; i++) {
|
|
||||||
if(["SUBMITTED", "RUNNING"].includes(nodes[i].globalStatus) && nodes[i].chunks.at(0).statusNodeName == nodes[i].name) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if(["SUBMITTED", "RUNNING"].includes(object.globalStatus)) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
selected: uigraph.selectedNode === node
|
selected: uigraph.selectedNode === node
|
||||||
hovered: uigraph.hoveredNode === node
|
hovered: uigraph.hoveredNode === node
|
||||||
onSelectedChanged: if(selected) forceActiveFocus()
|
onSelectedChanged: if(selected) forceActiveFocus()
|
||||||
|
|
|
@ -16,7 +16,7 @@ Item {
|
||||||
/// The underlying Node object
|
/// The underlying Node object
|
||||||
property variant node
|
property variant node
|
||||||
/// Whether the node can be modified
|
/// Whether the node can be modified
|
||||||
property bool readOnly: false
|
property bool readOnly: node.locked
|
||||||
/// Whether the node is in compatibility mode
|
/// Whether the node is in compatibility mode
|
||||||
readonly property bool isCompatibilityNode: node.hasOwnProperty("compatibilityIssue")
|
readonly property bool isCompatibilityNode: node.hasOwnProperty("compatibilityIssue")
|
||||||
/// Mouse related states
|
/// Mouse related states
|
||||||
|
|
|
@ -167,13 +167,10 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
_reconstruction.execute(node);
|
_reconstruction.execute(node);
|
||||||
|
|
||||||
nodeEditor.updateNodeStatus()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function submit(node) {
|
function submit(node) {
|
||||||
_reconstruction.submit(node);
|
_reconstruction.submit(node);
|
||||||
nodeEditor.updateNodeStatus()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -729,32 +726,8 @@ ApplicationWindow {
|
||||||
node: _reconstruction.selectedNode
|
node: _reconstruction.selectedNode
|
||||||
property bool computing: _reconstruction.computing
|
property bool computing: _reconstruction.computing
|
||||||
// Make NodeEditor readOnly when computing
|
// Make NodeEditor readOnly when computing
|
||||||
readOnly: false
|
readOnly: node.locked
|
||||||
onNodeChanged: { updateNodeStatus() }
|
|
||||||
onComputingChanged: { updateNodeStatus() }
|
|
||||||
|
|
||||||
function updateNodeStatus() {
|
|
||||||
if(! _reconstruction.computing) {
|
|
||||||
readOnly = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(node.globalStatus === "SUCCESS") {
|
|
||||||
var nodes = _reconstruction.graph.onlyNodesFromNode(node);
|
|
||||||
for(var i = 0; i < nodes.length; i++) {
|
|
||||||
if(["SUBMITTED", "RUNNING"].includes(nodes[i].globalStatus) && nodes[i].chunks.at(0).statusNodeName == nodes[i].name) {
|
|
||||||
readOnly = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
readOnly = false;
|
|
||||||
} else if(["SUBMITTED", "RUNNING"].includes(node.globalStatus)) {
|
|
||||||
readOnly = true;
|
|
||||||
} else {
|
|
||||||
readOnly = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
onAttributeDoubleClicked: workspaceView.viewAttribute(attribute, mouse)
|
onAttributeDoubleClicked: workspaceView.viewAttribute(attribute, mouse)
|
||||||
onUpgradeRequest: {
|
onUpgradeRequest: {
|
||||||
var n = _reconstruction.upgradeNode(node);
|
var n = _reconstruction.upgradeNode(node);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue