I am trying to find a way to successfully pass a function to a Worker class in Python using PyQT5. Instead of using the pre-defined run function (or Long-running task) in the sample Worker class code, I would like to be able to pass a custom function to the worker class. Below I’ve pasted the sample code I’m working with, followed by an adjustment I’ve tried.
JavaScript
x
83
83
1
from time import sleep
2
from PyQt5.QtCore import QObject, QThread, pyqtSignal,Qt
3
from PyQt5.QtWidgets import QApplication,QMainWindow,QLabel,QPushButton,QVBoxLayout,QWidget
4
import sys
5
6
# Step 1: Create a worker class
7
class Worker(QObject):
8
finished = pyqtSignal()
9
progress = pyqtSignal(int)
10
11
def run(self):
12
"""Long-running task."""
13
for i in range(5):
14
sleep(1)
15
self.progress.emit(i + 1)
16
self.finished.emit()
17
18
class Window(QMainWindow):
19
def __init__(self, parent=None):
20
super().__init__(parent)
21
self.clicksCount = 0
22
self.setupUi()
23
24
def setupUi(self):
25
self.setWindowTitle("Freezing GUI")
26
self.resize(300, 150)
27
self.centralWidget = QWidget()
28
self.setCentralWidget(self.centralWidget)
29
# Create and connect widgets
30
self.clicksLabel = QLabel("Counting: 0 clicks", self)
31
self.clicksLabel.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
32
self.stepLabel = QLabel("Long-Running Step: 0")
33
self.stepLabel.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
34
self.countBtn = QPushButton("Click me!", self)
35
self.countBtn.clicked.connect(self.countClicks)
36
self.longRunningBtn = QPushButton("Long-Running Task!", self)
37
self.longRunningBtn.clicked.connect(self.runLongTask)
38
# Set the layout
39
layout = QVBoxLayout()
40
layout.addWidget(self.clicksLabel)
41
layout.addWidget(self.countBtn)
42
layout.addStretch()
43
layout.addWidget(self.stepLabel)
44
layout.addWidget(self.longRunningBtn)
45
self.centralWidget.setLayout(layout)
46
47
def countClicks(self):
48
self.clicksCount += 1
49
self.clicksLabel.setText(f"Counting: {self.clicksCount} clicks")
50
51
def reportProgress(self, n):
52
self.stepLabel.setText(f"Long-Running Step: {n}")
53
54
def runLongTask(self):
55
# Step 2: Create a QThread object
56
self.thread = QThread()
57
# Step 3: Create a worker object
58
self.worker = Worker()
59
# Step 4: Move worker to the thread
60
self.worker.moveToThread(self.thread)
61
# Step 5: Connect signals and slots
62
self.thread.started.connect(self.worker.run)
63
self.worker.finished.connect(self.thread.quit)
64
self.worker.finished.connect(self.worker.deleteLater)
65
self.thread.finished.connect(self.thread.deleteLater)
66
self.worker.progress.connect(self.reportProgress)
67
# Step 6: Start the thread
68
self.thread.start()
69
70
# Final resets
71
self.longRunningBtn.setEnabled(False)
72
self.thread.finished.connect(
73
lambda: self.longRunningBtn.setEnabled(True)
74
)
75
self.thread.finished.connect(
76
lambda: self.stepLabel.setText("Long-Running Step: 0")
77
)
78
79
app = QApplication(sys.argv)
80
win = Window()
81
win.show()
82
sys.exit(app.exec())
83
JavaScript
1
12
12
1
class Worker(QObject):
2
finished = pyqtSignal(str)
3
4
def __init__(self, *init_args, **init_kwargs):
5
QObject.__init__(self, *init_args, **init_kwargs)
6
self._return = None
7
8
def run(self):
9
"""Long-running task."""
10
self._return = self._target(*self._args, **self._kwargs)
11
self.finished.emit(self._return)
12
Advertisement
Answer
As one of solutions, you can pass a function and an argument to the Worker’s __init__
method, like:
JavaScript
1
15
15
1
class Worker(QObject):
2
finished = pyqtSignal()
3
progress = pyqtSignal(int)
4
result = pyqtSignal('QVariant')
5
6
def __init__(self, function, args):
7
super().__init__()
8
self.function = function
9
self.args = args
10
11
def run(self):
12
res = self.function(self.args)
13
self.result.emit(res)
14
self.finished.emit()
15
So you can pass a method and an argument when creating a worker and connect the method to handle the result:
JavaScript
1
10
10
1
self.thread = QThread()
2
self.worker = Worker(Proxy.GetInfo, code)
3
self.worker.moveToThread(self.thread)
4
self.thread.started.connect(self.worker.run)
5
self.worker.finished.connect(self.thread.quit)
6
self.worker.finished.connect(self.thread.deleteLater)
7
self.worker.result.connect(self.receiveResult)
8
self.thread.finished.connect(self.thread.deleteLater)
9
self.thread.start()
10