PyQt异步任务进度条完整方案:多线程任务处理与平滑进度展示

文章目录

  • 简介
  • 业务上设计
    • 结果集设计
    • 业务设计
  • 线程类设计
    • worker类
  • 进度条的设计
    • Qtimer创建
    • QProgressBar的更新方法
    • QProgressBar的启动方法
    • QProgressBar的初始化方法
    • QProgressBar的成功方法
    • QProgressBar的停止方法
  • 按钮交互流程
    • 按钮绑定运行方法
    • 运行方法
    • worker 成功后处理
    • worker失败后处理
    • 任务前准备工作
    • 任务执行完清除工作
  • 总结

简介

这是一个基于PyQt异步任务进度展示解决方案,主要解决在GUI应用中执行耗时任务时的。


主要解决两个问题:

  1. 如何避免耗时任务导致界面卡死
  2. 如何平滑展示任务进度

核心设计包括:

  • 统一的结果集封装
  • 基于QThread的异步任务处理
  • 进度回调机制
  • 平滑的进度条展示

优势:

  • 可复用性强,支持各类耗时任务
  • 界面响应流畅
  • 进度展示平滑自然
  • 异常处理完善

适用于各类需要执行耗时操作的桌面应用场景,如文件处理、数据分析、网络请求等。



业务上设计

  1. 结果集设计

  2. 业务回调函数设计

结果集设计

方便后续统一处理结果

  • 成功:res=Result.ok(data) 或者res=Result.ok()
  • 失败 :res=Result.fail(error_msg)
# 定义一个结果集
class Result:
    def __init__(self, success: bool, data: Any = None, error: str = ""):
        self.success = success
        self.data = data
        self.error = error

    @classmethod
    def ok(cls, data: Any = None) -> 'Result':
        return cls(True, data)

    @classmethod
    def fail(cls, error: str) -> 'Result':
        return cls(False, error=error)

业务设计

  • 回调函数参数:max_value ,estimate_time
    • max_value :这个阶段的进度最大值
    • estimate_time:这个阶段完成的预估时间
  • 返回类型:Result
  • 进度由业务(后端)自己添加,有多少个阶段就调用几次,可自行定义某阶段的进度最大值和预估时间。
  • 如果就一个阶段的耗时事件,就直接 progress_callback(100,15000)
# service.py
def service_function(progress_callback):
    
    ''' 执行业务,并带有进度回调函数,函数会传入两个参数,(该进度的最大值,预估时间)max_value, estimate_time '''
	
    progress_callback(101000)
	# 执行阶段1 
    # ....
    
    progress_callback(505000)
	# 执行阶段2 
    # ....
    
    progress_callback(10015000)
	# 执行阶段3
    # ....
    
    # 成功
    res=Result.ok(data)
    
    # 失败
    res=Result.fail(error_msg)
    
    return res



线程类设计

在ptqy种,耗时任务可以使用Qthread去执行,需要注意的是:

  • 写一个worker类去继承Qthread,把要执行的耗时任务传进去,这样就可以统一管理类似的任务了,不需要一个任务一个Qthread类
  • 这是一个Qthread类型的类,在worker类里我们**!!不做ui相关的操作!!不做ui相关的操作!!不做ui相关的操作!!**(重要的事情说三遍)因此我们利用信号与槽机制把回调函数给的参数传出去。
worker干什么?
它在子线程执行耗时任务,利用信号与槽机制和主线程通信。
执行的是什么类型的任务?
耗时,可以传回调函数(上面设计的那种),返回的结果是Result类型的。
worker怎么和主线程通信?
把任务执行的结果利用信号与槽发送出去,把任务中调用回调函数开启进度条的信号也发送出去。

worker类

 class Worker(QThread):
    success = pyqtSignal()
    error = pyqtSignal(str)  # 错误信号
    start_progress = pyqtSignal(int, int)  # 开始进度条信号(一个是max值,一个是预计时间)

    def __init__(self, function):
        super().__init__()
        self.function = function

    def run(self):
        try:
            # 开始执行-------------
            res = self.function(self.progress_callback)
            if res.success:
                # 结束:------------
                self.success.emit()  # 发生结束信号
            else:
                self.error.emit(res.error)
        except Exception as e:
            # 统一异常处理 .....
            
   # 定义回调函数,直接把信号发出去,让主线程去处理  
   def progress_callback(self, max_value, estimated_time):
        """
            max_value: 当前阶段的最大占比 例子:10(最多是100)
            estimated_time: 这个阶段的预计时间 例子:1000ms
        """
        self.start_progress.emit(max_value, estimated_time) # 发送信号到外头



进度条的设计

  1. QProgressBar:显示进度
  2. Qtimer: 用于不断缓慢更新ui进度条的进度
  3. 定义QProgressBar的更新方法:QProgressBar当前值+1 (除非QProgressBar当前值 == max_value - 1,则Qtimer:stop)
  4. 定义QProgressBar的启动方法:通过max_value 和 estimated_time,计算timeout间隔并设置。Qtimer:start
  5. 定义QProgressBar的初始化方法:QProgressBar:0
  6. 定义QProgressBar的成功方法:Qtimer:stop ,QProgressBar:100
  7. 定义QProgressBar的停止方法:Qtimer:stop

代码如下:

Qtimer创建

# MyWindow - init方法

# 设置定时器用于更新进度条
self.timer_progress = QTimer(self)
self.timer_progress.timeout.connect(self.update_progress_ui)
self._progress_max_value = 0  # 记录进度条的最大值

QProgressBar的更新方法

# MyWindow.py
def update_progress_ui(self):
    """更新进度条UI"""
    try:
        current_value = self.customProgressBar.value()
        next_value = min(current_value + 1, self._progress_max_value -1)

        self.customProgressBar.setValue(next_value)

        # 检查是否需要停止定时器
        if next_value >= self._progress_max_value - 1:
            self.timer_progress.stop()

    except Exception as e:
        self.logger.error(f"更新进度条失败: {e}")
        self.timer_progress.stop()


QProgressBar的启动方法

# MyWindow.py
def start_progress_from_current_progress(self, max_value, estimated_time):
    """启动进度定时器"""
    try:
        current_value = self.customProgressBar.value()
        if current_value >= max_value:
            return

        # 停止现有定时器
        if self.timer_progress.isActive():
            self.timer_progress.stop()

        # 设置进度参数
        self._progress_max_value = max_value
        remaining_progress = max_value - current_value
        if remaining_progress <= 0:
            return

        # 计算间隔时间
        interval_time = max(1, int(estimated_time / remaining_progress))

        # 启动定时器
        self.timer_progress.setInterval(interval_time)
        self.timer_progress.start()

    except Exception as e:
        self.logger.error(f"启动进度定时器失败: {e}")
        self.show_error_ui("Progress timer error")

QProgressBar的初始化方法

# MyWindow.py
def init_progress_ui(self):
	# 其他 ui 操作....
    self.customProgressBar.setValue(0)


QProgressBar的成功方法

# MyWindow.py
def success_progress_ui(self):
    self.timer_progress.stop()
    self.customProgressBar.setValue(100)

QProgressBar的停止方法

# MyWindow.py
def stop_progress_ui(self):
    self.timer_progress.stop()



按钮交互流程

  1. 页面按钮绑定任务执行

  2. 运行方法:

    1. 执行任务前准备(禁用按钮,进度条初始化)
    2. 创建worker,传入要做的耗时业务/任务
    3. 绑定worker的信号
      • success:进度条成功,执行任务后处理(启动按钮)
      • error:提示失败信息,进度条停止,执行任务后处理(启动按钮)
      • start_progress:开启进度条
    4. worker开始

代码如下:

按钮绑定运行方法

# MyWindow - init方法
self.btn.clicked.connect(self.run_function)

运行方法

# MyWindow.py
def run_function(self):
    # 任务执行前处理
    self.pre_process()
    
    # 创建worker  !!这里的任务是后端业务来的!!
    worker = Worker(service_function)  
    
    # 连接worker信号
    worker.success.connect(self.worker_success_handle)
    worker.error.connect(lambda msg: self.worker_error_handle(msg))
    worker.start_progress.connect(self.start_progress_from_current_progress)

    # 开始执行业务
    worker.start()

worker 成功后处理

# MyWindow.py
def worker_success_handle(self):
    try:
        # 进度条成功
        self.success_progress_ui()

        # 清理工作
        self.post_process()
            
	except Exception as e:
		self.show_error_ui(str(e))

worker失败后处理

# MyWindow.py
def worker_error_handle(self, error_message):
    try:
        # 显示错误
        self.show_error_ui(error_message)
        
        # 进度条停止
        self.stop_progress()

        # 清理工作
        self.post_process()
        
	except Exception as e:
		# 确保最基本的错误提示能显示
		print(f"Error handling failed: {str(e)}")

任务前准备工作

# MyWindow.py
def pre_process(self):
    # 禁用按钮
    self.disable_buttons()

    # 进度条初始话
    self.init_progress_ui()
    
    # 其他...

任务执行完清除工作

# MyWindow.py
def post_process(self):
    # 停止计时器
    if self.timer_progress.isActive():
        self.timer_progress.stop()

	# 启用按钮
	self.enable_buttons()
    
    # 其他...



总结


整个框架设计的核心思想:

  1. 业务层专注于任务处理逻辑,通过回调函数报告进度
  2. 线程层负责异步执行和信号传递,确保界面响应
  3. 界面层关注进度展示效果,实现流畅的动画效果

希望能帮助到大家!!!!

你可能感兴趣的:(pyqt,python)