讨论GUI库pyqt5的中线程通信问题
本帖最后由 wutljs 于 2023-12-2 19:44 编辑# 讨论pyqt5的中线程通信
## 背景
在使用pyqt5编写GUI程序时,我们可能有时会需要使用多线程来处理问题,这就可能会涉及到线程之间相互通信的问题。比如接下来要解决的进度条问题。
## 解决思路
我们可以通过pyqt5的**信号与槽机制**来完成线程之间的通信。
## 代码编写
```python
# Copyright (C) 2023 - 2023 wutljs, Inc. All Rights Reserved
# @Time: 2023/12/2 10:12
# @Author: wutljs
# @File: item_1.py
# @IDE: PyCharm
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# 获得空dialog
self.dialog.setFixedSize(480, 360)# 设置dialog尺寸
self.progressbar = QProgressBar(self.dialog)# 添加进度条
self.progressbar.setGeometry(QRect(80, 80, 351, 31))# 进设置度条尺寸
self.progressbar.setValue(0)# 设置进度初始值为0
def add_task_thread(self):
"""添加子线程,并建立线程通信(重头戏)"""
task_thread = TaskThread()# 添加任务子线程(任务线程)
task_thread.update_signal.connect(self.update_progressbar)# 连接子线程(任务线程)信号与主线程的槽,搭建线程通信
task_thread.start()# 开启子线程(任务线程)
self.dialog.exec_()# dialog开始生效(显示)
def update_progressbar(self, progressbar_value):
"""槽: 用来接收来自子线程(任务线程)发射的信号,并作出响应(更新进度条)"""
self.progressbar.setValue(progressbar_value)
class TaskThread(QThread):
"""子线程类"""
update_signal = pyqtSignal(int)# 信号: 通知主线程更新进度条的信号
task_total_num = 1000# 假设任务总量为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润色加工一下,放在项目中感觉也还可以。
除此以外,**信号与槽**机制的应用显然不止这些,感兴趣的朋友可以自行探索~
## 参考文章
(https://blog.csdn.net/huayunhualuo/article/details/102718509)
(https://www.cnblogs.com/linyfeng/p/12239856.html) lookfeiji 发表于 2023-12-4 10:57
不对,不是线程问题,是多线程的机制与通信的问题,tkinter没提供多线程的方法,python的多线程都被说是 ...
确实,python因为GIL缘故不像java那样可以搞多线程(Hadoop等框架都是java写的)。
pyqt5可以解决您的问题,查相关文档即可~ 最近刚好在学习这个,支持你一波{:1_918:} 一个时间倒数子线程CPU占用过高,看看有没有帮助 aqin5014 发表于 2023-12-3 10:34
最近刚好在学习这个,支持你一波
谢谢啦,祝你顺利!
本帖最后由 wutljs 于 2023-12-3 20:30 编辑
Cacarot 发表于 2023-12-3 17:54
一个时间倒数子线程CPU占用过高,看看有没有帮助
嗯~这篇文章的逻辑是将耗时任务放在子线程,防止主线程卡死。线程间交互的方式为信号与槽机制~
如果该耗时任务真的很棘手,建议使用节点分布式等手段完成。 收下代码先,我在tkinter上也碰到了这个问题,但是遗憾的是我没找到解决办法,最后放弃用进度条了 lookfeiji 发表于 2023-12-4 08:36
收下代码先,我在tkinter上也碰到了这个问题,但是遗憾的是我没找到解决办法,最后放弃用进度条了
嗯,之前我尝试使用threading开线程作为子线程,但依然使主线程处于假死的状态。后来使用pyqt5中特有的线程通信机制才解决了问题。tkinter没怎么接触过,不过您可以试试tkinter提供的多线程方法? wutljs 发表于 2023-12-4 09:14
嗯,之前我尝试使用threading开线程作为子线程,但依然使主线程处于假死的状态。后来使用pyqt5中特有的线 ...
谢谢,你这给了我一个很好的提示,我问gpt他说tkinter没有多线程方法,但是我在百度居然搜到了,研究研究去 wutljs 发表于 2023-12-4 09:14
嗯,之前我尝试使用threading开线程作为子线程,但依然使主线程处于假死的状态。后来使用pyqt5中特有的线 ...
不对,不是线程问题,是多线程的机制与通信的问题,tkinter没提供多线程的方法,python的多线程都被说是假的,主要没法实现线程与线程之间的通信,还有就是基本的线程关闭,以及线程与主线程之间的调度都有点难搞
页:
[1]