From c00db25c236d55a824c34dc2091e0acdb1394686 Mon Sep 17 00:00:00 2001 From: Lee Geertsen Date: Mon, 12 Aug 2019 18:41:15 +0200 Subject: [PATCH] Don't lock graph and node editor while computing nodes Add possibilty to, while computing nodes: add more nodes to the task manager, edit, duplicate and remove nodes without breaking the tasks that are submitted --- meshroom/common/qt.py | 1 + meshroom/core/graph.py | 15 +++++ meshroom/core/node.py | 9 ++- meshroom/ui/qml/GraphEditor/GraphEditor.qml | 61 ++++++++++++++++----- meshroom/ui/qml/main.qml | 36 +++++++----- 5 files changed, 94 insertions(+), 28 deletions(-) diff --git a/meshroom/common/qt.py b/meshroom/common/qt.py index cb1087d5..7743f49e 100644 --- a/meshroom/common/qt.py +++ b/meshroom/common/qt.py @@ -216,6 +216,7 @@ class QObjectListModel(QtCore.QAbstractListModel): def reset(self, objects): self.setObjectList(objects) + @QtCore.Slot(QtCore.QObject, result=bool) def contains(self, obj): """ Returns true if the list contains an occurrence of object; otherwise returns false. diff --git a/meshroom/core/graph.py b/meshroom/core/graph.py index b72a3eca..8b39160e 100644 --- a/meshroom/core/graph.py +++ b/meshroom/core/graph.py @@ -893,6 +893,21 @@ class Graph(BaseObject): self.dfs(visitor=visitor, startNodes=[startNode], reverse=True) return nodes, edges + @Slot(Node, result="QVariantList") + def onlyNodesFromNode(self, startNode, filterType=None): + nodes = [] + edges = [] + visitor = Visitor() + + def discoverVertex(vertex, graph): + if not filterType or vertex.nodeType == filterType: + nodes.append(vertex) + + visitor.discoverVertex = discoverVertex + visitor.examineEdge = lambda edge, graph: edges.append(edge) + self.dfs(visitor=visitor, startNodes=[startNode], reverse=True) + return nodes + def _applyExpr(self): with GraphModification(self): for node in self._nodes: diff --git a/meshroom/core/node.py b/meshroom/core/node.py index d0cae199..26ca5d04 100644 --- a/meshroom/core/node.py +++ b/meshroom/core/node.py @@ -225,6 +225,9 @@ class NodeChunk(BaseObject): if newStatus.value <= self.status.status.value: print('WARNING: downgrade status on node "{}" from {} to {}'.format(self.name, self.status.status, newStatus)) + + if(newStatus == Status.SUBMITTED): + self.status = StatusData(self.node.name, self.node.nodeType, self.node.packageName, self.node.packageVersion) if execMode is not None: self.status.execMode = execMode self.execModeNameChanged.emit() @@ -282,7 +285,8 @@ class NodeChunk(BaseObject): try: self.node.nodeDesc.processChunk(self) except Exception as e: - self.upgradeStatusTo(Status.ERROR) + if self.status.status != Status.STOPPED: + self.upgradeStatusTo(Status.ERROR) raise except (KeyboardInterrupt, SystemError, GeneratorExit) as e: self.upgradeStatusTo(Status.STOPPED) @@ -314,6 +318,9 @@ class NodeChunk(BaseObject): logFile = Property(str, logFile.fget, notify=nodeFolderChanged) statisticsFile = Property(str, statisticsFile.fget, notify=nodeFolderChanged) + nodeName = Property(str, lambda self: self.node.name, constant=True) + statusNodeName = Property(str, lambda self: self.status.nodeName, constant=True) + # simple structure for storing node position Position = namedtuple("Position", ["x", "y"]) diff --git a/meshroom/ui/qml/GraphEditor/GraphEditor.qml b/meshroom/ui/qml/GraphEditor/GraphEditor.qml index b87fc085..82a73bd5 100755 --- a/meshroom/ui/qml/GraphEditor/GraphEditor.qml +++ b/meshroom/ui/qml/GraphEditor/GraphEditor.qml @@ -60,8 +60,6 @@ Item { /// Duplicate a node and optionnally all the following ones function duplicateNode(node, duplicateFollowingNodes) { - if(root.readOnly) - return; var nodes = uigraph.duplicateNode(node, duplicateFollowingNodes) selectNode(nodes[0]) } @@ -120,13 +118,9 @@ Item { onClicked: { if(mouse.button == Qt.RightButton) { - if(readOnly) - lockedMenu.popup(); - else { - // store mouse click position in 'draggable' coordinates as new node spawn position - newNodeMenu.spawnPosition = mouseArea.mapToItem(draggable, mouse.x, mouse.y); - newNodeMenu.popup(); - } + // store mouse click position in 'draggable' coordinates as new node spawn position + newNodeMenu.spawnPosition = mouseArea.mapToItem(draggable, mouse.x, mouse.y); + newNodeMenu.popup(); } } @@ -281,12 +275,12 @@ Item { MenuItem { text: "Compute" - enabled: !uigraph.computing && !root.readOnly && nodeMenu.canComputeNode + enabled: nodeMenu.canComputeNode onTriggered: computeRequest(nodeMenu.currentNode) } MenuItem { text: "Submit" - enabled: !uigraph.computing && !root.readOnly && nodeMenu.canComputeNode + enabled: nodeMenu.canComputeNode visible: uigraph.canSubmit height: visible ? implicitHeight : 0 onTriggered: submitRequest(nodeMenu.currentNode) @@ -298,7 +292,7 @@ Item { MenuSeparator {} MenuItem { text: "Duplicate Node" + (duplicateFollowingButton.hovered ? "s From Here" : "") - enabled: !root.readOnly + enabled: true onTriggered: duplicateNode(nodeMenu.currentNode, false) MaterialToolButton { id: duplicateFollowingButton @@ -313,7 +307,27 @@ Item { } MenuItem { text: "Remove Node" + (removeFollowingButton.hovered ? "s From Here" : "") - enabled: !root.readOnly + enabled: { + 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) MaterialToolButton { id: removeFollowingButton @@ -329,7 +343,26 @@ Item { MenuSeparator {} MenuItem { text: "Delete Data" + (deleteFollowingButton.hovered ? " From Here" : "" ) + "..." - enabled: !root.readOnly + enabled: { + 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; + } function showConfirmationDialog(deleteFollowing) { var obj = deleteDataDialog.createObject(root, diff --git a/meshroom/ui/qml/main.qml b/meshroom/ui/qml/main.qml index 213320d9..32b75178 100755 --- a/meshroom/ui/qml/main.qml +++ b/meshroom/ui/qml/main.qml @@ -21,7 +21,8 @@ ApplicationWindow { visible: true /// Whether graph is currently locked and therefore read-only - readonly property bool graphLocked: _reconstruction.computing && GraphEditorSettings.lockOnCompute + readonly property bool graphLocked: _reconstruction.computing + title: { var t = _reconstruction.graph.filepath || "Untitled" @@ -568,17 +569,6 @@ ApplicationWindow { enabled: !_reconstruction.computingLocally onTriggered: _reconstruction.forceNodesStatusUpdate() } - Menu { - title: "Advanced" - MenuItem { - text: "Lock on Compute" - ToolTip.text: "Lock Graph when computing. This should only be disabled for advanced usage." - ToolTip.visible: hovered - checkable: true - checked: GraphEditorSettings.lockOnCompute - onClicked: GraphEditorSettings.lockOnCompute = !GraphEditorSettings.lockOnCompute - } - } } } } @@ -646,7 +636,27 @@ ApplicationWindow { width: Math.round(parent.width * 0.3) node: _reconstruction.selectedNode // Make NodeEditor readOnly when computing - readOnly: graphLocked +// readOnly: graphLocked + readOnly: { + if(! _reconstruction.computing) { + return false; + } + + if(_reconstruction.taskManager.nodes.contains(_reconstruction.selectedNode)) { + return true; + } else { + if(_reconstruction.selectedNode.globalStatus == "SUCCESS") { + var nodes = _reconstruction.graph.onlyNodesFromNode(_reconstruction.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 true; + } + } + } + } + + return false; + } onAttributeDoubleClicked: workspaceView.viewIn3D(attribute, mouse) onUpgradeRequest: { var n = _reconstruction.upgradeNode(node);