将 PyQt5 按钮连接到函数时如何使用 lambda
How to use lambda when connecting PyQt5 buttons to functions
在我的代码中,当单击一个按钮 (self.longRunningBtn
) 时,它会连接到一个不带参数的函数,并且可以正常工作。当该函数被修改为采用额外参数 a, b, c
时,我得到一个错误 TypeError: argument 1 has unexpected type 'NoneType'
。然后通过在调用中添加 lambda 来修复此问题,但现在该函数似乎根本 运行 并且整个 GUI 冻结,即使它在不同的线程上也是如此。我希望能够单击带有参数 a, b, c
的 self.longRunningBtn
而不会看到该错误并且 GUI 不会冻结。
没有参数的原始代码,完美运行:
from PyQt5.QtCore import QObject, QThread, pyqtSignal
import sys
from time import sleep
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
QApplication,
QLabel,
QMainWindow,
QPushButton,
QVBoxLayout,
QWidget,
)
class Worker(QObject):
finished = pyqtSignal()
progress = pyqtSignal(str)
def run(self):
for i in range(5):
sleep(1)
self.progress.emit(f"{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()
self.worker.moveToThread(self.thread)
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())
添加了参数的代码:
def run(self, a, b, c):
for i in range(5):
sleep(1)
self.progress.emit(f"{i+1} {a} {b} {c}")
self.finished.emit()
self.thread.started.connect(self.worker.run("alpha", "beta", "charlie"))
returns TypeError: argument 1 has unexpected type 'NoneType'
修复类型错误:
self.thread.started.connect(lambda: self.worker.run("alpha", "beta", "charlie"))
但是现在 GUI 冻结了,我看不到该函数正在执行的任何操作,我希望它的功能与原始代码中的完全一样,但标签末尾附加了 a, b, c
作为嗯
问题在于,最后 lambda 相当于在主线程中直接调用 运行() 方法,这与在接收线程中发出信号以执行它的方法不同。
一种可能的解决方案是使用 functools.partial:
from functools import partial
self.thread.started.connect(partial(self.worker.run, "alpha", "beta", "charlie"))
在我的代码中,当单击一个按钮 (self.longRunningBtn
) 时,它会连接到一个不带参数的函数,并且可以正常工作。当该函数被修改为采用额外参数 a, b, c
时,我得到一个错误 TypeError: argument 1 has unexpected type 'NoneType'
。然后通过在调用中添加 lambda 来修复此问题,但现在该函数似乎根本 运行 并且整个 GUI 冻结,即使它在不同的线程上也是如此。我希望能够单击带有参数 a, b, c
的 self.longRunningBtn
而不会看到该错误并且 GUI 不会冻结。
没有参数的原始代码,完美运行:
from PyQt5.QtCore import QObject, QThread, pyqtSignal
import sys
from time import sleep
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
QApplication,
QLabel,
QMainWindow,
QPushButton,
QVBoxLayout,
QWidget,
)
class Worker(QObject):
finished = pyqtSignal()
progress = pyqtSignal(str)
def run(self):
for i in range(5):
sleep(1)
self.progress.emit(f"{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()
self.worker.moveToThread(self.thread)
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())
添加了参数的代码:
def run(self, a, b, c):
for i in range(5):
sleep(1)
self.progress.emit(f"{i+1} {a} {b} {c}")
self.finished.emit()
self.thread.started.connect(self.worker.run("alpha", "beta", "charlie"))
returns TypeError: argument 1 has unexpected type 'NoneType'
修复类型错误:
self.thread.started.connect(lambda: self.worker.run("alpha", "beta", "charlie"))
但是现在 GUI 冻结了,我看不到该函数正在执行的任何操作,我希望它的功能与原始代码中的完全一样,但标签末尾附加了 a, b, c
作为嗯
问题在于,最后 lambda 相当于在主线程中直接调用 运行() 方法,这与在接收线程中发出信号以执行它的方法不同。
一种可能的解决方案是使用 functools.partial:
from functools import partial
self.thread.started.connect(partial(self.worker.run, "alpha", "beta", "charlie"))