PyQt5中使用multiprocessing多进程模块无法绘制图表问题
PyQt5中使用multiprocessing模块无法绘制图表的解决方案
问题根源
你遇到的问题由三个核心原因导致:
- 进程内存空间隔离:multiprocessing创建的子进程拥有完全独立的内存地址空间,子进程中访问到的
self.chart、self._chart_view等Qt UI对象都是主进程对象的拷贝,你在子进程中对这些对象的所有修改都只会作用于子进程内的副本,完全不会同步到主进程的实际界面上 - UI线程被阻塞:你在
__init__方法中直接调用proc.join(),会导致主进程的UI线程一直阻塞等待所有子进程执行完成,后续的窗口显示、事件循环启动逻辑完全无法执行,界面会直接处于假死状态 - 违反Qt GUI操作规范:Qt的所有GUI相关类的操作都只能在主进程的主线程(UI线程)中执行,在子进程中操作GUI对象本身就是未定义行为,即使没有内存隔离也会触发崩溃
正确实现思路
核心原则是计算逻辑与UI操作完全分离:
- 子进程只负责执行纯数据计算,不碰任何Qt UI相关类
- 使用
multiprocessing.Queue等进程间通信工具,将子进程计算完成的结果传递回主进程 - 主进程在自己的UI线程中接收数据,再执行图表绘制操作
修改后可运行代码
import numpy as np from PyQt5 import QtWidgets, QtChart from PyQt5.QtCore import QTimer, Qt from multiprocessing import Process, Queue import time # 子进程计算逻辑:纯数据处理,不涉及任何UI操作 def calculate_data(cou, queue): print(cou) time.sleep(3) U1 = np.random.random(500) U2 = np.random.random(500) # 仅把计算得到的原始数据和标记传回主进程 queue.put((cou, U1, U2)) class MainWindow(QtWidgets.QMainWindow): def __init__(self, data_queue, parent=None): super().__init__(parent) self.data_queue = data_queue self._chart_view = QtChart.QChartView() self.chart = QtChart.QChart() central_widget = QtWidgets.QWidget() self.setCentralWidget(central_widget) lay = QtWidgets.QVBoxLayout(central_widget) lay.addWidget(self._chart_view) self._chart_view.setChart(self.chart) # 启动子进程 self.procs = [] for i in range(3): proc = Process(target=calculate_data, args=(i, self.data_queue)) self.procs.append(proc) proc.start() # 启动定时器轮询队列,接收子进程返回的数据 self.poll_timer = QTimer() self.poll_timer.timeout.connect(self.check_data_queue) self.poll_timer.start(100) # 每100ms检查一次队列 def check_data_queue(self): # 所有子进程已退出且队列无数据,停止轮询 if not any(p.is_alive() for p in self.procs) and self.data_queue.empty(): self.poll_timer.stop() return # 处理队列中所有可用数据 while not self.data_queue.empty(): cou, U1, U2 = self.data_queue.get() # UI绘制操作全部在主进程中执行 scatterU = QtChart.QScatterSeries() if cou == 0: scatterU.setColor(Qt.darkGreen) elif cou == 1: scatterU.setColor(Qt.darkBlue) elif cou == 2: scatterU.setColor(Qt.red) scatterU.setMarkerSize(10.0) for x, y in zip(U1, U2): scatterU.append(x, y) self.chart.addSeries(scatterU) self.chart.createDefaultAxes() if __name__ == "__main__": import sys # 队列必须在if __name__ == "__main__"块中创建,适配Windows spawn进程模式 data_queue = Queue() app = QtWidgets.QApplication(sys.argv) w = MainWindow(data_queue) w.resize(800, 600) w.show() sys.exit(app.exec_())
核心修改说明
- 拆分了计算逻辑与UI逻辑,子进程仅负责生成随机数数组,完全不涉及Qt UI类操作
- 使用
multiprocessing.Queue实现跨进程数据传递,解决内存隔离导致的数据不互通问题 - 移除了
__init__中的阻塞join调用,主进程UI可以正常启动渲染 - 使用QTimer轮询队列接收子进程数据,所有UI绘制操作都在主进程UI线程执行,符合Qt的操作规范
- 适配Windows系统的进程创建模式,避免序列化异常
内容的提问来源于stack exchange,提问作者inquirer
相关产品推荐
相关产品推荐

