如何从进程池向PySide6 Qt GUI实时发送消息更新进度
问题根源
你的代码核心问题在于:点击按钮后主线程直接调用了do_stuff(),而multiprocessing.Pool.map()是阻塞方法,会一直等待所有子进程任务完成才返回。这导致主线程被彻底卡住,无法及时处理UI更新信号,队列中的消息只能在所有任务结束后一次性被批量处理。
解决方案
将进程池的执行逻辑移到独立的后台QThread中,让主线程专注处理UI事件;同时优化进度更新的显示方式,确保实时性。
修改后的完整代码
模块文件(other_module.py)
如果是实际FTP下载,可在foo中替换为真实的下载逻辑并实时计算进度:
import os import multiprocessing import time shared_queue = multiprocessing.Queue() class MyProcess: def foo(self, file_info): # 模拟FTP下载任务,实际场景替换为真实下载逻辑 file_name, total_size = file_info pid = os.getpid() downloaded = 0 # 实时上报下载进度 while downloaded < total_size: downloaded += 1024 * 100 # 模拟每次下载100KB progress = (downloaded / total_size) * 100 output = f"进程 {pid} | {file_name}: {progress:.1f}%" shared_queue.put(output) time.sleep(0.1) # 模拟网络延迟 # 上报下载完成状态 output = f"进程 {pid} | {file_name} 下载完成" shared_queue.put(output) print(output) def do_stuff(self): pool_size = 4 # 模拟4个待下载文件,实际场景替换为FTP服务器文件列表 files = [ ("file1.txt", 1024*1024), ("file2.txt", 1024*1024), ("file3.txt", 1024*1024), ("file4.txt", 1024*1024) ] p = multiprocessing.Pool(pool_size) p.map(self.foo, files)
主模块文件
import sys from PySide6.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QVBoxLayout from PySide6.QtCore import QThread, Signal from other_module import MyProcess, shared_queue class QueueListenerThread(QThread): """监听进程间队列,将进度信号传递给主线程""" update_signal = Signal(str) def run(self): while True: try: text = shared_queue.get(block=True) self.update_signal.emit(text) except Exception: # 处理队列异常(如队列被关闭) break class ProcessPoolThread(QThread): """在后台线程执行进程池任务,避免阻塞主线程""" def run(self): task = MyProcess() task.do_stuff() class Window(QWidget): def __init__(self): super().__init__() self.label = QLabel("等待任务开始...") self.button = QPushButton("启动下载") # 布局设置 layout = QVBoxLayout() layout.addWidget(self.label) layout.addWidget(self.button) self.setLayout(layout) # 初始化并启动队列监听线程 self.queue_thread = QueueListenerThread() self.queue_thread.update_signal.connect(self.update_label) self.queue_thread.start() # 初始化进程池线程 self.pool_thread = ProcessPoolThread() self.pool_thread.finished.connect(lambda: self.button.setEnabled(True)) # 绑定按钮点击事件 self.button.clicked.connect(self.start_task) def update_label(self, text): """实时更新UI显示,累加进度信息""" current_text = self.label.text() new_text = f"{current_text}\n{text}" if current_text != "等待任务开始..." else text self.label.setText(new_text) def start_task(self): """启动下载任务,防止重复点击""" if not self.pool_thread.isRunning(): self.label.setText("任务启动中...") self.button.setEnabled(False) self.pool_thread.start() if __name__ == "__main__": app = QApplication([]) window = Window() window.show() sys.exit(app.exec())
关键修改点说明
- 新增
ProcessPoolThread:把进程池的执行逻辑移到后台线程,彻底避免主线程被阻塞,确保UI能实时响应信号。 - 队列监听线程独立:保持
QueueListenerThread专注于监听进程间队列,将进度信号传递给主线程处理。 - UI更新优化:改为累加显示进度信息,方便查看所有文件的下载状态;移除不必要的
QApplication.processEvents(),主线程会自动处理UI更新。 - 按钮状态控制:任务启动后禁用按钮,任务结束后恢复,防止重复触发任务。
内容的提问来源于stack exchange,提问作者Jan Donal
相关产品推荐
相关产品推荐

