猿问

工作线程和主窗口之间的 PyQt5 信号通信无法正常工作

我正在使用 PyQt5 创建一个应用程序,它有一个非常简单的用户界面。有一个可以从中选择值的下拉列表,然后有一个可以单击的按钮,将根据下拉列表中的当前值执行操作。

目前,如果我运行下面的代码,我可以从下拉列表中获取一个值,它会在myaction.myaction(customer_name)运行之前发送到工作线程。代码也运行良好,但是当工作线程中的函数运行时,GUI 没有按照我希望的方式工作。当我单击开始按钮时,它应该向 GUI 发出一个信号以禁用该按钮、更改其标签和颜色,但这从未发生过。当函数完成时,它应该变回原来的形式。

问题是我如何处理信号,还是我的类中有多余的功能?每次单击按钮时将该下拉列表值发送到工作线程的正确方法是什么,所以我可以简单地将它用作变量?

我在网上找到的每一个可能的解决方案都让我兴奋,但他们都没有为我工作,或者他们中的一些让我无法理解。


守候你守候我
浏览 430回答 2
2回答

芜湖不芜

这个问题是由一个非常普遍和错误的概念引起的,他们认为 QThread 是一个Qt Thread,即Qt创建的新线程,但不,QThread是文档中指出的线程处理程序:QThread 类提供了一种与平台无关的方法来管理线程。QThread 对象管理程序中的一个控制线程。QThreads 在 run() 中开始执行。默认情况下,run() 通过调用 exec() 启动事件循环,并在线程内运行 Qt 事件循环。在另一个线程上运行的唯一部分是 run 方法,在您的情况下,您没有调用,因为您的逻辑不同,您不想连续执行繁重的任务,但应用户的要求,因此设计必须是worker 但您使用 QThread 作为基类,这是不正确的,您必须使用 QObject 作为基类并将其移动到新线程,以便 QObject 在该线程中执行其任务,防止 GUI 阻塞。一个 QObject 不是线程安全的,它存在于一个线程中,它所在的线程由以下决定:AQObject住在父线程中如果您没有父级,请留在创建它的线程中,除非您已移动到另一个线程。并且您可以使用该moveToThread()函数移动到另一个线程,但是moveToThread()如果它QObject有父级,因为第一个条件是特权的,则会失败。另一方面,如果您想从另一个线程调用一个方法,则有必要使用装饰器 @QtCore.pyqtSlot()综上所述,我们得到以下解决方案:#!/usr/bin/env python3import sysimport timefrom PyQt5 import QtCore, QtWidgetsclass ConfWorker(QtCore.QObject):    updated_button = QtCore.pyqtSignal(list)    updated_label = QtCore.pyqtSignal(str)    updated_error = QtCore.pyqtSignal(str)    request_signal = QtCore.pyqtSignal()    customer = QtCore.pyqtSignal(str)    def __init__(self, parent=None):        super(ConfWorker, self).__init__(parent)        self.customer.connect(self.getcustomer)    @QtCore.pyqtSlot()    def doWork(self):        self.request_signal.emit()    @QtCore.pyqtSlot(str)    def getcustomer(self, text):        self.configure(text)    def configure(self, customer_name):        self.updated_button.emit(["In progress...", False])        self.updated_label.emit(customer_name)        time.sleep(5) # During this time you should be able to see color change etc.        #myaction.myaction(customer_name)# TAKES ~20 SECONDS TO FINISH        self.updated_button.emit(["Start", True])class ConfGUI(QtWidgets.QWidget):    def __init__(self, parent=None):        super(ConfGUI, self).__init__()        # create a QThread and start the thread that handles        thread = QtCore.QThread(self)        thread.start()        # create the worker without a parent so you can move        self.worker = ConfWorker()        # the worker moves to another thread        self.worker.moveToThread(thread)        self.worker.updated_button.connect(self.updateButton)        self.worker.updated_label.connect(self.updateLabel)        self.worker.updated_error.connect(self.updateError)        self.worker.request_signal.connect(self.sendCustomer)        self.targetBtn = QtWidgets.QPushButton('Start Configuration', self)        self.targetBtn.setStyleSheet("QPushButton { background-color: green; color: white }"                        "QPushButton:disabled { background-color: red; color: white }")        self.targetBtn.clicked.connect(self.worker.doWork)        self.targetBtn.setFixedSize(200, 50)        self.customerlist = QtWidgets.QComboBox(self)        self.customerlist.addItems(["testcustomer1", "testcustomer2", "testcustomer3"])        self.customerlist.setFixedSize(200, 50)        self.label = QtWidgets.QLabel(self, alignment=QtCore.Qt.AlignCenter)        self.label.setStyleSheet('font-size: 30pt; font-family: Courier; color: green;')        self.label.setFixedSize(400,50)        self.error_label = QtWidgets.QLabel(self, alignment=QtCore.Qt.AlignCenter)        self.error_label.setStyleSheet('font-size: 30pt; font-family: Courier; color: red;')        self.error_label.setFixedSize(400,50)        lay = QtWidgets.QVBoxLayout(self)        lay.addWidget(self.customerlist, alignment=QtCore.Qt.AlignCenter)        lay.addWidget(self.label, alignment=QtCore.Qt.AlignCenter)        lay.addWidget(self.error_label, alignment=QtCore.Qt.AlignCenter)        lay.addWidget(self.targetBtn, alignment=QtCore.Qt.AlignCenter)        self.setFixedSize(400, 550)    @QtCore.pyqtSlot()    def sendCustomer(self):        self.worker.customer.emit(self.customerlist.currentText())    @QtCore.pyqtSlot(list)    def updateButton(self, button_list):        self.targetBtn.setText(button_list[0])        self.targetBtn.setEnabled(button_list[1])    @QtCore.pyqtSlot(str)    def updateLabel(self, label_text):        self.label.setText(label_text)    @QtCore.pyqtSlot(str)    def updateError(self, error_text):        self.error_label.setText(error_text)if __name__ == '__main__':    app = QtWidgets.QApplication(sys.argv)    ex = ConfGUI()    ex.show()    sys.exit(app.exec_())观察:另外,正如您在我的解决方案中看到的那样,QThread将是窗口的子级,因为QThread是QObject处理另一个线程。

FFIVE

你真的写了很困难很简单的事情。尝试一下:import sysfrom PyQt5.QtWidgets import QWidget, QApplication, QPushButton, QComboBox, QLabelfrom PyQt5.QtCore    import QThread, pyqtSignalclass ConfWorker(QThread):    threadSignal = pyqtSignal(str)    finishSignal = pyqtSignal(str)    def __init__(self, startParm):        super().__init__()        self.startParm = startParm   # Initialize the parameters passed to the task     def run(self):        # Do something...        for i in range(20):            text = "In progress ................." \                    if i%2==0 else "In progress {}".format(self.startParm)            self.threadSignal.emit(text)            QThread.msleep(500)        self.finishSignal.emit(self.startParm)class ConfGUI(QWidget):    def __init__(self):        super().__init__()        self.setGeometry(800, 100, 400, 550)        self.setFixedSize(400, 550)                self.targetBtn = QPushButton('Start Configuration', self)        self.targetBtn.setStyleSheet(                        "QPushButton { background-color: green; color: white;}"                        "QPushButton:disabled { background-color: red; color: white;}"                        )        self.targetBtn.setGeometry(100, 400, 200, 50)        self.targetBtn.clicked.connect(self.workerStart)                   self.customerlist = QComboBox(self)        self.customerlist.setGeometry(100, 50, 200, 50)        self.customerlist.setObjectName("customer")        self.customerlist.addItem("testcustomer1")        self.customerlist.addItem("testcustomer2")        self.customerlist.addItem("testcustomer3")        self.customerlist.activated[str].connect(self.comboActivated)        self.startParm = "testcustomer1"        self.label = QLabel(self)        self.label.setStyleSheet('font-size: 30pt; font-family: Courier; color: green;')        self.label.setGeometry(70,250,400,50)    def workerStart(self):        self.targetBtn.setText("In progress...")        self.targetBtn.setEnabled(False)        self.label.setText("")        self.worker = ConfWorker(self.startParm)          self.worker.threadSignal.connect(self.on_threadSignal)        self.worker.finishSignal.connect(self.on_finishSignal)                self.worker.start()                         def on_threadSignal(self, text):        self.targetBtn.setText(text)    def on_finishSignal(self, text):        self.targetBtn.setEnabled(True)        self.targetBtn.setText("Start Configuration'")        self.label.setText(text)    def comboActivated(self, text):        self.startParm = textif __name__ == '__main__':    app = QApplication(sys.argv)    ex  = ConfGUI()    ex.show()    sys.exit(app.exec_())
随时随地看视频慕课网APP

相关分类

Python
我要回答