PyQt5:如何向工作线程发送信号
Posted
技术标签:
【中文标题】PyQt5:如何向工作线程发送信号【英文标题】:PyQt5: How to send a signal to a worker thread 【发布时间】: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。 不,它没有运行代码。这是一个示例供您查看。 好的,所以我看了一下,我发现......它不起作用。无论如何,是什么阻止您向工作人员发送信号? 请参阅***.com/a/35534047/1994235,但请注意,您不能在工作人员中拥有一个永远循环的函数,因为这会阻止工作人员处理信号。用其他东西替换循环(比如QTimer
,但要确保它存在于正确的线程中——那里有一些微妙之处)
@squirtgun 我不确定为什么链接不是您要找的。主线程中的信号是带有QPushButton
的clicked
信号。随意使用主线程中存在的任何预先存在的信号,或者在位于主线程中的QObject
中定义一个新信号。 slot 是 worker 函数中的一个方法。因此,信号从主线程发出并调用工作函数中的一个槽。这不是你要找的吗?该示例显示了主线程和工作线程之间的双向通信。不确定您还想要什么?
【参考方案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.do
和 self.flag
被证明是不成功的,不确定是否只是因为我应该将它们传递给工作人员
【讨论】:
以上是关于PyQt5:如何向工作线程发送信号的主要内容,如果未能解决你的问题,请参考以下文章