PyQt5 QThread Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)

Maurizio Berti maurizio.berti at gmail.com
Thu Jul 1 03:09:39 BST 2021


Il giorno mer 30 giu 2021 alle ore 16:30 Giuseppe Corbelli <
corbelligiuseppe at mesdan.it> ha scritto:

> This is the root of all problems. UI (QWidget) subclasses must live in
> the main thread.
>

To be precise, UI elements can only be created in the main thread, read
access to their properties is possible but not always reliable (some
properties might require more than one "cycle" of the main event loop in
order to be applied), while write access is "forbidden" (or, better, not
safe), as it usually causes various levels of issues, from incorrect
repainting up to possible crashes.


> Besides that you usually do NOT subclass QThread [1] but moveToThread()
> a QObject subclass and communicate with signals and slots.
> [1] https://www.qt.io/blog/2010/06/17/youre-doing-it-wrong


This is a common misconception, caused by some incorrect interpretations of
the above post and how QThread actually works (including with PyQt).
First of all, exactly like python's Thread, QThread is not a "thread", but
a wrapper around the operating system thread.
They are fundamentally identical, as Phil Thompson (the developer of
PyQt) himself explained [1].

The actual thread is created and then started when start() is called, and
it's from that thread that run() is actually executed.
Subclassing QThread and implementing its run() method is perfectly safe and
correct, but there are some differences.

As explained in the related post on woboq [2]:

When to subclass and when not to?
>
>    - If you do not really need an event loop in the thread, you should
>    subclass.
>    - If you need an event loop and handle signals and slots within the
>    thread, you may not need to subclass.
>
> Note: the second point says "you may *not* need", not "you should not".

This is related to the fact that most threading (especially in PyQt)
doesn't need to execute its own separate event loop (which is what the
default QThread.run() implementation does: creates an event loop and calls
its exec()), but we should also consider that subclassing on C++ and
python, while conceptually identical, prompts some differences (I won't
dwell on this as I don't have enough technical knowledge on the former).

For most use cases, subclassing QThread and implementing run() in PyQt is
perfectly fine, correct and safe.
In any case (QThread or QObject with moveToThread), what is important to
keep in mind is:

- as said, no direct access to UI elements should ever happen *from
outside* the main UI thread, at least for setting properties or creating
objects used by the GUI in any way (eg. setting a QTextCursor);
- the same is also valid for some UI related classes, most importantly
QPixmap [3]: use QImage or QPicture instead;
- widgets cannot be moved to a thread;
- communicating *to* a thread is possible, as long as it's done in a
thread-safe fashion (simple "keep running" flags, Queues, etc);
- Qt "decides" how the signal is processed by the slots it's connected to
based on the thread of the receiver; if it's a QObject, and any of the two
(sender or receiver) reside on different threads, the signal is *queued*;
- for the above reason, objects sent from/to a thread should not be
expected to be "mutable" by the target thread (they never should, by the
way);
- for the same reason, it's not possible to connect the started signal to a
function of a QObject instance that exists in the main thread: it would
require moving the object to the thread, which is not allowed, and the
result is that the signal will be queued and then the function will be
executed in the receiver thread;
- while QThread should allow concurrency, the problem is that Python is
still "in control", meaning that a QThread will provide almost no
performance benefit over standard python threads: both implementations obey
to the GIL problem, *except* for operations directly implemented in C++ or
that deal with I/O operations; in practice: they don't "speed up" things,
they mostly ensure that the UI keeps responsive;

Finally, you could still subclass a QThread and implement a "worker
function" in that thread (thus allowing the default implementation to
execute and use the thread's event loop): just give it a different name and
connect it to the started signal.

There's a useful and interesting answer about this subject on StackOverflow
[4], which clearly explains and demonstrates what written above, and also
another question [5] that has equally interesting insights (with some
inaccuracies in certain posts, though).

[1] https://riverbankcomputing.com/pipermail/pyqt/2007-October/017361.html
[2] https://woboq.com/blog/qthread-you-were-not-doing-so-wrong.html
[3] https://doc.qt.io/qt-5/threads-modules.html#painting-in-threads
[4]
https://stackoverflow.com/questions/30676599/emitting-signals-from-a-python-thread-using-qobject/49802578#49802578
[5]
https://stackoverflow.com/Questions/1595649/threading-in-a-pyqt-application-use-qt-threads-or-python-threads

Maurizio

-- 
È difficile avere una convinzione precisa quando si parla delle ragioni del
cuore. - "Sostiene Pereira", Antonio Tabucchi
http://www.jidesk.net
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20210701/48ee88dd/attachment-0001.htm>


More information about the PyQt mailing list