繁体   English   中英

当子图查看不同的轴区域时,如何在交互式 plot 中更新 matplotlib 子图轴?

[英]How can I update matplotlib subplot axes in an interactive plot when the subplots are viewing different axis regions?

我想使用 matplotlib 中的交互式子图,并将我所做的更改(滚动、裁剪)应用于每个子图。 通常,我会使用

ax1 = plt.subplot(112)   
ax2 = plt.subplot(212, sharex=ax1)   

但我不希望轴相同。 我希望它们相互依赖,但能够显示不同的 x 值。 例如,我希望我的初始 plot 看起来像这样

3 个子图显示正弦曲线,每个子图中的 x 轴略有偏移

然后我将能够使用交互式 plot 工具滚动,这样每个工具在 x 方向上移动相等的量。

我努力了

a,b = ax1.get_xlim()
ax2 = plt.subplot(212,xlim=(a-1,b-1))

但是get_xlim()不会实时更新ab ,它只会在第一次制作 plot 时获得一次。

我可以提出两种方法,每种方法都有其缺点。

方法 1:挂钩xlim_changedylim_changed轴回调

这种方法适用于平移、缩放、滚动事件和主页/前进/后退按钮。

但是,只有一个轴可以控制另一个轴或多个轴,因为将所有轴相互连接会导致触发回调时无限递归。

import numpy as np
import matplotlib.pyplot as plt

class XAxisUpdater(object):

    def __init__(self, master, slave):
        self.master = master
        self.slave = slave
        self.old_limits = self.get_limits(self.master)

    def __call__(self, dummy):
        new_limits = self.get_limits(self.master)
        deltas = new_limits - self.old_limits
        self.set_limits(self.slave, self.get_limits(self.slave) + deltas)
        self.old_limits = new_limits

    def get_limits(self, ax):
        return np.array(ax.get_xlim())

    def set_limits(self, ax, limits):
        ax.set_xlim(limits)


class YAxisUpdater(XAxisUpdater):

    def get_limits(self, ax):
        return np.array(ax.get_ylim())

    def set_limits(self, ax, limits):
        ax.set_ylim(limits)


if __name__ == '__main__':

    fig, (ax1, ax2) = plt.subplots(1, 2)
    ax1.set_xlim(0, 10)
    ax2.set_xlim(5, 15)
    ax1.callbacks.connect('xlim_changed', XAxisUpdater(ax1, ax2))
    ax1.callbacks.connect('ylim_changed', YAxisUpdater(ax1, ax2))
    plt.show()

方法 2:检测平移(和缩放)事件

这不会导致递归。 但是,如果使用主页、前进或后退按钮,则此方法会中断。

此外,由于我无法辨别的原因,我在下面的实现不会触发缩放(即使它应该,IMO)。 也许其他人可以发现并纠正问题。

import numpy as np
import matplotlib.pyplot as plt


class PanEvent(object):
    def __init__(self, ax, func, button=1):
        self.ax = ax
        self.func = func
        self.button = button
        self.press = False
        self.move = False
        self.ax.figure.canvas.mpl_connect('button_press_event', self.on_press)
        self.ax.figure.canvas.mpl_connect('button_release_event', self.on_release)
        self.ax.figure.canvas.mpl_connect('motion_notify_event', self.on_move)

    def on_press(self, event):
        if (event.inaxes == self.ax) & (event.button == self.button):
            self.press = True

    def on_move(self, event):
        if (event.inaxes == self.ax) & self.press:
            self.func()

    def on_release(self, event):
        if event.inaxes == self.ax:
            self.press = False


class AxisUpdater(object):

    def __init__(self, axes):
        self.axes = axes
        self.limits = np.array([ax.axis() for ax in self.axes])

    def __call__(self):
        for ax, old_limits in zip(self.axes, self.limits):
            deltas = np.array(ax.axis()) - old_limits
            if not np.all(np.isclose(deltas, 0)):
                break

        for ii, (ax, old_limits) in enumerate(zip(self.axes, self.limits)):
            self.limits[ii] = old_limits + deltas
            ax.axis(self.limits[ii])


if __name__ == '__main__':

    fig, axes = plt.subplots(1, 2)
    axes[0].set_xlim(0, 10)
    axes[1].set_xlim(5, 15)
    instance1 = PanEvent(axes[0], AxisUpdater(axes)) # NB: you have to keep a reference to the class, otherwise it will be GCed.
    instance2 = PanEvent(axes[1], AxisUpdater(axes))
    plt.show()
 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM