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

plt.show与fig.savefig含下标图例标签间距不一致问题

Matplotlib图例下标导致plt.show与savefig行间距差异问题解决

问题描述

使用Matplotlib绘图时发现:plt.show()弹出的交互式窗口中,包含下标(如$\mathdefault{H_{12}\ 5}$)的图例行间距,比fig.savefig()保存的PNG等静态图片的间距更大。叠加对比后字体大小完全一致,且仅当下标存在时出现该差异。相关TeX类rcParams配置如下:

'text.antialiased': True,
'text.color': 'black',
'text.hinting': 'force_autohint',
'text.hinting_factor': 8,
'text.kerning_factor': 0,
'text.latex.preamble': '\usepackage{amsmath}\usepackage{amssymb} \usepackage{sfmath}',
'text.usetex': False,
'mathtext.bf': 'sans:bold',
'mathtext.cal': 'cursive',
'mathtext.default': 'it',
'mathtext.fallback': 'cm',
'mathtext.fontset': 'dejavusans',
'mathtext.it': 'sans:italic',
'mathtext.rm': 'sans',
'mathtext.sf': 'sans',
'mathtext.tt': 'monospace',

测试代码:

import matplotlib.pyplot as plt

# 创建示例数据
x = [1, 2, 3, 4]
y1 = [1, 2, 3, 4]
y2 = [4, 3, 2, 1]
y3 = [2, 3, 2, 1]

# 创建主图
fig, ax = plt.subplots()

# 绘制线条
line1, = ax.plot(x, y1, label='$\mathdefault{H_{12}\ 5}$')
line2, = ax.plot(x, y2, label='$\mathdefault{H_{12}\ 5}$')
line3, = ax.plot(x, y3, label='$\mathdefault{H_{12}\ 5}$')
line3, = ax.plot(x, y3, label='$\mathdefault{H_{12}\ 5}$')
line3, = ax.plot(x, y3, label='$\mathdefault{H_{12}\ 5}$')
line3, = ax.plot(x, y3, label='$\mathdefault{H_{12}\ 5}$')
line3, = ax.plot(x, y3, label='$\mathdefault{H_{12}\ 5}$')

# 在图下方创建图例
fig.legend(loc='lower center', bbox_to_anchor=(0.5, 0), ncol=1, fontsize=8, labelspacing=0.5)

# 设置标签和标题
ax.set_xlabel('X-axis')
ax.set_ylabel('Y-axis')
ax.set_title('Multiple Lines with Legend Below')

# 调整布局
plt.tight_layout()
plt.subplots_adjust(bottom=0.4)
plt.show()

问题分析与解决

是否为Bug?

这并非严格意义上的Bug,而是Matplotlib交互式渲染(plt.show)与静态渲染(savefig)对数学公式文本的行高计算逻辑差异导致的:

  • 交互式窗口(如QtAgg、TkAgg后端)会基于文本的实际渲染边界(包括下标带来的额外垂直空间)计算行间距;
  • 静态保存时则使用更紧凑的文本基线间距计算,忽略下标超出基线的部分。

修复/绕过方案

以下方法可让plt.show()的图例行间距与savefig保持一致:

1. 强制设置图例handleheight参数

手动指定图例图标的高度,覆盖自动计算的行高:

fig.legend(loc='lower center', bbox_to_anchor=(0.5, 0), ncol=1, fontsize=8, 
           labelspacing=0.5, handleheight=1.0)  # 根据字体大小调整数值,如0.8~1.2

该参数控制图例图标的高度,间接限制行间距的自动扩展,使交互式窗口与静态保存的行高一致。

2. 开启usetex=True统一渲染逻辑

启用LaTeX渲染后,交互式与静态渲染的文本布局逻辑会统一,下标带来的行间距差异会消失。修改rcParam并确保系统已安装LaTeX环境:

plt.rcParams['text.usetex'] = True

注意:开启后渲染速度会变慢,但文本排版一致性更好。

3. 用Unicode下标替换LaTeX语法(若允许)

如果下标不需要严格的数学格式,直接使用Unicode下标字符,避免触发数学公式的行高计算:

label='H₁₂ 5'  # 使用Unicode下标字符,而非LaTeX的$H_{12}$语法

此方法完全绕过数学文本渲染逻辑,两种模式下的行间距会完全一致。

4. 自定义图例文本位置

通过legend.get_texts()获取所有文本对象,手动调整垂直位置:

legend = fig.legend(loc='lower center', bbox_to_anchor=(0.5, 0), ncol=1, fontsize=8, labelspacing=0.5)
for i, text in enumerate(legend.get_texts()):
    text.set_y(-i * 0.8)  # 0.8为自定义行间距因子,根据fontsize微调

该方法灵活性高,需根据实际字体大小和布局调整数值。


内容的提问来源于stack exchange,提问作者Maurits Houck

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 13:55:54