本帖最后由 wutljs 于 2023-12-2 19:44 编辑
讨论pyqt5的中线程通信
背景
在使用pyqt5编写GUI程序时,我们可能有时会需要使用多线程来处理问题,这就可能会涉及到线程之间相互通信的问题。比如接下来要解决的进度条问题。
解决思路
我们可以通过pyqt5的信号与槽机制来完成线程之间的通信。
代码编写
import sys, time
from PyQt5.QtCore import QRect, QThread, pyqtSignal
from PyQt5.QtWidgets import QDialog, QProgressBar, QApplication
class UiProgressBarDialog:
"""主线程类"""
def __init__(self, dialog):
"""做一些简单的初始化工作"""
self.dialog = dialog
self.dialog.setFixedSize(480, 360)
self.progressbar = QProgressBar(self.dialog)
self.progressbar.setGeometry(QRect(80, 80, 351, 31))
self.progressbar.setValue(0)
def add_task_thread(self):
"""添加子线程,并建立线程通信(重头戏)"""
task_thread = TaskThread()
task_thread.update_signal.connect(self.update_progressbar)
task_thread.start()
self.dialog.exec_()
def update_progressbar(self, progressbar_value):
"""槽: 用来接收来自子线程(任务线程)发射的信号,并作出响应(更新进度条)"""
self.progressbar.setValue(progressbar_value)
class TaskThread(QThread):
"""子线程类"""
update_signal = pyqtSignal(int)
task_total_num = 1000
task_finished_num = 0
def do_task(self):
self.task_finished_num += 1
self.update_signal.emit(
self.task_finished_num / self.task_total_num * 100)
def run(self):
for i in range(self.task_total_num):
self.do_task()
time.sleep(0.001)
if __name__ == '__main__':
app = QApplication(sys.argv)
ui = UiProgressBarDialog(QDialog())
ui.add_task_thread()
运行结果
结论
当我们使用pyqt5进行可视化编程时,或许我们总能碰到主线程卡死的情况。除了代码编写的有问题,有一种可能就是有一些非常耗时、占用资源的代码在运行时阻塞着主线程,故此时需要使用多线程的方法来解决问题。而在代码中,我们看到使用信号与槽这一机制可以很好地完成线程之间的通信问题。
展望
显然,这个程序有着很强的可扩展性:
- 将TaskThread的run方法抽离出来,把这个进度条代码变成装饰器,可以很方便的看到被修饰代码运行进度。
- 这段代码可以与异步协程结合。使用协程解决的问题一般都是任务量比较大的问题,正好可以用进度条来记录当前任务进度。不过如果真有此打算的话,代码还需要补充一些必要的东西,碰巧笔者有一些经验,欢迎探讨。
- 将这个进度条的UI使用html、js或者css润色加工一下,放在项目中感觉也还可以。
除此以外,信号与槽机制的应用显然不止这些,感兴趣的朋友可以自行探索~
参考文章
PyQt5——信号与槽函数
pyqt5多线程模块QThread的使用方法
|