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

如何在Qt窗口中实时绘制Matplotlib颜色图?

实现Qt中Matplotlib颜色图的实时更新

核心思路

实时更新颜色图的关键是复用已创建的imshow对象,仅更新数据而非重新绘制整个图像,这样能避免UI卡顿,提升实时性。


1. 初始化阶段:创建并保存颜色图引用

在Qt窗口初始化时,一次性创建3个子图的imshow对象并保存引用,后续更新仅操作这些对象:

import numpy as np
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import matplotlib.pyplot as plt
from PyQt5.QtWidgets import QWidget, QVBoxLayout

class MainWindow(QWidget):
    def __init__(self, x_values, y_values, parent=None):
        super().__init__(parent)
        self.x_vals = x_values
        self.y_vals = y_values
        x_min, x_max = min(x_values), max(x_values)
        y_min, y_max = min(y_values), max(y_values)
        
        # 初始化第一个画布(3个颜色图)
        self.fig_cmaps, (self.ax1, self.ax2, self.ax3) = plt.subplots(1, 3, figsize=(12, 4))
        self.canvas_cmaps = FigureCanvas(self.fig_cmaps)
        
        # 初始化z数据(与x、y维度匹配的二维数组)
        self.z1 = np.zeros((len(self.y_vals), len(self.x_vals)))
        self.z2 = np.zeros_like(self.z1)
        self.z3 = np.zeros_like(self.z1)
        
        # 创建imshow对象并保存引用
        self.im1 = self.ax1.imshow(self.z1, extent=[x_min, x_max, y_min, y_max], cmap='viridis')
        self.im2 = self.ax2.imshow(self.z2, extent=[x_min, x_max, y_min, y_max], cmap='plasma')
        self.im3 = self.ax3.imshow(self.z3, extent=[x_min, x_max, y_min, y_max], cmap='inferno')
        
        # 添加颜色条(初始化一次即可)
        self.fig_cmaps.colorbar(self.im1, ax=self.ax1)
        self.fig_cmaps.colorbar(self.im2, ax=self.ax2)
        self.fig_cmaps.colorbar(self.im3, ax=self.ax3)
        
        # 布局设置(这里省略第二个折线图画布的初始化,按你的现有代码保留即可)
        layout = QVBoxLayout()
        layout.addWidget(self.canvas_cmaps)
        # layout.addWidget(self.canvas_lineplot)  # 你的实时折线图画布
        self.setLayout(layout)

2. 实时更新逻辑:更新数据并触发重绘

当从微处理器获取到某个x、y点对应的z值后,更新z数组并刷新颜色图:

def update_colormaps(self, x_idx, y_idx, z1_val, z2_val, z3_val):
    # 更新z数组的对应位置
    self.z1[y_idx, x_idx] = z1_val
    self.z2[y_idx, x_idx] = z2_val
    self.z3[y_idx, x_idx] = z3_val
    
    # 更新imshow对象的数据
    self.im1.set_data(self.z1)
    self.im2.set_data(self.z2)
    self.im3.set_data(self.z3)
    
    # 可选:如果z值范围动态变化,更新颜色范围;若范围固定可注释此部分
    self.im1.set_clim(vmin=np.min(self.z1), vmax=np.max(self.z1))
    self.im2.set_clim(vmin=np.min(self.z2), vmax=np.max(self.z2))
    self.im3.set_clim(vmin=np.min(self.z3), vmax=np.max(self.z3))
    
    # 触发画布轻量重绘,避免阻塞UI
    self.canvas_cmaps.draw_idle()

3. 结合Qt异步线程处理数据采集

为避免仪器/微处理器通信阻塞UI线程,用QThread处理数据采集,通过信号槽传递数据到主线程:

from PyQt5.QtCore import QThread, pyqtSignal

class DataAcquisitionThread(QThread):
    # 定义信号:传递x索引、y索引、三个z值
    new_data = pyqtSignal(int, int, float, float, float)
    
    def __init__(self, x_vals, y_vals):
        super().__init__()
        self.x_vals = x_vals
        self.y_vals = y_vals
        
    def run(self):
        # 替换为你的微处理器通信、DAC/ADC控制、频谱仪采集逻辑
        for x_idx in range(len(self.x_vals)):
            for y_idx in range(len(self.y_vals)):
                # 模拟获取z值(实际代码替换为真实采集数据)
                z1 = np.random.uniform(0, 10)
                z2 = np.random.uniform(0, 10)
                z3 = np.random.uniform(0, 10)
                
                # 发送信号到主线程
                self.new_data.emit(x_idx, y_idx, z1, z2, z3)
                
                # 模拟采集间隔(根据实际仪器响应时间调整)
                self.msleep(150)

在主窗口中启动线程并连接信号:

# 假设x_values、y_values是用户输入的列表
x_values = [1,2,3,4,5]
y_values = [1,2,3,4]

self.acq_thread = DataAcquisitionThread(x_values, y_values)
self.acq_thread.new_data.connect(self.update_colormaps)
self.acq_thread.start()

优化提示

  • 若z值范围固定,初始化imshow时通过vmin、vmax参数设置,无需每次更新调用set_clim,提升性能。
  • 如果x、y是等间隔网格,可预先用np.meshgrid生成坐标,简化索引计算。
  • 若需批量更新多个点,可先一次性修改z数组,再统一调用set_data和draw_idle,减少重绘次数。

内容的提问来源于stack exchange,提问作者Stephanie Howell

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 18:22:46