我也为此苦苦挣扎。关键是 matplotlib 在实际绘制文本之前不会确定文本的大小。所以你需要显式调用plt.draw(),然后调整你的边界,然后重新绘制。
get_window_extent 方法应该根据documentation 给出显示坐标的答案,而不是数据坐标。但是,如果尚未绘制画布,它似乎会在您在 textcoords 关键字参数中指定的任何坐标系中响应 annotate。这就是为什么您上面的代码使用textcoords='data',而不是'offset points'。
这是一个例子:
x = np.linspace(0,360,101)
y = np.sin(np.radians(x))
line, = plt.plot(x, y)
label = plt.annotate('finish', (360,0),
xytext=(12, 0), textcoords='offset points',
ha='left', va='center')
bbox = label.get_window_extent(plt.gcf().canvas.get_renderer())
print(bbox.extents)
array([ 12. , -5. , 42.84375, 5. ])
我们想要更改限制,使文本标签位于轴内。给定的bbox 的值没有多大帮助:因为它是相对于标记点的点数:在 x 中偏移 12 个点,一个字符串显然会长 30 点多一点,字体为 10 点(-5到 5 in y)。弄清楚如何从那里到达一组新的坐标轴边界并非易事。
但是,如果我们在绘制之后再次调用该方法,我们会得到一个完全不同的 bbox:
bbox = label.get_window_extent(plt.gcf().canvas.get_renderer())
print(bbox.extents)
现在我们得到
array([ 578.36666667, 216.66666667, 609.21041667, 226.66666667])
这是在显示坐标中,我们可以像以前那样使用ax.transData 进行转换。所以为了让我们的标签进入边界,我们可以这样做:
x = np.linspace(0,360,101)
y = np.sin(np.radians(x))
line, = plt.plot(x, y)
label = plt.annotate('finish', (360,0),
xytext=(8, 0), textcoords='offset points',
ha='left', va='center')
plt.draw()
bbox = label.get_window_extent()
ax = plt.gca()
bbox_data = bbox.transformed(ax.transData.inverted())
ax.update_datalim(bbox_data.corners())
ax.autoscale_view()
请注意,在绘制一次绘图后,不再需要将plt.gcf().canvas.get_renderer() 显式传递给get_window_extent。另外,我直接使用update_datalim 而不是xlim 和ylim,这样自动缩放就可以自动调整到整数。
我以笔记本格式here发布了这个答案。