Walk the Filesystem by Creating a QThread
Subclass
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import os
import sys
from PySide6.QtCore import QThread, Slot, Signal, Qt
from PySide6.QtWidgets import (QApplication, QPushButton,
QLabel, QWidget, QVBoxLayout)
# 1. Create a QThread subclass
# and subclass its run() method.
# Add signals as needed.
class WorkerThread(QThread):
progress = Signal(str)
def __init__(self, parent=None):
super().__init__(parent)
print('Init it', QThread.currentThread().objectName())
def run(self):
print('Running in: ',
QThread.currentThread().objectName())
print('event loop level: ',
QThread.currentThread().loopLevel())
path = os.path.abspath('.').split(os.path.sep)[0] + os.path.sep
for root, _, _ in os.walk(path):
if QThread.currentThread().isInterruptionRequested():
return
self.progress.emit(os.path.basename(root))
class Window(QWidget):
def __init__(self):
super().__init__()
QThread.currentThread().setObjectName('Main thread')
layout = QVBoxLayout()
self.setLayout(layout)
self.start_button = QPushButton('Start background thread')
self.start_button.clicked.connect(self.on_start_button_clicked)
self.cancel_button = QPushButton('Cancel')
self.cancel_button.clicked.connect(self.on_cancel_button_clicked)
self.cancel_button.setDisabled(True)
self.label = QLabel()
self.label.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(self.start_button)
layout.addWidget(self.cancel_button)
layout.addWidget(self.label)
@Slot()
def on_start_button_clicked(self):
print('Main thread loop level',
QThread.currentThread().loopLevel())
# 2. Create the WorkerThread object
self.worker_thread = WorkerThread()
self.worker_thread.setObjectName('Worker thread')
# 3. Connect the signals with the slots
self.worker_thread.progress.connect(self.on_progress)
self.worker_thread.finished.connect(
self.worker_thread.deleteLater)
self.start_button.setDisabled(True)
self.cancel_button.setEnabled(True)
# 5. Start the worker thread
self.worker_thread.start()
# 4. Handle the signals
@Slot()
def on_cancel_button_clicked(self):
self.start_button.setEnabled(True)
self.cancel_button.setDisabled(True)
if hasattr(self, 'worker_thread'):
self.worker_thread.requestInterruption()
self.worker_thread.wait()
@Slot()
def on_progress(self, msg):
self.label.setText(msg)
# Make sure the thread is destroyed
# when the main window is closed.
def closeEvent(self, event):
try:
self.worker_thread.requestInterruption()
self.worker_thread.wait()
except Exception as e:
print(e)
if __name__ == '__main__':
app = QApplication(sys.argv)
main_window = Window()
main_window.show()
sys.exit(app.exec())
Now let’s walk the filesystem using a QThread
subclass:
-
Create a
QThread
subclass namedWorkerThread
and override itsrun()
method. Inrun()
we first print the current thread object name to show that it indeed runs in the worker thread and after that we useos.walk
to walk the filesystem recursively. For each filesystem object we emit theprogress
signal with the object’s name as the argument. Then, in the main window -
On the start button click create a
WorkerThread
object and set its object name to “Worker thread”, -
Connect the signals with the slots. Each time
WorkerThread.progress
is emitted we executeWindow.on_progress)
which sets the label’s text to the filesystem object name thatprogress
emits as the argument. We also connect theWorkerThread.finished
signal withWorkerThread.deleteLater()
to dispose of the thread when it finishes. -
Handle the signals. When
WorkerThread.progress
is emittedWindow.on_progress()
updates the label text. When the cancel button is clicked we request the worker thread interruption usingQThread.requestInterruption()
and wait for it to finish. -
Start the worker thread each time the start button is clicked.
To make sure the thread is deleted when the user closes the main window we also override Qwidget.closeEvent()
. Note that we didn’t need to call QThread.quit()
but only QThread.wait()
because the thread event loop is not running.