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.
from time import sleep from PyQt5.QtCore import QObject, QThread, pyqtSignal,Qt from PyQt5.QtWidgets import QApplication,QMainWindow,QLabel,QPushButton,QVBoxLayout,QWidget import sys # Step 1: Create a worker class class Worker(QObject): finished = pyqtSignal() progress = pyqtSignal(int) def run(self): """Long-running task.""" for i in range(5): sleep(1) self.progress.emit(i + 1) self.finished.emit() class Window(QMainWindow): def __init__(self, parent=None): super().__init__(parent) self.clicksCount = 0 self.setupUi() def setupUi(self): self.setWindowTitle("Freezing GUI") self.resize(300, 150) self.centralWidget = QWidget() self.setCentralWidget(self.centralWidget) # Create and connect widgets self.clicksLabel = QLabel("Counting: 0 clicks", self) self.clicksLabel.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) self.stepLabel = QLabel("Long-Running Step: 0") self.stepLabel.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) self.countBtn = QPushButton("Click me!", self) self.countBtn.clicked.connect(self.countClicks) self.longRunningBtn = QPushButton("Long-Running Task!", self) self.longRunningBtn.clicked.connect(self.runLongTask) # Set the layout layout = QVBoxLayout() layout.addWidget(self.clicksLabel) layout.addWidget(self.countBtn) layout.addStretch() layout.addWidget(self.stepLabel) layout.addWidget(self.longRunningBtn) self.centralWidget.setLayout(layout) def countClicks(self): self.clicksCount += 1 self.clicksLabel.setText(f"Counting: {self.clicksCount} clicks") def reportProgress(self, n): self.stepLabel.setText(f"Long-Running Step: {n}") def runLongTask(self): # Step 2: Create a QThread object self.thread = QThread() # Step 3: Create a worker object self.worker = Worker() # Step 4: Move worker to the thread self.worker.moveToThread(self.thread) # Step 5: Connect signals and slots self.thread.started.connect(self.worker.run) self.worker.finished.connect(self.thread.quit) self.worker.finished.connect(self.worker.deleteLater) self.thread.finished.connect(self.thread.deleteLater) self.worker.progress.connect(self.reportProgress) # Step 6: Start the thread self.thread.start() # Final resets self.longRunningBtn.setEnabled(False) self.thread.finished.connect( lambda: self.longRunningBtn.setEnabled(True) ) self.thread.finished.connect( lambda: self.stepLabel.setText("Long-Running Step: 0") ) app = QApplication(sys.argv) win = Window() win.show() sys.exit(app.exec())
class Worker(QObject): finished = pyqtSignal(str) def __init__(self, *init_args, **init_kwargs): QObject.__init__(self, *init_args, **init_kwargs) self._return = None def run(self): """Long-running task.""" self._return = self._target(*self._args, **self._kwargs) self.finished.emit(self._return)
Advertisement
Answer
As one of solutions, you can pass a function and an argument to the Worker’s __init__
method, like:
class Worker(QObject): finished = pyqtSignal() progress = pyqtSignal(int) result = pyqtSignal('QVariant') def __init__(self, function, args): super().__init__() self.function = function self.args = args def run(self): res = self.function(self.args) self.result.emit(res) self.finished.emit()
So you can pass a method and an argument when creating a worker and connect the method to handle the result:
self.thread = QThread() self.worker = Worker(Proxy.GetInfo, code) self.worker.moveToThread(self.thread) self.thread.started.connect(self.worker.run) self.worker.finished.connect(self.thread.quit) self.worker.finished.connect(self.thread.deleteLater) self.worker.result.connect(self.receiveResult) self.thread.finished.connect(self.thread.deleteLater) self.thread.start()