如何在PyQt中创建带颜色映射的实时三张子图
解决PyQt中嵌入3个共享Y轴的实时Matplotlib子图问题
需要在PyQt窗口内实现3个共享Y轴的实时子图时,遇到两个核心问题:
- 使用
plt.subplots(1,3)会生成独立Matplotlib窗口,无法嵌入PyQt界面; - 直接用
fig.add_subplot添加子图时程序报错,无法正常生成界面。
核心解决思路
- 避开
plt.subplots:该函数默认会创建Matplotlib自带的GUI窗口,不适合PyQt嵌入场景,应直接使用matplotlib.figure.Figure对象创建子图。 - 正确绑定共享Y轴:通过
fig.add_subplot的sharey参数关联三个子图的Y轴,同时保存所有子图引用,方便后续批量更新。 - 整合实时更新与颜色映射:在定时更新函数中分别清除并重绘每个子图,同时应用自定义颜色映射逻辑。
完整实现代码
from matplotlib.figure import Figure from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from PyQt5 import QtCore, QtWidgets import sys import numpy as np import matplotlib matplotlib.use('Qt5Agg') from matplotlib.colors import LinearSegmentedColormap class MplCanvas(FigureCanvas): def __init__(self, parent=None, width=15, height=8, dpi=100): # 直接创建Figure对象,不依赖plt模块的窗口创建逻辑 fig = Figure(figsize=(width, height), dpi=dpi) # 创建3个共享Y轴的子图,后两个绑定第一个的Y轴 self.ax1 = fig.add_subplot(1, 3, 1) self.ax2 = fig.add_subplot(1, 3, 2, sharey=self.ax1) self.ax3 = fig.add_subplot(1, 3, 3, sharey=self.ax1) # 保存子图列表,便于批量操作 self.axes_list = [self.ax1, self.ax2, self.ax3] # 设置标题与布局 fig.suptitle("CHIP", fontsize=16) self.ax1.set_title('Iᵦ→ᶜ', fontsize=12) self.ax2.set_title('dIᵦ→ᶜ/dt', fontsize=12) self.ax3.set_title('Noise', fontsize=12) # 调整子图间距,避免标题、颜色条重叠 fig.tight_layout(rect=[0, 0.05, 1, 0.95]) # 自定义颜色映射 self.custom_cmap = LinearSegmentedColormap.from_list( "custom", ["#00008B", "blue", "cyan", "green", "yellow", "orange", "red", "#8B0000"] ) super(MplCanvas, self).__init__(fig) class MainWindow(QtWidgets.QMainWindow): def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self.canvas = MplCanvas(self) self.setCentralWidget(self.canvas) # 初始化模拟实验数据 self.n_data = 2000 self.xdata = np.random.random(self.n_data) self.ydata1 = np.random.random(self.n_data) self.ydata2 = np.random.random(self.n_data) * 0.5 + 0.25 self.ydata3 = np.random.random(self.n_data) * 0.8 + 0.1 self.update_plot() self.show() # 定时刷新数据(100ms/次) self.timer = QtCore.QTimer() self.timer.setInterval(100) self.timer.timeout.connect(self.update_plot) self.timer.start() def update_plot(self): # 更新模拟数据(实际场景替换为实验数据读取逻辑) self.xdata = np.random.random(self.n_data) self.ydata1 = np.random.random(self.n_data) self.ydata2 = np.random.random(self.n_data) * 0.5 + 0.25 self.ydata3 = np.random.random(self.n_data) * 0.8 + 0.1 # 生成颜色映射数据(可根据实际指标调整) colors1 = self.xdata + self.ydata1 colors2 = self.xdata + self.ydata2 colors3 = self.xdata + self.ydata3 # 逐个更新子图 for ax, ydata, colors in zip(self.canvas.axes_list, [self.ydata1, self.ydata2, self.ydata3], [colors1, colors2, colors3]): ax.clear() # 绘制散点图,若需折线图替换为ax.plot()即可 scatter = ax.scatter(self.xdata, ydata, c=colors, cmap=self.canvas.custom_cmap, s=10) # 添加颜色条,pad参数调整与子图的间距 self.canvas.figure.colorbar(scatter, ax=ax, orientation='horizontal', pad=0.1) # 触发画布重绘 self.canvas.draw() if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) w = MainWindow() app.exec_()
关键细节说明
- 子图创建逻辑:
通过sharey=self.ax1让后两个子图共享第一个子图的Y轴,保证三个子图的Y轴刻度完全同步,避免独立缩放导致的对比误差。 - 窗口嵌入兼容性:
全程使用matplotlib.figure.Figure对象创建绘图区域,彻底避免plt模块自动生成独立GUI窗口的问题。 - 实时更新优化:
每个子图调用clear()清除旧内容后重绘,若需固定颜色范围,可在scatter函数中添加vmin和vmax参数,避免颜色映射随数据波动。 - 布局调整:
使用tight_layout调整子图间距,预留顶部标题和底部颜色条的空间,避免元素重叠。
内容的提问来源于stack exchange,提问作者Stephanie Howell
相关产品推荐
相关产品推荐

