【问题标题】:PyQt5: How to send a signal to a worker threadPyQt5:如何向工作线程发送信号
【发布时间】:2016-12-07 19:44:36
【问题描述】:

我知道如何将信号从工作线程发送回主 GUI 线程,但是如何将信号从主线程发送到工作线程?

这是一些包含信号和槽的示例代码。在这里,我将信号发送回主线程,但我该如何反方向呢?

这里的目标是在我希望线程停止时发送一个信号将 self.do 的值更改为 0。

这里是主文件,我会把 UI 文件放在下面

from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtCore import QThread, QObject, pyqtSignal, pyqtSlot
from progressUI import Ui_Form

import sys
import time

class ProgressBar(QObject):
    progress = pyqtSignal(int)
    kill = pyqtSignal()

    def __init__(self, timer, parent=None):
        super(ProgressBar, self).__init__(parent)
        self.time = timer
        self.do = 1

    def work(self):

        while self.do == 1:
            y = 0
            for _ in range(100):
                time.sleep(.1)
                y += 1
                self.progress.emit(y)
            break

        self.kill.emit()

    @pyqtSlot(str)
    def worker_slot(self, sentence):
        print(sentence)

class Go(QMainWindow, Ui_Form, QObject):
    custom_signal = pyqtSignal(str)

    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)
        self.setupUi(self)
        self.progressBar.setValue(0)
        self.startThread()

    @pyqtSlot(int)
    def updateProgress(self, val):
        self.progressBar.setValue(val)
        self.custom_signal.emit('hi from main thread')

    def startThread(self):
        self.progressThread = ProgressBar(60)
        self.thread = QThread()
        self.progressThread.moveToThread(self.thread)

        self.progressThread.progress.connect(self.updateProgress)
        self.progressThread.kill.connect(self.thread.quit)
        self.custom_signal.connect(self.progressThread.worker_slot)
        self.thread.started.connect(self.progressThread.work)

        self.thread.start()



if __name__ == '__main__':
     app = QtWidgets.QApplication(sys.argv)
     MainApp = Go()
     MainApp.show()
     sys.exit(app.exec_())

这是 UI 文件。

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(658, 118)
        self.progressBar = QtWidgets.QProgressBar(Form)
        self.progressBar.setGeometry(QtCore.QRect(30, 40, 601, 23))
        self.progressBar.setProperty("value", 24)
        self.progressBar.setObjectName("progressBar")
        self.label = QtWidgets.QLabel(Form)
        self.label.setGeometry(QtCore.QRect(45, 75, 581, 26))
        self.label.setAlignment(QtCore.Qt.AlignCenter)
        self.label.setObjectName("label")

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
        self.label.setText(_translate("Form", "TextLabel"))

【问题讨论】:

  • 您的代码无法运行。存在拼写错误,我们无法访问您的 UI。
  • 不,它没有运行代码。这是一个示例供您查看。
  • 好的,所以我看了一下,我发现......它不起作用。无论如何,是什么阻止您向工作人员发送信号?
  • 请参阅stackoverflow.com/a/35534047/1994235,但请注意,您不能在工作人员中拥有一个永远循环的函数,因为这会阻止工作人员处理信号。用其他东西替换循环(比如QTimer,但要确保它存在于正确的线程中——那里有一些微妙之处)
  • @squirtgun 我不确定为什么链接不是您要找的。主线程中的信号是带有QPushButtonclicked 信号。随意使用主线程中存在的任何预先存在的信号,或者在位于主线程中的QObject 中定义一个新信号。 slot 是 worker 函数中的一个方法。因此,信号从主线程发出并调用工作函数中的一个槽。这不是你要找的吗?该示例显示了主线程和工作线程之间的双向通信。不确定您还想要什么?

标签: python qt python-3.x pyqt pyqt5


【解决方案1】:

如何向工作线程发送信号?与从工作线程向 GUI 发送信号的方式完全相同。我希望它会更加不同。

@three-pineapples 链接到主线程和工作线程之间双向通信的优秀示例。

如果您想在主 GUI 线程中创建自定义信号,您需要确保继承 QObject,然后才能创建自定义信号。

我在原始帖子中更新了我的代码以包含 UI 文件以便您可以运行它,并且我在 GUI 线程中包含了一个自定义信号的示例,它向工作人员发送信号。

但是,在 for 循环完成之前,您不会看到 print 语句的输出,因为它会阻止 worker 处理信号,正如@three-pineapples 所说的那样。

所以,虽然这不是最好的例子,但希望如果有人在理解这个概念时遇到同样的问题,也许这会有所帮助。

【讨论】:

    【解决方案2】:

    您好,在另一类项目中遇到了同样的问题,见PyQt5 unable to stop/kill QThread

    在这里找到了非常丰富的解决方案/解释:Stopping an infinite loop in a worker thread in PyQt5 the simplest way 并尝试使用第二个建议的解决方案:解决方案 2:将可变变量作为控制变量传递,选择这个,因为我的项目没有无限循环,但是直到一个目录被清空的 for 循环是 subolders/files。

    要了解它是如何工作的,请查看我的结果,应用于上面的代码:

    用户界面文件progressUI_2.py

    from PyQt5 import QtCore, QtGui, QtWidgets
    
    class Ui_Form(object):
        def setupUi(self, Form):
            Form.setObjectName("Form")
            Form.resize(658, 218)
            self.progressBar = QtWidgets.QProgressBar(Form)
            self.progressBar.setGeometry(QtCore.QRect(30, 40, 581, 23))
            self.progressBar.setProperty("value", 24)
            self.progressBar.setObjectName("progressBar")
            self.label = QtWidgets.QLabel(Form)
            self.label.setGeometry(QtCore.QRect(45, 75, 581, 26))
            self.label.setAlignment(QtCore.Qt.AlignCenter)
            self.label.setObjectName("label")
            
            self.button = QtWidgets.QPushButton('press here', Form)
            self.button.setGeometry(QtCore.QRect(30, 115, 581, 26))
            
            self.button.setObjectName("button")
    
            self.retranslateUi(Form)
            QtCore.QMetaObject.connectSlotsByName(Form)
    
        def retranslateUi(self, Form):
            _translate = QtCore.QCoreApplication.translate
            Form.setWindowTitle(_translate("Form", "Form"))
            self.label.setText(_translate("Form", "TextLabel"))
    

    主脚本文件main.py

    from PyQt5 import QtWidgets
    from PyQt5.QtWidgets import QMainWindow
    from PyQt5.QtCore import QThread, QObject, pyqtSignal, pyqtSlot
    from progressUI_2 import Ui_Form
    
    import sys
    import time
    
    class ProgressBar(QObject):
        progress = pyqtSignal(int)
        kill = pyqtSignal()
    
        def __init__(self, timer, ctrl, parent=None):
            super(ProgressBar, self).__init__(parent)
            self.time = timer
            self.do = 1
            
            self.flag = 'off'
            
            self.ctrl = ctrl # dict with your control var
            
    
    
        def work(self):
            
            print('Entered run in worker thread')
            print('id of ctrl in worker:', id(self.ctrl))
            self.ctrl['break'] = False
    
            while self.do == 1:
                y = 0
                for _ in range(100):
                    time.sleep(.1)
                    y += 1
                    self.progress.emit(y)
                    
                    if self.flag == 'on':
                        break
                    # print('flag : ', self.flag, 'self.do : ', self.do)
                    
                    if self.ctrl['break'] == True :
                        break
                    
                break
    
            # self.kill.emit()
            
        @pyqtSlot(str)
        def worker_slot2(self, sentence2):
            self.flag = 'on'
            self.do = 0
            print('from worker !!!!', sentence2, 'self.flag : ', self.flag,'self.do : ', self.do)   
            
    
        @pyqtSlot(str)
        def worker_slot(self, sentence):
            print(sentence)
    
    class Go(QMainWindow, Ui_Form, QObject):
        custom_signal = pyqtSignal(str)
        button_signal = pyqtSignal(str)
    
        def __init__(self, parent=None):
            QMainWindow.__init__(self, parent)
            self.setupUi(self)
            self.progressBar.setValue(0)
            
            
            
            self.button.clicked.connect(self.clicked)
            
            self.ctrl = {'break': False} # dict with your control variable
            print('id of ctrl in main:', id(self.ctrl))
            
            self.startThread()
            
        def clicked(self):
            print('clicked')
            self.button_signal.emit('emitted from go ')
            self.flag = 'on'
            self.do = 0
            # self.ctrl['break'] = True
    
        @pyqtSlot(int)
        def updateProgress(self, val):
            self.progressBar.setValue(val)
            self.custom_signal.emit('hi from main thread')
    
        def startThread(self):
            self.progressThread = ProgressBar(60, self.ctrl)
            self.thread = QThread()
            self.progressThread.moveToThread(self.thread)
    
            self.progressThread.progress.connect(self.updateProgress)
            self.progressThread.kill.connect(self.thread.quit)
            self.custom_signal.connect(self.progressThread.worker_slot)
            
            self.thread.started.connect(self.progressThread.work)
            
            self.button_signal.connect(self.progressThread.worker_slot2)
            
            
            self.thread.start()
    
    
    
    if __name__ == '__main__':
         app = QtWidgets.QApplication(sys.argv)
         MainApp = Go()
         MainApp.show()
         sys.exit(app.exec_())
    

    尝试运行main.py

    注释掉和取消注释self.ctrl['break'] = True

    在:

     def clicked(self):
            print('clicked')
            self.button_signal.emit('emitted from go ')
            self.flag = 'on'
            self.do = 0
            # self.ctrl['break'] = True
    

    看看你是否能够停止按下 QBButton 的进度条,

    请注意如何在代码的各个部分更改 self.doself.flag 被证明是不成功的,不确定是否只是因为我应该将它们传递给工作人员

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-04-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-25
      • 2018-04-29
      相关资源
      最近更新 更多