Matplotlib调用get_window_extent配transform参数获取Bbox坐标错误问题
问题成因
- 坐标转换时机错误:
plt.show()执行前调用fig.canvas.get_renderer()获取的是未完成全局布局的渲染结果,此时文本、坐标轴的位置还没有完成最终适配,拿到的extent坐标是临时值,窗口缩放后布局重绘,之前计算的坐标就会失效 - 坐标系匹配错误:你计算得到的坐标是相对于
ax_img的归一化坐标轴坐标,但如果将矩形添加到主坐标轴ax,又使用ax_img.transAxes作为变换,两个坐标轴的位置、尺寸参数不匹配,会导致坐标进一步偏移 - 自适应布局干扰:matplotlib默认的紧凑布局、坐标轴自适应调整会在渲染过程中修改axes的实际位置,和你初始化
add_axes时指定的参数产生偏差
修复方案
两种可选方案,可根据使用场景选择:
方案1:固定窗口尺寸+强制预渲染
适合不需要缩放窗口、直接导出图片的场景,提前固定画布DPI和尺寸,强制完成全量渲染后再计算坐标:
from matplotlib import pyplot as plt from matplotlib.patches import Rectangle # 提前固定画布尺寸和DPI,避免后续布局偏移 fig, ax = plt.subplots(figsize=(8,6), dpi=100) # 禁用紧凑布局,避免axes位置被自动调整 fig.set_layout_engine(None) # 替换为你的本地图片路径 flash_pic = plt.imread("./statics/images/flash_decrease.png") ax_img = fig.add_axes([0.3,0.3,0.1,0.1], aspect='equal') ax_img.imshow(flash_pic) # 隐藏ax_img的坐标轴边框、刻度,避免干扰显示 ax_img.axis('off') str_up = "This is" str_right = "a random string" text_up = ax_img.text(0, 1.1, str_up, fontsize=14, transform=ax_img.transAxes) text_right = ax_img.text(1.1, 0, str_right, fontsize=14, transform=ax_img.transAxes) # 强制canvas完成全量渲染,拿到最终布局参数 fig.canvas.draw() r = fig.canvas.get_renderer() # 坐标转换时直接转为figure的归一化坐标,矩形直接添加到fig上避免坐标轴变换干扰 x0 = text_up.get_window_extent(renderer=r).transformed(fig.transFigure.inverted()).x0 x1 = text_right.get_window_extent(renderer=r).transformed(fig.transFigure.inverted()).x1 y0 = text_right.get_window_extent(renderer=r).transformed(fig.transFigure.inverted()).y0 y1 = text_up.get_window_extent(renderer=r).transformed(fig.transFigure.inverted()).y1 width = x1 - x0 height = y1 - y0 # 矩形添加到fig的patch层,使用fig的坐标变换,完全不受坐标轴影响 rect = Rectangle((x0, y0), width, height, transform=fig.transFigure, color='red', alpha=0.3, zorder=0) fig.patches.append(rect) plt.show()
方案2:添加重绘回调,支持窗口自适应缩放
适合需要交互式操作、窗口可能缩放的场景,把矩形坐标计算逻辑放到重绘回调中,每次窗口调整时自动重新计算坐标:
from matplotlib import pyplot as plt from matplotlib.patches import Rectangle fig, ax = plt.subplots() flash_pic = plt.imread("./statics/images/flash_decrease.png") ax_img = fig.add_axes([0.3,0.3,0.1,0.1], aspect='equal') ax_img.imshow(flash_pic) ax_img.axis('off') str_up = "This is" str_right = "a random string" text_up = ax_img.text(0, 1.1, str_up, fontsize=14, transform=ax_img.transAxes) text_right = ax_img.text(1.1, 0, str_right, fontsize=14, transform=ax_img.transAxes) # 初始化矩形,后续回调里更新坐标 rect = Rectangle((0,0), 0, 0, color='red', alpha=0.3, zorder=0) fig.patches.append(rect) def update_rect(event): r = fig.canvas.get_renderer() x0 = text_up.get_window_extent(renderer=r).transformed(fig.transFigure.inverted()).x0 x1 = text_right.get_window_extent(renderer=r).transformed(fig.transFigure.inverted()).x1 y0 = text_right.get_window_extent(renderer=r).transformed(fig.transFigure.inverted()).y0 y1 = text_up.get_window_extent(renderer=r).transformed(fig.transFigure.inverted()).y1 rect.set_bounds(x0, y0, x1-x0, y1-y0) # 绑定draw事件,每次重绘自动更新矩形坐标 fig.canvas.mpl_connect('draw_event', update_rect) # 首次触发更新 fig.canvas.draw() plt.show()
内容的提问来源于stack exchange,提问作者calvin
相关产品推荐
相关产品推荐

