QObject.moveToThread() Template
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/
import sys
from PySide6.QtCore import (QObject, QThread,
Slot, Signal, Qt)
from PySide6.QtWidgets import (QApplication,
QPushButton, QLabel, QWidget, QVBoxLayout)
# 1. Create the worker class
class Worker(QObject):
finished = Signal()
error = Signal(str)
def __init__(self, parent=None):
super().__init__(parent)
# This method to be executed
@Slot()
def process(self):
print('Hello World')
self.finished.emit()
class Window(QWidget):
def __init__(self):
super().__init__()
layout = QVBoxLayout()
self.setLayout(layout)
button = QPushButton('Start background thread')
button.clicked.connect(self.on_button_clicked)
self.label = QLabel()
self.label.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(button)
layout.addWidget(self.label)
@Slot()
def on_button_clicked(self):
# 2. Create the thread object
self.background_thread = QThread()
# 3. Create the worker and move it to the thread
self.worker = Worker()
self.worker.moveToThread(self.background_thread)
self.worker.finished.connect(self.on_finished)
# 4. Connect the signals and the slots
self.worker.error.connect(self.on_error)
self.background_thread.started.connect(self.worker.process)
self.worker.finished.connect(self.background_thread.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.background_thread.finished.connect(self.background_thread.deleteLater)
# 5. Start the thread
self.background_thread.start()
@Slot()
def on_finished(self):
self.label.setText('Worker finished')
@Slot()
def on_error(self, message):
print(message)
if __name__ == '__main__':
app = QApplication(sys.argv)
main_window = Window()
main_window.show()
sys.exit(app.exec())
In the previous example we demonstrated how a PySide6 Gui can become nonresponsive. Now let’s see how we can use Qt threads to execute long running tasks in the background while keeping the Gui responsive
-
Create a
QObject
subclass that contains a slot / method to be executed in a background thread (ie a thread other than the Gui thread). In the example, the slot to be executed is calledprocess()
and simply prints a message and emits a custom signal namedfinished
before it returns. This is how you communicate between Qt threads - using signals and slots. In addition tofinished
theWorker
class declares another signal namederror
which we never call but would be emitted on error conditions in theprocess()
execution. Then, in the main window class -
Create a
QThread
object namedbackground_thread
or something similar (don’t call itthread
as that name is already taken). Note how we useself
to makebackground_thread
the main window member - this makes surebackground_thread
doesn’t go out of scope while the thread is still running. -
Create a
Worker
object and useQObject.moveToThread()
to move it tobackground_thread
. Now theWorker.process()
method will be executed inbackground_thread
. - Connect the appropriate signals and slots:
- connect the
background_thread.started
signal with theworker.process()
slot. This means thatWorker.process()
will be executed as soon as the background thread is started, - connect the
Worker.finished
signal with thebackground_thread.quit
method. This means that the background thread will quit as soon as theWorker.process()
method returns, - connect the
Worker.finished
signal with theWorker.deleteLater()
method. This means that the worker object will be deleted some time after it emits thefinished
signal. - connect the
background_thread.finished
signal with thebackground_thread.deleteLater()
method. This means that the background thread will be deleted some time after it finishes.
- connect the
- Finally, start the
background_thread
using theQThread.start()
method.
With the above setup the process()
method is executed as soon as the thread is started, the thread quits as soon as the method returns and both the worker object and the thread object are destroyed while the Gui stays responsive.