为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

守候你守候我 2021-07-23 14:04:10
我正在使用 PyQt5 创建一个应用程序,它有一个非常简单的用户界面。有一个可以从中选择值的下拉列表,然后有一个可以单击的按钮,将根据下拉列表中的当前值执行操作。目前,如果我运行下面的代码,我可以从下拉列表中获取一个值,它会在myaction.myaction(customer_name)运行之前发送到工作线程。代码也运行良好,但是当工作线程中的函数运行时,GUI 没有按照我希望的方式工作。当我单击开始按钮时,它应该向 GUI 发出一个信号以禁用该按钮、更改其标签和颜色,但这从未发生过。当函数完成时,它应该变回原来的形式。问题是我如何处理信号,还是我的类中有多余的功能?每次单击按钮时将该下拉列表值发送到工作线程的正确方法是什么,所以我可以简单地将它用作变量?我在网上找到的每一个可能的解决方案都让我兴奋,但他们都没有为我工作,或者他们中的一些让我无法理解。
查看完整描述

2 回答

?
芜湖不芜

TA贡献1796条经验 获得超7个赞

这个问题是由一个非常普遍和错误的概念引起的,他们认为 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 python3

import sys

import time

from PyQt5 import QtCore, QtWidgets



class 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处理另一个线程。


查看完整回答
反对 回复 2021-07-28
?
FFIVE

TA贡献1797条经验 获得超6个赞

你真的写了很困难很简单的事情。尝试一下:


import sys

from PyQt5.QtWidgets import QWidget, QApplication, QPushButton, QComboBox, QLabel

from PyQt5.QtCore    import QThread, pyqtSignal


class 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 = text



if __name__ == '__main__':

    app = QApplication(sys.argv)

    ex  = ConfGUI()

    ex.show()

    sys.exit(app.exec_())

//img1.sycdn.imooc.com//610141d50001bb6a04000582.jpg

查看完整回答
反对 回复 2021-07-28
  • 2 回答
  • 0 关注
  • 429 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号