You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyQt5中使用multiprocessing多进程模块无法绘制图表问题

PyQt5中使用multiprocessing模块无法绘制图表的解决方案

问题根源

你遇到的问题由三个核心原因导致:

  • 进程内存空间隔离:multiprocessing创建的子进程拥有完全独立的内存地址空间,子进程中访问到的self.chart、self._chart_view等Qt UI对象都是主进程对象的拷贝,你在子进程中对这些对象的所有修改都只会作用于子进程内的副本,完全不会同步到主进程的实际界面上
  • UI线程被阻塞:你在__init__方法中直接调用proc.join(),会导致主进程的UI线程一直阻塞等待所有子进程执行完成,后续的窗口显示、事件循环启动逻辑完全无法执行,界面会直接处于假死状态
  • 违反Qt GUI操作规范:Qt的所有GUI相关类的操作都只能在主进程的主线程(UI线程)中执行,在子进程中操作GUI对象本身就是未定义行为,即使没有内存隔离也会触发崩溃

正确实现思路

核心原则是计算逻辑与UI操作完全分离:

  1. 子进程只负责执行纯数据计算,不碰任何Qt UI相关类
  2. 使用multiprocessing.Queue等进程间通信工具,将子进程计算完成的结果传递回主进程
  3. 主进程在自己的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 12:39:03