Block the Qt GUI Thread or How Not to Do Things
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
import os
import sys
from PySide6.QtCore import QObject, Signal, Slot
from PySide6.QtWidgets import (QApplication,
QWidget, QPushButton, QLabel, QVBoxLayout)
class Task(QObject):
progress = Signal(str)
def __init__(self, parent=None):
super().__init__(parent)
# 1. Create a long running task
# We search for a file using os.walk()
# using a tight (blocking) for loop.
# The loop blocks the Qt event loop
# so no signals are send or events
# processed until the loop exits.
# This effectively freezes the Gui.
@Slot()
def do_work(self):
path = os.path.abspath('.').split(os.path.sep)[0] + os.path.sep
name = 'bogus'
for root, _, files in os.walk(path):
self.progress.emit(root)
if name in files:
print(os.path.join(root, name))
class Window(QWidget):
def __init__(self):
super().__init__()
layout = QVBoxLayout()
self.setLayout(layout)
# 2. Create a push button
self.button = QPushButton('Start working')
self.label = QLabel()
self.button.clicked.connect(self.do_work)
layout.addWidget(self.button)
layout.addWidget(self.label)
# 3. Execute the long running task.
def do_work(self):
self.task = Task()
self.task.progress.connect(self.label.setText)
self.task.do_work()
if __name__ == '__main__':
app = QApplication(sys.argv)
main_window = Window()
main_window.show()
sys.exit(app.exec())
Threads of execution let you execute your code concurrently while sharing the program memory and other resources. There are two use cases for threads:
- Make processing faster by using more than one processor core,
- Keep the Gui thread responsive by moving long running tasks to other threads
In the following examples we’ll focus on the second one but first let’s demonstrate the problem ans see what a non-responsive Qt Gui looks like. To block the Qt Gui
-
Create the task. In the example we use
os.walk()
and a blocking (“tight”)for
loop to search the file system for a bogus file. We also try to report progress after each file is iterated over using a custom Qt signal namedprogress
. This does not work because the loop blocks the Qt event loop and no signals are sent or events processed until the loop exits. The loop is executed in the main application thread (also called the Gui thread) so the whole application becomes nonresponsive - it freezes and you can’t even close it. -
Create a push button,
-
Create a slot that starts the long running task when the push button is clicked.