简体   繁体   中英

How to postpone execution of the QRunnable until signal from previous is emitted

In order not to freeze the PyQT GUI I am using QRunnable (functions to be executed in the background) and QThreadPool. I have three functions out of which one requires results from previous two to run and I am struggling to ensure that the execution of third function starts after two previous return their respective results.

The class for QThreadPool is defined in following way:

class Worker(QtCore.QRunnable):
'''
Worker thread

Inherits from QRunnable to handler worker thread setup, signals and wrap-up.

:param callback: The function callback to run on this worker thread. Supplied args and
                 kwargs will be passed through to the runner.
:type callback: function
:param args: Arguments to pass to the callback function
:param kwargs: Keywords to pass to the callback function

'''

def __init__(self, fn, *args, **kwargs):
    super(Worker, self).__init__()

    # Store constructor arguments (re-used for processing)
    self.running = None
    self.fn = fn
    self.args = args
    self.kwargs = kwargs
    self.signals = WorkerSignals()

    # Add the callback to our kwargs
    self.kwargs['callback_progress'] = self.signals.progress
    self.kwargs['callback_data'] = self.signals.data

# @Slot()
def run(self):
    '''
    Initialise the runner function with passed args, kwargs.
    '''

    # Retrieve args/kwargs here; and fire processing using them
    try:
        self.signals.started.emit()
        self.result = self.fn(
            *self.args,
            **self.kwargs
        )
    except:
        traceback.print_exc()
        exctype, value = sys.exc_info()[:2]
        self.signals.error.emit((exctype, value, traceback.format_exc()))
    else:
        self.signals.result.emit(self.result)  # Return the result of the processing
    finally:
        self.signals.finished.emit()  # Done

and signals

class WorkerSignals(QtCore.QObject):
    '''
    Defines the signals available from a running worker thread.

    Supported signals are:

    finished
        No data

    error
        `tuple` (exctype, value, traceback.format_exc() )

    result
        `object` data returned from processing, anything

    progress
        `int` indicating % progress

    '''
    error = QtCore.Signal(tuple)
    started = QtCore.Signal()
    finished = QtCore.Signal()
    progress = QtCore.Signal(int)
    result = QtCore.Signal(object)
    data = QtCore.Signal(dict)

the function to be executed is invoked by the following function

def exe_worker_run(self, WorkerPool, function, arguments):
    Worker = thread.Worker(function, arguments)
    Worker.signals.started.connect(self.sig_thread_start)
    Worker.signals.error.connect(self.sig_thread_error)
    Worker.signals.result.connect(self.sig_thread_result)
    Worker.signals.finished.connect(self.sig_thread_finish)
    WorkerPool.start(Worker)

the signal that emits the result is connected to function

def sig_thread_result(self, result):
    for key in result.keys():
        try:
            dfrm = getattr(self, key)
            print('{} {} loaded!!!'.format(time.time(), key))
        except:
            pass

The main problem is that the result of each function is emitted after all functions finished the execution. So what I need is the solution which allows to hold the execution of a QRunnable until the result from previous QRunnable are available.

You can organize tasks into queue and execute them passing to worker one by one over signal-slot mechanism. This way you can use results from one computation to schedule next computation.

from PySide2 import QtCore, QtWidgets, QtGui

class Task:
    def __init__(self, taskId):
        self._taskId = taskId

    def taskId(self):
        return self._taskId

    def execute():
        pass

class TaskPlusOne(Task):

    def __init__(self, taskId, value):
        super().__init__(taskId)
        self._value = value

    def execute(self):
        QtCore.QThread.currentThread().sleep(3)
        return self._value + 1

class Worker(QtCore.QObject):

    complete = QtCore.Signal(int, object)

    def append(self, task):
        print("execute", task.taskId())
        res = task.execute()
        self.complete.emit(task.taskId(), res)

class Window(QtWidgets.QWidget):

    task = QtCore.Signal(object)

    def __init__(self, parent = None):
        super().__init__(parent)

        worker = Worker()
        self.task.connect(worker.append)
        worker.complete.connect(self.onComplete)
        thread = QtCore.QThread()
        worker.moveToThread(thread)
        thread.start()

        self._thread = thread
        self._worker = worker

        self._queue = [TaskPlusOne(0, 0)]
        self.executeOne()

    def executeOne(self):
        queue = self._queue
        if len(queue) == 0:
            print("task queue is empty")
            return
        self.task.emit(queue.pop(0))

    def executeAll(self):
        while len(self._queue) > 0:
            self.executeOne()

    def onComplete(self, taskId, res):
        print("onComplete", taskId)
        if res < 2:
            print("append task to queue")
            self._queue.append(TaskPlusOne(taskId + 1, res))
        self.executeOne()

    def closeEvent(self, event):
        thread = self._thread
        thread.quit()
        thread.wait()
        super().closeEvent(event)

if __name__ == "__main__":
    app = QtWidgets.QApplication([])

    widget = Window()
    widget.show()

    app.exec_()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM