Matplotlib子图作文本容器时tight_layout大文本下布局失效问题
Matplotlib布局问题:tight_layout下文本换行导致子图超出边界
问题描述
在Matplotlib中,将subfigure作为文本载体时,启用tight_layout且文本量较大时会出现布局失效问题:子图会超出figure的边界,仿佛文本并未换行。
复现代码
import pandas as pd from matplotlib import pyplot as plt # Paramters for A4 Paper fullheight = 11.69 fullwidth = 8.27 # how the subfigures dive the space fig0_factor = 0.7 fig1_factor = 0.3 # generate the figure fig = plt.figure(constrained_layout = True, figsize=(fullwidth, fullheight)) # generate 2 subfigures subfigs = fig.subfigures(2,height_ratios=[fig0_factor,fig1_factor]) # fill the fist subfigure axes = subfigs[0].subplots(nrows=1) # some plot ax = plt.plot([0,0],[-1,1], lw=1, c='b', figure=subfigs[0]) # some text subfigs[0].text(0.55, 0.00001, 'Figure 1', ha='center', va='center', rotation='horizontal',weight='bold') # fill the second subfigure text_ax = subfigs[1].subplots(1) # make its background transparent subfigs[1].patch.set_alpha(0.0) # remove the axis, not removing it makes no difference regarding the problem text_ax.set_axis_off() # generate some text message = ['word']*50 # 50 is enough to make the problem visable, my usecase has a way longer text message = ' '.join(message) # fill in the Text and wrap it text_ax.text(0.00001, 0.8, message, horizontalalignment='left', verticalalignment='top',size=7, wrap=True) # call tight layout # this is neccecary for the whole plot, that is not shown for simplicity reasons # explaination: subfigure one would be an array of subplots with different scales and descriptions, # ever changing depending on the data that is to be plotted, so tight_layout mittigates # a lot of tideos formatting fig.tight_layout()
现象说明
可以看到图的右边缘超出了整个面板。若关闭文本换行(wrap=False),则可明确原因:布局计算采用的是未换行文本的尺寸。
问询
是否存在其他方法,能让子图中的文本渲染时,布局正确识别文本换行后的边界而非未换行的边界?
解决方法
1. 放弃tight_layout,改用constrained_layout(推荐)
代码中已经设置了constrained_layout = True,但后续调用fig.tight_layout()会导致两种布局机制冲突。constrained_layout会自动处理文本换行后的边界计算,只需移除tight_layout的调用即可解决问题。
修改后的关键代码:
# 保留constrained_layout设置 fig = plt.figure(constrained_layout = True, figsize=(fullwidth, fullheight)) # ... 其他代码不变 ... # 移除该行调用 # fig.tight_layout()
2. 手动计算换行文本尺寸并调整子图
若必须使用tight_layout,可手动获取文本渲染后的实际尺寸,再调整子图边界:
- 先触发一次画布绘制,确保文本已经渲染
- 获取文本对象的实际包围盒,以此调整子图的显示范围
示例代码片段:
# 添加文本后先绘制画布 fig.canvas.draw() # 获取文本对象的实际尺寸 text_obj = text_ax.text(0.00001, 0.8, message, horizontalalignment='left', verticalalignment='top',size=7, wrap=True) bbox = text_obj.get_window_extent().transformed(text_ax.transAxes.inverted()) # 调整子图右边距,预留少量缓冲空间 text_ax.set_xlim(0, bbox.width * 1.05) # 再调用tight_layout fig.tight_layout()
3. 用textwrap提前手动换行
借助Python的textwrap模块,按子图宽度提前将文本分割为多行,让tight_layout能直接识别文本的实际行数与宽度:
import textwrap # 根据子图宽度和字体大小计算最大字符宽度 max_char_width = int(text_ax.get_window_extent().width / text_ax.figure.dpi * 72 / 7) wrapped_message = textwrap.fill(message, width=max_char_width) # 传入提前换行后的文本,无需再设置wrap=True text_ax.text(0.00001, 0.8, wrapped_message, horizontalalignment='left', verticalalignment='top',size=7)
内容的提问来源于stack exchange,提问作者Alexander Vocaet
相关产品推荐
相关产品推荐

