亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

工作線程和主窗口之間的 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_())

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

查看完整回答
反對 回復 2021-07-28
  • 2 回答
  • 0 關注
  • 482 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號