需求是这样的:主线程需要把一些耗时操作放在子线程中执行,子线程执行完毕后通知主线程更新 UI。但是子线程只有一个,且耗时操作必须一个一个来,不能并发执行。怎么让子线程挨个执行主线程下发的命令呢?
解决办法就是:主线程把需要执行的命令放在一个队列里,子线程不断去读取队列里的命令,然后挨个读取出来执行,执行完毕之后通知主线程更新 UI。在这里,主线程相当于生成者(生产命令),子线程相当于消费者(消费命令)。在这里,只有一个生产者和一个消费者,是最简单的生产者消费者模型。
我这里有个样例程序,点击发送就是向子线程发送命令,子线程获取到命令后就开始执行,然后发信号通知主线程更新 UI。
完整代码如下:
import datetime
from PySide2.QtCore import QThread, QObject, Signal, Slot
from PySide2.QtWidgets import QApplication, QInputDialog, QDialog, QLineEdit, QPushButton, QVBoxLayout, QLabel
import util
class MyInputDialog(QDialog):
def __init__(self):
super().__init__()
#
# 输入框
#
self.input = QLineEdit()
#
# label 标签
#
self.label_1 = QLabel()
#
# label_2
#
self.label_2 = QLabel()
#
# 发送按钮
#
self.ok_btn = QPushButton(text="发送")
self.ok_btn.clicked.connect(self.submit)
layout = QVBoxLayout()
layout.addWidget(self.input)
layout.addWidget(self.label_1)
layout.addWidget(self.label_2)
layout.addWidget(self.ok_btn)
self.setLayout(layout)
@Slot(str)
def update_label1(self, result):
self.label_1.setText("更新标签成功:" + result)
@Slot(str)
def update_label2(self, result):
self.label_2.setText("更新标签成功:" + result)
def submit(self):
msg = self.input.text()
message.append(msg)
class Worker(QThread):
def __init__(self, parent):
super().__init__()
self.signals = Communicate()
self.signals.label_singal.connect(parent.update_label1)
self.signals.table_singal.connect(parent.update_label2)
def run(self):
while True:
if len(message) != 0:
msg = message.pop(0)
if msg == "label_1":
self.get_label1()
elif msg == "label_2":
self.get_label2()
def get_label1(self):
#
# 模拟耗时操作,去获取数据
#
self.signals.label_singal.emit(get_time())
def get_label2(self):
self.signals.table_singal.emit(get_time())
class Communicate(QObject):
label_singal = Signal(str)
table_singal = Signal(str)
def get_time() -> str:
"""
获取当前时间,返回时间字符串,如:2020-06-12 11:13:22
:return:时间字符串
"""
now_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
return now_time
#
# 消息队列,全局变量
#
message = []
if __name__ == '__main__':
app = QApplication()
#
# 显示输入框
#
dial = MyInputDialog()
dial.show()
worker = Worker(dial)
worker.start()
app.exec_()