简体   繁体   English

PyQt5:如何向工作线程发送信号

[英]PyQt5: How to send a signal to a worker thread

I know how to send signals from worker threads back to the main GUI thread, but how can I send signals from the main thread to the worker thread?我知道如何将信号从工作线程发送回主 GUI 线程,但如何将信号从主线程发送到工作线程?

Here's some sample code which includes a signal and slot.这是一些包含信号和槽的示例代码。 Here I'm sending signals back to the main thread, but how can I go in the opposite direction?在这里,我将信号发送回主线程,但是我怎样才能在相反的方向上发送 go 呢?

The goal here being to send a signal to change the value of self.do to 0 when I want the thread to stop.这里的目标是发送信号以在我希望线程停止时将 self.do 的值更改为 0。

Here's the main file and I'll put the UI file below这是主文件,我将把 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_())

Here's the UI file.这是用户界面文件。

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"))

How do you send a signal to a worker thread? 你如何向工人线程发送信号? Exactly the same way as sending a signal from a worker thread to the GUI. 与从工作线程向GUI发送信号的方式完全相同。 I expected it to be more different. 我预计它会更加不同。

@three-pineapples linked to an excellent example of bi-directional communication between the main thread and a worker thread. @ three-pineapples链接到主线程和工作线程之间的双向通信的一个很好的例子。

If you want to create a custom signal in the main GUI thread, you need to make sure you inherit QObject and then you'll be able to create custom signals. 如果要在主GUI线程中创建自定义信号,则需要确保继承QObject,然后才能创建自定义信号。

I updated my code in the original post to include the UI file so you can run it, and I included an example of a custom signal in the GUI thread that sends a signal to the worker. 我在原始帖子中更新了我的代码以包含UI文件以便您可以运行它,并且我在GUI线程中包含了一个自定义信号的示例,该信号向工作人员发送信号。

However you will not see the output of the print statement until the for loop has finished as it blocks the worker from processing signals, as @three-pineapples also stated. 但是,在for循环结束之前你不会看到print语句的输出,因为它阻止了worker处理信号,就像@ three-pineapples所说的那样。

So, although it's not the best example, hopefully if someone is having the same trouble understanding the concept, maybe this will help. 所以,虽然这不是最好的例子,但希望如果有人在理解这个概念时遇到同样的麻烦,也许这会有所帮助。

Hi encountered the same proble on another type of project, see PyQt5 unable to stop/kill QThread ,您好在另一种类型的项目中遇到了同样的问题,请参阅PyQt5 unable to stop/kill QThread

found very informative solution/explanation here: Stopping an infinite loop in a worker thread in PyQt5 the simplest way and tried to use the second proposed solution: Solution 2: Passing a mutable as a control variable , choose this one because my project doesnt have an infinite loop, but a for loop running until a directory is emptied of all is subolders/files.在这里找到了非常有用的解决方案/解释: Stopping an infinite loop in a worker thread in PyQt5 最简单的方法并尝试使用第二个建议的解决方案:解决方案 2:将可变变量作为控制变量传递,选择这个,因为我的项目没有无限循环,但是一个 for 循环运行直到一个目录被清空所有是 subolders/files。

To try to understand how it works see my result, applied to code above:要尝试了解它是如何工作的,请查看我的结果,将其应用于上面的代码:

UI file progressUI_2.py :用户界面文件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 script file main.py :主脚本文件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_())

try to run the main.py尝试运行main.py

commenting out and uncommenting self.ctrl['break'] = True注释掉和取消注释self.ctrl['break'] = True

in:在:

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

and see if you are able to stop the progress bar pushing the QBButton,看看你是否能够停止按下 QBButton 的进度条,

notice how trying to change self.do and self.flag in various part of the code prove to be unsuccessful, not sure if it just because I should have passed them to the worker请注意如何尝试更改代码的各个部分中的self.doself.flag被证明是不成功的,不确定是否只是因为我应该将它们传递给工作人员

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM