【问题标题】:subplots_adjust moves axes unpredictably?subplots_adjust 不可预测地移动轴?
【发布时间】:2019-08-15 00:27:45
【问题描述】:

我正在开发一个 python 模块,该模块创建一个带有on_resize 侦听器的 matplotlib 图。侦听器将下轴的高度强制为特定数量的像素(而不是相对于图形大小进行缩放)。有用。但是,如果(在 matplotlib 交互模式下)在创建绘图后用户调用 fig.subplots_adjust() 它会弄乱子图的大小。这是该模块功能的一个彻底简化的版本:

import matplotlib.pyplot as plt
plt.ion()

def make_plot():
    fig = plt.figure()
    gs = plt.GridSpec(10, 1, figure=fig)
    ax_upper = fig.add_subplot(gs[:-1])
    ax_lower = fig.add_subplot(gs[-1])
    ax_upper.plot([0, 1])
    ax_lower.plot([0, 1])
    fig.canvas.mpl_connect('resize_event', on_resize)
    return fig


def on_resize(event):
    fig = event.canvas.figure
    # get the current position
    ax_lower_pos = list(fig.axes[1].get_position().bounds)  # L,B,W,H
    # compute desired height in figure-relative coords
    desired_height_px = 40
    xform = fig.transFigure.inverted()
    desired_height_rel = xform.transform([0, desired_height_px])[1]
    # set the new height
    ax_lower_pos[-1] = desired_height_rel
    fig.axes[1].set_position(ax_lower_pos)

    # adjust ax_upper accordingly
    ax_lower_top = fig.axes[1].get_position().extents[-1]   # L,B,R,T
    ax_upper_pos = list(fig.axes[0].get_position().bounds)  # L,B,W,H
    # new bottom
    new_upper_bottom = ax_lower_top + desired_height_rel
    ax_upper_pos[1] = new_upper_bottom
    # new height
    ax_upper_top = fig.axes[0].get_position().extents[-1]   # L,B,R,T
    new_upper_height = ax_upper_top - new_upper_bottom
    ax_upper_pos[-1] = new_upper_height
    # set the new position
    fig.axes[0].set_position(ax_upper_pos)

    fig.canvas.draw()

这是用户调用fig = make_plot()时的输出:

现在如果用户调用fig.subplots_adjust,底部轴被挤压,底部和顶部轴之间的空间被挤压得更大(on_resize 监听器将它们都设置为 40px):

fig.subplots_adjust(top=0.7)

此时,抓住窗口的一角并拖动一点点就足以触发on_resize 侦听器并恢复我想要的(底部轴的固定像素高度和轴之间的空间),同时保持新的-原封不动地添加宽上边距:

如何在不手动触发调整大小事件的情况下获得该结果?据我所知,subplots_adjust 不会触发任何我可以监听的事件。

认为问题在于ax.update_params() 使用从底层subplotspec 获取的figbox 更新轴位置(据我所知,在初始之后不会更新)人物创作?)。 (注意:update_params 是从 subplots_adjust 内部调用的,参见 here)。

【问题讨论】:

  • @ImportanceOfBeingErnest 调用set_position的回调是python模块的一部分。 subplots_adjust 以后可能会或可能不会被用户调用(为标题腾出空间,或以其他方式自定义默认图形布局以用于出版物或演示文稿)。作为模块的贡献者,我可以更改回调的工作方式,但我无法控制用户,所以如果可能的话,我希望 subplots_adjust(大多数用户都知道)按他们的预期工作。
  • 不,plt.show 发生在模块中,在用户开始与绘图交互之前。
  • @ImportanceOfBeingErnest 我已经编辑了问题和示例代码,希望能让您更容易重现。

标签: matplotlib events subplot


【解决方案1】:

潜在的问题似乎是制作一个具有特定高度(以像素为单位)的轴。一个简单的解决方案是使用mpl_toolkits.axes_grid1make_axes_locatable。 这允许摆脱任何回调,从而摆脱事件中竞争条件的完整问题。

注意:情节似乎是更大图书馆的一部分。由于不光顾此类软件包的用户总是很好,因此通常允许他们指定要绘制的轴,这样他们就可以将绘图与其他元素一起放入更大的图形中。下面的解决方案使这变得特别容易。

当然,也可以随时拨打plt.subplots_adjust

import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.axes_divider import make_axes_locatable

desired_height_px = 40 #pixel

def make_plot(ax=None):
    if not ax:
        fig, ax = plt.subplots()
    else:
        fig = ax.figure
    div = make_axes_locatable(ax)
    cax = div.append_axes("bottom", desired_height_px/fig.dpi, pad=0.25)

    sc1 = ax.scatter([2,1,3], [2,3,1], c=[1,2,3])
    sc2 = cax.scatter([3,2,1],[2,3,1], c=[3,1,2])

    return fig, ax, cax, (sc1, sc2) 


fig, (ax1, ax2) = plt.subplots(1,2)

make_plot(ax=ax1)

#user plot on ax2
ax2.plot([1,3])

fig.subplots_adjust(top=0.7)
plt.show()

【讨论】:

    猜你喜欢
    • 2013-09-30
    • 1970-01-01
    • 2017-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-09
    • 2021-01-05
    • 2012-07-10
    • 2018-10-09
    相关资源
    最近更新 更多