Multithreading PyQt applications with QThreadPool


It doesn't actually matter which you use — when we assign self.kwargs = kwargs, we're not creating a copy of kwargs, so kwargs & self.kwargs refer to the same dictionary. Adding something to kwargs is also updating self.kwargs.

But I agree that's confusing, will move the assignment above storing it.


Thanks for such a good tutorial. Bought the book as a token of thanks :-)


Great tutorial! Thanks for taking the time. Also bought your book. As a python newb and being new to QT as well, it took me the best part of yesterday to wrap my head around it. Now I am left with one question:

In your Worker class, why do you have a @pyqtSlot() decorator in front of the worker run function? This being the worker, it emits the signals and is not the slot function. Right?


Hey thanks for the great feedback (and for buying the book!) really appreciated :)

As for the run method — you're right that emit signals from here. But the @pyqtSlot() decorator is actually there to define the run method itself as a slot. It basically registers it so it is able to be called by Qt (Python methods would usually be invisible).


Hey, what do you mean by "dynamic callbacks" — a separate callback for each job? For that I would create a signal that emits type "object" (which covers all Python types). You can then hook that up to whatever you want when you create the job.


There is no direct way to terminate a running worker/thread from outside — it basically has to end gracefully or crash (this is a Qt limitation). What you can do however is have a "kill" signal which you use to trigger the thread to shutdown.Connect the signal internally to your worker to store a "I should die" flag, and check this in your worker loop/between your tasks.


Thanks for the detailed Explanation and the Code.
1. First I added the "QApplication.processEvents()" in my Code, and it mainly did what I Need (not really, the Code ran, GUI kept Control, but only in the tab I had been in when starting the program part which better shoud be run as a thread.

Anyway it proved that iis working

2. Second tried to adapt it to my Needs, but the GUI lost Control, and the program crashed in NIRWANA

3.THIRD, I created a new Project an entered your Code with CUT and PASTE. The windoe startet with "START", the Counter proceeded - until I clicked the DANGER button - GUI lost Control completely, the program crashed in NIRWANA.

I am running python 3.6.2 (32 bit) in a Windows 10 Environment (64 bit) in the pycharm development System.
I also tried in thesde provided bypython, the same result.

Is it my configuration?
Thanks in advance


What do you mean by "in NIRWANA"? The code should work fine on your platform, did you make sure to copy the code from the very last section ("The complete code").


Excellent article - great explanation and examples. There is a problem with the code though. I looked to see if others had a similar problem but didn't see anything. Maybe the article was updated more recently.

Anyway, the progress signal is assigned to keyword args via self.signals before self.signals is instantiated:
# Add the callback to our kwargs
kwargs['progress_callback'] = self.signals.progress

The fix simply is to set the progress signal and kwargs after self.signal is instantiated:
self.signals = WorkerSignals()

# Add the callback to our kwargs
kwargs['progress_callback'] = self.signals.progress
self.kwargs = kwargs


Hi, I'm getting an error on Anaconda:

AttributeError: 'Worker' object has no attribute 'signals'
on Python 3.6 it just crashes


Same issue here. Any answer? I want to feel as thrilled as these other folks. Thanks!


This seems excellent, but Like Roger Gonzales, I am also running Anaconda3 - Python 3.6.2. It is also crashing at the line (52):

kwargs['progress_callback'] = self.signals.progress

Running in debug, at the point this is executed, `self` exists, but is empty.

Help?! Thanks!



It's because this line
kwargs['progress_callback'] = self.signals.progress

Is before the creation of the signals


That's what it looked like, but others were saying it worked, so I was questioning myself. Thanks!


Thanks for this —I've fixed the code. Originally it assigned to kwargs after the self.signals (which would update self.kwargs, because they were still the same object). Someone pointed out this was confusing, and I broke it while trying to clarify that. Now it stores the kwargs in the same place and assigns to self.kwargs (which is still the same object, but it's less confusing!)


Thanks caleb, yep you're quite right. I broke that while trying to clear up a question someone else had asked. It should be working now — the assignment to the kwargs variable is just moved after.


Thanks Roger, I broke it while trying to clarify something else. I've fixed it now (apologies for the slow response to your earlier post).


Thank you, this code help me a lot to understand the way QThreadPool work, now I have some Worker classes running on my app. Greetings


This is an awesome tutorial and example, appreciate the work! IdIbe curious to see if/how qthread could be implemented alongside qthreadpool to manage/stop loops. Or generally how that's done in a qrunnable? Timers maybe?


Life saving article. Thanks!