<div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"></div><div class="gmail_quote"><div dir="ltr" class="gmail_attr">Il giorno mer 30 giu 2021 alle ore 16:30 Giuseppe Corbelli <<a href="mailto:corbelligiuseppe@mesdan.it">corbelligiuseppe@mesdan.it</a>> ha scritto:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">This is the root of all problems. UI (QWidget) subclasses must live in <br>
the main thread.<br></blockquote><div><br></div><div>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.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
Besides that you usually do NOT subclass QThread [1] but moveToThread() <br>
a QObject subclass and communicate with signals and slots.<br>
[1] <a href="https://www.qt.io/blog/2010/06/17/youre-doing-it-wrong" rel="noreferrer" target="_blank">https://www.qt.io/blog/2010/06/17/youre-doing-it-wrong</a></blockquote><div> </div><div></div></div><div>This is a common misconception, caused by some incorrect interpretations of the above post and how QThread actually works (including with PyQt).</div><div>First of all, exactly like python's Thread, QThread is not a "thread", but a wrapper around the operating system thread.</div><div>They are fundamentally identical, as Phil Thompson (the developer of PyQt) himself explained [1].</div><div><br></div><div>The actual thread is created and then started when start() is called, and it's from that thread that run() is actually executed.</div><div>Subclassing QThread and implementing its run() method is perfectly safe and correct, but there are some differences.</div><div><br>As explained in the related post on woboq [2]:</div><div><br></div><div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">When to subclass and when not to?<br><ul><li>If you do not really need an event loop in the thread, you should subclass.<br></li><li>If you need an event loop and handle signals and slots within the thread, you may not need to subclass.<br></li></ul></blockquote></div><div>Note: the second point says "you may *not* need", not "you should not".</div><div><br></div><div>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).</div><div><br></div><div>For most use cases, subclassing QThread and implementing run() in PyQt is perfectly fine, correct and safe.</div><div>In any case (QThread or QObject with moveToThread), what is important to keep in mind is:</div><div><br></div><div>- 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);</div><div>- the same is also valid for some UI related classes, most importantly QPixmap [3]: use QImage or QPicture instead;</div><div>- widgets cannot be moved to a thread;</div><div>- communicating *to* a thread is possible, as long as it's done in a thread-safe fashion (simple "keep running" flags, Queues, etc);</div><div>- 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*;</div><div>- 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);</div><div>- 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;</div><div>- 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;</div><div><br></div><div>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.</div><div><br></div><div>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).</div><div><br></div><div>[1] <a href="https://riverbankcomputing.com/pipermail/pyqt/2007-October/017361.html">https://riverbankcomputing.com/pipermail/pyqt/2007-October/017361.html</a></div><div>[2] <a href="https://woboq.com/blog/qthread-you-were-not-doing-so-wrong.html">https://woboq.com/blog/qthread-you-were-not-doing-so-wrong.html</a></div><div>[3] <a href="https://doc.qt.io/qt-5/threads-modules.html#painting-in-threads">https://doc.qt.io/qt-5/threads-modules.html#painting-in-threads</a></div><div>[4] <a href="https://stackoverflow.com/questions/30676599/emitting-signals-from-a-python-thread-using-qobject/49802578#49802578">https://stackoverflow.com/questions/30676599/emitting-signals-from-a-python-thread-using-qobject/49802578#49802578</a></div><div>[5] <a href="https://stackoverflow.com/Questions/1595649/threading-in-a-pyqt-application-use-qt-threads-or-python-threads">https://stackoverflow.com/Questions/1595649/threading-in-a-pyqt-application-use-qt-threads-or-python-threads</a><br></div><div><br></div><div>Maurizio<br><br></div>-- <br><div dir="ltr" class="gmail_signature">È difficile avere una convinzione precisa quando si parla delle ragioni del cuore. - "Sostiene Pereira", Antonio Tabucchi<br><a href="http://www.jidesk.net" target="_blank">http://www.jidesk.net</a></div></div></div></div></div></div></div></div></div></div></div>