Multithreading PyQt applications with QThreadPool

Last updated on:2 years ago

A common problem when building Python GUI applications is “locking up” of the interface when attempting to perform long-running background tasks.

MultiThreading

The solution is simple: get your work out of the GUI thread (and into another thread). PyQt (via Qt) provides an straightforward interface to do exactly that.

If you take a step back and think about what you want to happen in your application, it can probably be summed up with “stuff to happen at the same time as other stuff happens”.

Threads share the same memory space, so are quick to start up and consume minimal resources. Processes use separate memory space (and an entirely separate Python interpreter). This side-steps any potential problems with the GIL, but at the cost of slower start-up times, larger memory overhead and complexity in sending/receiving data.

QRunnable and QThreadPool. The former is the container for the work you want to perform, while the latter is the method by which you pass that work to alternate threads.

Code

Worker setting up:

class WorkerSignals(QObject):
    
    # finished = pyqtSignal()
    error = pyqtSignal(tuple)
    result = pyqtSignal(object)
    progress = pyqtSignal(int)
    

class Worker(QThread):
    # Pass data into the exicution function
    def __init__(self, fn, *args, **kwargs):
        super(Worker, self).__init__()
        self.fn = fn
        self.args = args
        self.kwargs = kwargs
        self.signals = WorkerSignals()

    @pyqtSlot()
    def run(self):
        try:
            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(result)

Start thread:

self.ThreadTrain = Worker(self.doTrain)
self.ThreadTrain.start()

Terminate thread:

self.ThreadTrain.terminate()

Reference

Multithreading PyQt5 applications with QThreadPool