From 34a90563c8f4b8e959fc7649ba6bad5c47f7786c Mon Sep 17 00:00:00 2001 From: Lee Geertsen Date: Fri, 6 Sep 2019 11:02:45 +0200 Subject: [PATCH] Update TaskManager when node is removed --- meshroom/common/qt.py | 4 +- meshroom/core/taskManager.py | 77 +++++++++++++++++++++++++++++------- meshroom/ui/graph.py | 1 + 3 files changed, 66 insertions(+), 16 deletions(-) diff --git a/meshroom/common/qt.py b/meshroom/common/qt.py index 7743f49e..c8f02f8c 100644 --- a/meshroom/common/qt.py +++ b/meshroom/common/qt.py @@ -1,5 +1,5 @@ from PySide2 import QtCore - +import shiboken2 class QObjectListModel(QtCore.QAbstractListModel): """ @@ -272,7 +272,7 @@ class QObjectListModel(QtCore.QAbstractListModel): def _dereferenceItem(self, item): # Ask for object deletion if parented to the model - if item.parent() == self: + if shiboken2.isValid(item) and item.parent() == self: # delay deletion until the next event loop # This avoids warnings when the QML engine tries to evaluate (but should not) # an object that has already been deleted diff --git a/meshroom/core/taskManager.py b/meshroom/core/taskManager.py index 6386a4a7..bfee84df 100644 --- a/meshroom/core/taskManager.py +++ b/meshroom/core/taskManager.py @@ -6,6 +6,9 @@ import meshroom from meshroom.common import BaseObject, DictModel, Property class State(Enum): + """ + State of the Thread that is computing nodes + """ IDLE = 0 RUNNING = 1 STOPPED = 2 @@ -13,6 +16,9 @@ class State(Enum): ERROR = 4 class TaskThread(Thread): + """ + A thread with a pile of nodes to compute + """ def __init__(self, manager): Thread.__init__(self, target=self.run) self._state = State.IDLE @@ -45,7 +51,7 @@ class TaskThread(Thread): if chunk.isStopped(): self._state = State.STOPPED self._manager._graph.clearSubmittedNodes() - self._manager._nodesToProcess.clear() + self._manager._nodesToProcess = [] else: logging.error("Error on node computation: {}".format(e)) @@ -60,21 +66,33 @@ class TaskThread(Thread): pass nodeD.clearSubmittedChunks() - self._manager._nodesToProcess.clear() + self._manager._nodesToProcess = [] self._state = State.DEAD self._manager._uigraph.computeStatusChanged.emit() class TaskManager(BaseObject): + """ + Manage the nodes that have to be computed locally or on a renderfarm + """ def __init__(self, parent=None): super(TaskManager, self).__init__(parent) - self._nodes = DictModel(keyAttrName='name', parent=self) - self._nodesToProcess = DictModel(keyAttrName='name', parent=self) - self._nodesExtern = DictModel(keyAttrName='name', parent=self) + self._nodes = DictModel(keyAttrName='_name', parent=self) + self._nodesToProcess = [] + self._nodesExtern = [] self._graph = None self._thread = TaskThread(self) def compute(self, graph=None, toNodes=None, uigraph=None, forceCompute=False, forceStatus=False): + """ + Nodes are to be computed locally are sent to a thread + :param graph: + :param toNodes: + :param uigraph: + :param forceCompute: + :param forceStatus: + :return: + """ self._graph = graph self._uigraph = uigraph @@ -93,7 +111,7 @@ class TaskManager(BaseObject): if not externEmpty: self._nodes.update(self._nodesExtern) else: - self._nodesExtern.clear() + self._nodesExtern = [] if forceCompute: nodes, edges = graph.dfsOnFinish(startNodes=toNodes) @@ -104,16 +122,14 @@ class TaskManager(BaseObject): if not node.isAlreadySubmittedOrFinished(): nodes.append(node) - print('Nodes to execute: ', str([n.name for n in nodes])) + logging.info('Nodes to execute: ', str([n.name for n in nodes])) for node in nodes: + node.destroyed.connect(lambda obj=None, name=node.name: self.onNodeDestroyed(obj, name)) node.beginSequence(forceCompute) - try: - self._nodes.update(nodes) - except: - print("nodes already added to Task Manager") - self._nodesToProcess.update(nodes) + self._nodes.update(nodes) + self._nodesToProcess .extend(nodes) if self._thread._state == State.IDLE: self._thread.start() @@ -121,7 +137,34 @@ class TaskManager(BaseObject): self._thread = TaskThread(self) self._thread.start() + def onNodeDestroyed(self, obj, name): + """ + Remove node from the taskmanager when it's destroyed in the graph + :param obj: + :param name: + :return: + """ + self._nodes.pop(name) + + def clear(self): + """ + Remove all the nodes from the taskmanager + :return: + """ + self._nodes.clear() + self._nodesExtern = [] + self._nodesToProcess = [] + + + def submit(self, graph=None, submitter=None, toNodes=None): + """ + Nodes are send to the renderfarm + :param graph: + :param submitter: + :param toNodes: + :return: + """ if self._thread._state in (State.IDLE, State.DEAD, State.ERROR, State.STOPPED): self._nodes.clear() @@ -134,7 +177,7 @@ class TaskManager(BaseObject): if not externEmpty: self._nodes.update(self._nodesExtern) else: - self._nodesExtern.clear() + self._nodesExtern = [] nodesToProcess, edgesToProcess = graph.dfsToProcess(startNodes=toNodes) flowEdges = graph.flowEdges(startNodes=toNodes) @@ -148,15 +191,21 @@ class TaskManager(BaseObject): res = sub.submit(nodesToProcess, edgesToProcess, graph.filepath) if res: for node in nodesToProcess: + node.destroyed.connect(lambda obj=None, name=node.name: self.onNodeDestroyed(obj, name)) node.submit() # update node status self._nodes.update(nodesToProcess) - self._nodesExtern.update(nodesToProcess) + self._nodesExtern.extend(nodesToProcess) except Exception as e: logging.error("Error on submit : {}".format(e)) nodes = Property(BaseObject, lambda self: self._nodes, constant=True) def getAlreadySubmittedChunks(nodes): + """ + Check if nodes already have been submitted + :param nodes: + :return: + """ out = [] for node in nodes: for chunk in node.chunks: diff --git a/meshroom/ui/graph.py b/meshroom/ui/graph.py index 5ebead1d..d6ec37c0 100644 --- a/meshroom/ui/graph.py +++ b/meshroom/ui/graph.py @@ -307,6 +307,7 @@ class UIGraph(QObject): if self._graph: self.clearNodeHover() self.clearNodeSelection() + self._taskManager.clear() self._graph.deleteLater() self._graph = None self._sortedDFSChunks.clear()