1. Some background info
The official documentation on how to instantiate and use a QThread can be found here: http://doc.qt.io/qt-5/qthread.html
The documentation describes two basic approaches: (1) worker-object approach and (2) QThread subclass approach.
> The following article explains why one needs to be careful when using the second approach: https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/
> The following article explains why both approaches have their merits: https://woboq.com/blog/qthread-you-were-not-doing-so-wrong.html
I quote:
As a rule of thumb:
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.
As I do need some sort of communication between the QApplication thread and the new QThread (and I believe signal-slot is a good way to communicate), I'll use the worker-object approach.
2. Sample code from the official docs
On the official Qt5 docs on QThreads (see http://doc.qt.io/qt-5/qthread.html), you can find sample code. I've done an effort to translate it to Python:
(See this stackoverflow question to see more details on that translation: QThreads in Pyqt5: is this the correct C++ to Python translation of the official QThread docs?)
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class Worker(QObject):
resultReady = pyqtSignal(str)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Note: this constructor is empty now.
# But would it be okay to instantiate new
# objects here, and use them in doWork(..)?
# A more general question: is it okay if
# doWork(..) accesses variables that were
# created in another thread (perhaps the
# main QApplication thread)?
@pyqtSlot(str)
def doWork(self, param):
result = "hello world"
print("foo bar")
# ...here is the expensive or blocking operation... #
self.resultReady.emit(result)
class Controller(QObject):
operate = pyqtSignal(str)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 1. Create 'workerThread' and 'worker' objects
# ----------------------------------------------
self.workerThread = QThread()
self.worker = Worker()
self.worker.moveToThread(self.workerThread)
# 2. Connect all relevant signals
# --------------------------------
self.workerThread.finished.connect(self.worker.deleteLater)
self.operate.connect(self.worker.doWork)
self.worker.resultReady.connect(self.handleResults)
# 3. Start the thread
# --------------------
self.workerThread.start()
def __del__(self):
self.workerThread.quit()
self.workerThread.wait()
@pyqtSlot(str)
def handleResults(self, param):
print(param)
global app
app.exit()
if __name__ == '__main__':
app = QCoreApplication([])
controller = Controller()
controller.operate.emit("foo")
sys.exit(app.exec_())
As you can see, the constructor of the Worker
class is empty. That brings us to the next paragraph.
3. My question: okay to create objects in the worker's constructor?
From the great article of Mss. Maya Posch (https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/), I quote:
By the way, one extremely important thing to note here is that you should NEVER allocate heap objects (using new) in the constructor of the QObject class as this allocation is then performed on the main thread and not on the new QThread instance, meaning that the newly created object is then owned by the main thread and not the QThread instance. This will make your code fail to work. Instead, allocate such resources in the main function slot such as process() in this case as when that is called the object will be on the new thread instance and thus it will own the resource.
[Maya Posch - how to really truly use qthreads, the full explanation]
This article was written for C++ software.
Does this quote still apply on Python applications? In other words, is it okay to instantiate objects in the constructor of the Worker
class?
More generally, I could ask: is it okay if the doWork(..)
function accesses variables that were instantiated in another thread (perhaps the main QApplication thread)?
Hereby I provide my current system settings:
> Qt5 (QT_VERSION_STR
= 5.10.1)
> PyQt5 (PYQT_VERSION_STR
= 5.10.1)
> Python 3.6.3
> Windows 10, 64-bit