I have successfully translated your code into mine with some caveats:
- Basically you need to decode the stdout to the correct data type, it is not enough to ask pickle to do it
- You need to deepcopy your input data
- You can add a progress bar
import qt
import json
import pickle
import slicer
import copy
import numpy as np
from Logic import logging, utils
from slicer.ScriptedLoadableModule import *
#
# ProcessesLogic
#
class ProcessesLogic(ScriptedLoadableModuleLogic):
def __init__(self, parent = None, maximumRunningProcesses=None, completedCallback=lambda : None):
super().__init__(parent)
self.results = {}
if not maximumRunningProcesses:
cpu_cores, cpu_processors, lsystem = utils.getSystemCoresInfo()
self.maximumRunningProcesses = cpu_cores
self.completedCallback = completedCallback
self.QProcessStates = {0: 'NotRunning', 1: 'Starting', 2: 'Running',}
self.processStates = ["Pending", "Running", "Completed"]
self.processLists = {}
for processState in self.processStates:
self.processLists[processState] = []
self.canceled = False
self.ProgressDialog = slicer.util.createProgressDialog(
parent=None, value=0, maximum=100)
labelText = "Processing ..."
self.ProgressDialog.labelText = labelText
self.ProgressDialog.show()
slicer.app.processEvents()
def setMaximumRunningProcesses(self, value):
self.maximumRunningProcesses = value
def saveState(self):
state = {}
for processState in self.processStates:
state[processState] = [process.name for process in self.processLists[processState]]
self.getParameterNode().SetAttribute("state", json.dumps(state))
def state(self):
return json.loads(self.getParameterNode().GetAttribute("state"))
def addProcess(self, process):
self.processLists["Pending"].append(process)
self.ProgressDialog.maximum = len(self.processLists["Pending"])
def run(self):
while len(self.processLists["Pending"]) > 0:
if len(self.processLists["Running"]) >= self.maximumRunningProcesses:
break
process = self.processLists["Pending"].pop()
process.run(self)
self.processLists["Running"].append(process)
self.saveState()
def onProcessFinished(self,process):
self.processLists["Running"].remove(process)
self.processLists["Completed"].append(process)
self.ProgressDialog.value += 1
slicer.app.processEvents()
self.saveState()
if len(self.processLists["Running"]) == 0 and len(self.processLists["Pending"]) == 0:
for process in self.processLists["Completed"]:
k, v = process.result[0], process.result[1]
self.results[k] = v
self.ProgressDialog.close()
self.completedCallback()
if self.ProgressDialog.wasCanceled:
self.canceled = True
else:
self.run()
class Process(qt.QProcess):
"""TODO: maybe this should be a subclass of QProcess"""
def __init__(self, scriptPath):
super().__init__()
self.name = "Process"
self.processState = "Pending"
self.scriptPath = scriptPath
self.debug = False
self.logger = logging.getLogger("Dosimetry4D.qprocess")
def run(self, logic):
self.connect('stateChanged(QProcess::ProcessState)', self.onStateChanged)
self.connect('started()', self.onStarted)
finishedSlot = lambda exitCode, exitStatus : self.onFinished(logic, exitCode, exitStatus)
self.connect('finished(int,QProcess::ExitStatus)', finishedSlot)
self.start("PythonSlicer", [self.scriptPath,])
def onStateChanged(self, newState):
self.logger.info('-'*40)
self.logger.info(f'qprocess state code is: {self.state()}')
self.logger.info(f'qprocess error code is: {self.error()}')
def onStarted(self):
self.logger.info("writing")
if self.debug:
with open("/tmp/pickledInput", "w") as fp:
fp.buffer.write(self.pickledInput())
self.write(self.pickledInput())
self.closeWriteChannel()
def onFinished(self, logic, exitCode, exitStatus):
self.logger.info(f'finished, code {exitCode}, status {exitStatus}')
stdout = self.readAllStandardOutput()
self.usePickledOutput(stdout.data())
logic.onProcessFinished(self)
class ConvolutionProcess(Process):
"""This is an example of running a process to operate on model data"""
def __init__(self, scriptPath, initDict, iteration):
super().__init__(scriptPath)
self.initDict = copy.deepcopy(initDict)
self.iteration = iteration
self.name = f"Iteration {iteration}"
def pickledInput(self):
return pickle.dumps(self.initDict)
def usePickledOutput(self, pickledOutput):
output = np.frombuffer(pickledOutput, dtype=float)
#output = pickle.loads(pickledOutput)
self.result = [self.iteration, output]