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
This commit is contained in:
Lee Geertsen 2019-08-12 18:41:15 +02:00 committed by Yann Lanthony
parent 51d6c18840
commit c00db25c23
No known key found for this signature in database
GPG key ID: 519FAE6DF7A70642
5 changed files with 94 additions and 28 deletions

View file

@ -216,6 +216,7 @@ class QObjectListModel(QtCore.QAbstractListModel):
def reset(self, objects): def reset(self, objects):
self.setObjectList(objects) self.setObjectList(objects)
@QtCore.Slot(QtCore.QObject, result=bool)
def contains(self, obj): def contains(self, obj):
""" Returns true if the list contains an occurrence of object; """ Returns true if the list contains an occurrence of object;
otherwise returns false. otherwise returns false.

View file

@ -893,6 +893,21 @@ class Graph(BaseObject):
self.dfs(visitor=visitor, startNodes=[startNode], reverse=True) self.dfs(visitor=visitor, startNodes=[startNode], reverse=True)
return nodes, edges 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): def _applyExpr(self):
with GraphModification(self): with GraphModification(self):
for node in self._nodes: for node in self._nodes:

View file

@ -225,6 +225,9 @@ class NodeChunk(BaseObject):
if newStatus.value <= self.status.status.value: if newStatus.value <= self.status.status.value:
print('WARNING: downgrade status on node "{}" from {} to {}'.format(self.name, self.status.status, print('WARNING: downgrade status on node "{}" from {} to {}'.format(self.name, self.status.status,
newStatus)) 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: if execMode is not None:
self.status.execMode = execMode self.status.execMode = execMode
self.execModeNameChanged.emit() self.execModeNameChanged.emit()
@ -282,6 +285,7 @@ class NodeChunk(BaseObject):
try: try:
self.node.nodeDesc.processChunk(self) self.node.nodeDesc.processChunk(self)
except Exception as e: except Exception as e:
if self.status.status != Status.STOPPED:
self.upgradeStatusTo(Status.ERROR) self.upgradeStatusTo(Status.ERROR)
raise raise
except (KeyboardInterrupt, SystemError, GeneratorExit) as e: except (KeyboardInterrupt, SystemError, GeneratorExit) as e:
@ -314,6 +318,9 @@ class NodeChunk(BaseObject):
logFile = Property(str, logFile.fget, notify=nodeFolderChanged) logFile = Property(str, logFile.fget, notify=nodeFolderChanged)
statisticsFile = Property(str, statisticsFile.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 # simple structure for storing node position
Position = namedtuple("Position", ["x", "y"]) Position = namedtuple("Position", ["x", "y"])

View file

@ -60,8 +60,6 @@ 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(node, duplicateFollowingNodes) {
if(root.readOnly)
return;
var nodes = uigraph.duplicateNode(node, duplicateFollowingNodes) var nodes = uigraph.duplicateNode(node, duplicateFollowingNodes)
selectNode(nodes[0]) selectNode(nodes[0])
} }
@ -120,15 +118,11 @@ Item {
onClicked: { onClicked: {
if(mouse.button == Qt.RightButton) if(mouse.button == Qt.RightButton)
{ {
if(readOnly)
lockedMenu.popup();
else {
// store mouse click position in 'draggable' coordinates as new node spawn position // store mouse click position in 'draggable' coordinates as new node spawn position
newNodeMenu.spawnPosition = mouseArea.mapToItem(draggable, mouse.x, mouse.y); newNodeMenu.spawnPosition = mouseArea.mapToItem(draggable, mouse.x, mouse.y);
newNodeMenu.popup(); newNodeMenu.popup();
} }
} }
}
// Contextual Menu for creating new nodes // Contextual Menu for creating new nodes
// TODO: add filtering + validate on 'Enter' // TODO: add filtering + validate on 'Enter'
@ -281,12 +275,12 @@ Item {
MenuItem { MenuItem {
text: "Compute" text: "Compute"
enabled: !uigraph.computing && !root.readOnly && nodeMenu.canComputeNode enabled: nodeMenu.canComputeNode
onTriggered: computeRequest(nodeMenu.currentNode) onTriggered: computeRequest(nodeMenu.currentNode)
} }
MenuItem { MenuItem {
text: "Submit" text: "Submit"
enabled: !uigraph.computing && !root.readOnly && nodeMenu.canComputeNode enabled: nodeMenu.canComputeNode
visible: uigraph.canSubmit visible: uigraph.canSubmit
height: visible ? implicitHeight : 0 height: visible ? implicitHeight : 0
onTriggered: submitRequest(nodeMenu.currentNode) onTriggered: submitRequest(nodeMenu.currentNode)
@ -298,7 +292,7 @@ Item {
MenuSeparator {} MenuSeparator {}
MenuItem { MenuItem {
text: "Duplicate Node" + (duplicateFollowingButton.hovered ? "s From Here" : "") text: "Duplicate Node" + (duplicateFollowingButton.hovered ? "s From Here" : "")
enabled: !root.readOnly enabled: true
onTriggered: duplicateNode(nodeMenu.currentNode, false) onTriggered: duplicateNode(nodeMenu.currentNode, false)
MaterialToolButton { MaterialToolButton {
id: duplicateFollowingButton id: duplicateFollowingButton
@ -313,7 +307,27 @@ Item {
} }
MenuItem { MenuItem {
text: "Remove Node" + (removeFollowingButton.hovered ? "s From Here" : "") 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) onTriggered: uigraph.removeNode(nodeMenu.currentNode)
MaterialToolButton { MaterialToolButton {
id: removeFollowingButton id: removeFollowingButton
@ -329,7 +343,26 @@ Item {
MenuSeparator {} MenuSeparator {}
MenuItem { MenuItem {
text: "Delete Data" + (deleteFollowingButton.hovered ? " From Here" : "" ) + "..." 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) { function showConfirmationDialog(deleteFollowing) {
var obj = deleteDataDialog.createObject(root, var obj = deleteDataDialog.createObject(root,

View file

@ -21,7 +21,8 @@ ApplicationWindow {
visible: true visible: true
/// Whether graph is currently locked and therefore read-only /// Whether graph is currently locked and therefore read-only
readonly property bool graphLocked: _reconstruction.computing && GraphEditorSettings.lockOnCompute readonly property bool graphLocked: _reconstruction.computing
title: { title: {
var t = _reconstruction.graph.filepath || "Untitled" var t = _reconstruction.graph.filepath || "Untitled"
@ -568,17 +569,6 @@ ApplicationWindow {
enabled: !_reconstruction.computingLocally enabled: !_reconstruction.computingLocally
onTriggered: _reconstruction.forceNodesStatusUpdate() 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) width: Math.round(parent.width * 0.3)
node: _reconstruction.selectedNode node: _reconstruction.selectedNode
// Make NodeEditor readOnly when computing // 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) onAttributeDoubleClicked: workspaceView.viewIn3D(attribute, mouse)
onUpgradeRequest: { onUpgradeRequest: {
var n = _reconstruction.upgradeNode(node); var n = _reconstruction.upgradeNode(node);