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

基于Matplotlib与PySimpleGUI实现带共享轴的动态子图

动态子图实现方案(Python3 + Matplotlib 3.6.2 + PySimpleGUI)

需求说明

  • 背景:通过PySimpleGUI的信号复选框控制Matplotlib子图的动态增减,勾选时添加对应子图并均分画布空间,取消勾选则移除子图,剩余子图自动填充整个画布。
  • 具体要求:
    1. 子图最多支持10行1列布局;
    2. 仅显示1张子图时,该子图占据整个画布;
    3. 新增子图后,所有子图均分画布空间,且共享底部X轴;
    4. 可随时删除指定子图,删除后剩余子图自动调整布局占满画布。

现有代码问题

原代码基本实现了子图添加,但存在轴未共享、子图尺寸不一致的问题,原代码如下:

import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
 
number_of_plots = 2
fig = plt.figure()
 
ax = fig.add_subplot(1, 1, 1)
 
gs = gridspec.GridSpec(number_of_plots + 1, 1)
plot_space = gs[0:number_of_plots].get_position(fig)
print(plot_space,
      [plot_space.x0, plot_space.y0, plot_space.width, plot_space.height])
ax.set_position(plot_space)
ax.set_subplotspec(gs[0:number_of_plots])              # only necessary if using tight_layout()
fig.tight_layout()                # not strictly part of the question
ax = fig.add_subplot(gs[2], sharex=ax)
 
plt.show()

修正后的完整实现

以下代码整合PySimpleGUI复选框交互,解决了轴共享和子图尺寸问题,满足所有需求:

import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import PySimpleGUI as sg
import matplotlib.gridspec as gridspec

# 初始化配置
MAX_PLOTS = 10
# 模拟10个信号的名称
signal_names = [f"信号{i+1}" for i in range(MAX_PLOTS)]
# 记录当前选中的信号索引
selected_signals = []
# 存储子图对象
axes = []

# 设置PySimpleGUI布局
sg.theme('DefaultNoMoreNagging')
# 左侧复选框列
checkbox_col = [[sg.Checkbox(name, key=f'-CHECKBOX-{i}', enable_events=True)] for i, name in enumerate(signal_names)]
# 右侧Matplotlib画布区域
layout = [
    [sg.Column(checkbox_col), sg.Canvas(key='-CANVAS-')]
]

window = sg.Window('动态子图演示', layout, finalize=True, element_justification='center')

# 初始化Matplotlib图
fig = plt.figure(figsize=(8, 6))
canvas = FigureCanvasTkAgg(fig, window['-CANVAS-'].TKCanvas)
canvas.draw()
canvas.get_tk_widget().pack(side='top', fill='both', expand=1)

def update_subplots():
    # 清除所有现有子图
    for ax in axes:
        ax.remove()
    axes.clear()
    
    num_plots = len(selected_signals)
    if num_plots == 0:
        # 无选中信号时清空画布
        fig.clear()
        canvas.draw()
        return
    
    # 创建GridSpec,所有子图均分空间
    gs = gridspec.GridSpec(num_plots, 1, hspace=0.1)
    # 共享X轴的基准子图
    base_ax = None
    
    for i in range(num_plots):
        if i == 0:
            # 第一个子图作为基准,后续子图共享其X轴
            ax = fig.add_subplot(gs[i])
            base_ax = ax
        else:
            ax = fig.add_subplot(gs[i], sharex=base_ax)
            # 隐藏中间子图的X轴刻度,只保留底部子图的
            plt.setp(ax.get_xticklabels(), visible=False)
        
        # 模拟绘制数据(可替换为真实信号数据)
        ax.plot([1,2,3,4,5], [i*2 + j for j in [1,3,2,4,3]])
        ax.set_title(signal_names[selected_signals[i]])
        axes.append(ax)
    
    # 调整布局,避免标签重叠
    fig.tight_layout()
    canvas.draw()

# 事件循环
while True:
    event, values = window.read()
    if event == sg.WIN_CLOSED:
        break
    
    # 处理复选框事件
    if '-CHECKBOX-' in event:
        idx = int(event.split('-')[-1])
        if values[event]:
            if len(selected_signals) < MAX_PLOTS:
                selected_signals.append(idx)
        else:
            if idx in selected_signals:
                selected_signals.remove(idx)
        # 更新子图布局
        update_subplots()

window.close()
plt.close(fig)

关键实现说明

  • 动态布局管理:使用gridspec.GridSpec根据当前选中的子图数量创建对应行数的布局,保证所有子图均分画布空间;
  • X轴共享:以第一个子图为基准,后续子图通过sharex=base_ax实现X轴共享,并隐藏中间子图的X轴刻度,仅保留底部子图的刻度;
  • 交互逻辑:通过PySimpleGUI的复选框事件监听,实时更新选中信号列表,触发子图重绘;
  • 子图清理:每次更新前移除所有现有子图,避免布局混乱,确保新布局完全适配当前选中状态。

内容的提问来源于stack exchange,提问作者Sreenath R

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 00:50:36