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

如何基于子图形状动态调整Matplotlib子图布局?

Matplotlib双生子图自动适配布局实现

我用Matplotlib做双生子图可视化,两个子图共享X、Y轴且都设置了equal aspect属性。目前可以手动选择横向并排(1行2列)或纵向堆叠(2行1列)布局,但希望能根据子图的形状自动切换:

  • 扁形子图(宽大于高):适配2行1列布局
  • 高形子图(高大于宽):适配1行2列布局

子图形状仅通过窗口缩放调整,且两个子图形状始终一致。当前使用的布局代码如下:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider

pcd = np.random.rand(1000, 2) * 40
mdfig = plt.figure()
ax = mdfig.add_subplot(224)
ax.set_aspect('equal', adjustable='box')
ax.set_title("fine tuned")
axs = mdfig.add_subplot(221, sharex=ax, sharey=ax)
axs.set_aspect('equal', adjustable='box')
axs.set_title("original")

def on_change(val):
    ax.cla()
    axs.cla()
    ax.set_title("fine tuned")
    ax.axis('scaled')
    axs.set_title("original")
    axs.axis('scaled')
    ax.scatter(pcd[:,0], pcd[:,1], s=5, c='black', label="pointcloud", alpha=freq_slider.val)
    axs.scatter(pcd[:,0], pcd[:,1], s=5, c='black', label="pointcloud", alpha=freq_slider.val)

axfreq = mdfig.add_axes([0.25, 0.1, 0.65, 0.03])
freq_slider = Slider(
    ax=axfreq,
    label='transparency pointcloud',
    valmin=0,
    valmax=1,
    valinit=0,
    valstep=0.01
)
freq_slider.on_changed(on_change)    
plt.show()

自动布局实现方案

要实现根据子图形状自动切换布局,需要监听窗口大小变化事件,动态调整子图的布局方式:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider

pcd = np.random.rand(1000, 2) * 40
mdfig = plt.figure()
ax = None
axs = None
freq_slider = None

def update_plot():
    """更新子图内容的通用函数"""
    if ax is None or axs is None:
        return
    ax.cla()
    axs.cla()
    ax.set_title("fine tuned")
    ax.set_aspect('equal', adjustable='box')
    axs.set_title("original")
    axs.set_aspect('equal', adjustable='box')
    ax.scatter(pcd[:,0], pcd[:,1], s=5, c='black', label="pointcloud", alpha=freq_slider.val)
    axs.scatter(pcd[:,0], pcd[:,1], s=5, c='black', label="pointcloud", alpha=freq_slider.val)
    mdfig.canvas.draw_idle()

def adjust_layout(event):
    """根据窗口大小调整子图布局"""
    global ax, axs
    # 清除现有子图(保留滑块轴)
    for ax_obj in mdfig.axes:
        if ax_obj != freq_slider.ax:
            mdfig.delaxes(ax_obj)
    # 获取当前绘图区域的宽高比(预留滑块区域高度)
    fig_width, fig_height = mdfig.get_size_inches()
    plot_height = fig_height - 0.6
    aspect_ratio = fig_width / plot_height

    # 根据宽高比选择布局
    if aspect_ratio > 1.2:
        # 宽大于高,使用1行2列布局
        ax = mdfig.add_subplot(122)
        axs = mdfig.add_subplot(121, sharex=ax, sharey=ax)
    else:
        # 高大于宽,使用2行1列布局
        ax = mdfig.add_subplot(212)
        axs = mdfig.add_subplot(211, sharex=ax, sharey=ax)
    # 保持等比例属性
    ax.set_aspect('equal', adjustable='box')
    axs.set_aspect('equal', adjustable='box')
    # 更新绘图内容
    update_plot()

def on_change(val):
    """滑块透明度调整回调"""
    update_plot()

# 创建滑块控件
axfreq = mdfig.add_axes([0.25, 0.1, 0.65, 0.03])
freq_slider = Slider(
    ax=axfreq,
    label='transparency pointcloud',
    valmin=0,
    valmax=1,
    valinit=0,
    valstep=0.01
)
freq_slider.on_changed(on_change)

# 绑定窗口缩放事件
mdfig.canvas.mpl_connect('resize_event', adjust_layout)
# 初始化布局
adjust_layout(None)

plt.show()

关键说明

  • 通过mdfig.canvas.mpl_connect('resize_event', adjust_layout)监听窗口缩放,触发布局调整
  • 在布局函数中计算绘图区域宽高比,预留滑块高度避免控件重叠
  • 提取update_plot通用函数,减少重复代码
  • 重新创建子图时保留sharex、sharey和equal aspect属性,保证可视化效果一致性

内容的提问来源于stack exchange,提问作者Olaf Verburg

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 22:20:34