[英]How can I update matplotlib subplot axes in an interactive plot when the subplots are viewing different axis regions?
I want to use the interactive subplots in matplotlib and have the changes I make (scrolling, cropping) apply to every subplot.我想使用 matplotlib 中的交互式子图,并将我所做的更改(滚动、裁剪)应用于每个子图。 Usually, I would use
通常,我会使用
ax1 = plt.subplot(112)
ax2 = plt.subplot(212, sharex=ax1)
but I do not want the axes to be identical.但我不希望轴相同。 I want them to be dependent, but able to display different x values.
我希望它们相互依赖,但能够显示不同的 x 值。 For example, I would want my initial plot to look like this
例如,我希望我的初始 plot 看起来像这样
3 subplots showing a sine curve with slightly offset x-axes in each 3 个子图显示正弦曲线,每个子图中的 x 轴略有偏移
And then I would be able to use the interactive plot tools to scroll across, such that each moves an equal amount in the x-direction.然后我将能够使用交互式 plot 工具滚动,这样每个工具在 x 方向上移动相等的量。
I have tried我努力了
a,b = ax1.get_xlim()
ax2 = plt.subplot(212,xlim=(a-1,b-1))
but the get_xlim()
does not get live, interactive updates to a
and b
, it only gets them once when the plot is first made.但是
get_xlim()
不会实时更新a
和b
,它只会在第一次制作 plot 时获得一次。
I can come up with two approaches, each with their drawbacks.我可以提出两种方法,每种方法都有其缺点。
xlim_changed
and ylim_changed
axis callbacksxlim_changed
和ylim_changed
轴回调This approach works for panning, zooming, scroll events, and the home/forward/backward buttons.这种方法适用于平移、缩放、滚动事件和主页/前进/后退按钮。
However, only one axis can control the other axis or axes, as connecting all axes to each other results in infinite recursion on triggering the callback.但是,只有一个轴可以控制另一个轴或多个轴,因为将所有轴相互连接会导致触发回调时无限递归。
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()
This doesn't result in recursion.这不会导致递归。 However, this approach breaks if the home, the forward, or the backward buttons is used.
但是,如果使用主页、前进或后退按钮,则此方法会中断。
Additionally, my implementation below doesn't trigger on zoom for reasons I can't discern (even though it should, IMO).此外,由于我无法辨别的原因,我在下面的实现不会触发缩放(即使它应该,IMO)。 Maybe someone else can spot and correct the issue.
也许其他人可以发现并纠正问题。
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.