[英]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 看起来像这样
然后我将能够使用交互式 plot 工具滚动,这样每个工具在 x 方向上移动相等的量。
我努力了
a,b = ax1.get_xlim()
ax2 = plt.subplot(212,xlim=(a-1,b-1))
但是get_xlim()
不会实时更新a
和b
,它只会在第一次制作 plot 时获得一次。
我可以提出两种方法,每种方法都有其缺点。
xlim_changed
和ylim_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()
这不会导致递归。 但是,如果使用主页、前进或后退按钮,则此方法会中断。
此外,由于我无法辨别的原因,我在下面的实现不会触发缩放(即使它应该,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.