在matplotlib中处理主页/返回/前进按钮事件
有没有人知道怎么从matplotlib图形中获取“主页”、“后退”和“前进”按钮的事件?
我需要这些事件来调用我的一些函数,这样当按下这些按钮时,我的图表才能正常工作,也就是说,默认的行为并不能满足我的需求。
matplotlib假设底层的数据集是固定的,它只需要重置x/y轴的范围并重新绘制图形来应对这些按钮的操作——但不幸的是,这个假设在我的情况下并不成立——我有一个数据堆栈,需要在这些按钮事件触发时进行推入和弹出。
3 个回答
我不知道有没有一种不依赖后台的解决方案来解决这个问题。不过,如果你使用的是Qt4Agg这个后台,你可以试试这样做:
import matplotlib
matplotlib.use("Qt4Agg")
import pylab as p
def home_callback():
print "home called"
def back_callback():
print "back called"
def forward_callback():
print "forward called"
p.ion()
p.plot(p.random((10)))
fm = p.get_current_fig_manager()
fm.toolbar.actions()[0].triggered.connect(home_callback)
fm.toolbar.actions()[1].triggered.connect(back_callback)
fm.toolbar.actions()[2].triggered.connect(forward_callback)
首先,我获取当前的图形管理器,这样我就可以访问它的工具栏。然后,我可以把其他的回调函数连接到它的操作上。
如果你不能使用QT4Agg作为后台,我们可以尝试为其他后台做类似的事情。
如果你想在x轴或y轴大小改变时简单地调用某个函数,最简单的方法就是绑定到xlim_changed
和ylim_changed
这两个事件上。这些事件是由你的坐标轴生成的。例如:
def on_xlim_change(*args):
print "do your pushing and popping here..."
ax = gca()
ax.callbacks.connect('xlim_changed',on_xlim_change)
这个回调函数会在你按下前进、后退或主页键时执行,同时在你使用平移或缩放工具时也会执行(至少在WX和GTK这两种后端中是这样的)。不过,它是在matplotlib已经完成了坐标轴的常规缩放之后才执行的。
如果你真的想直接访问这些按钮的回调函数,那我觉得没有简单的方法可以不依赖于后端,因为事件处理的方式会根据你使用的后端不同而有所不同。我认为基本的方法是创建一个matplotlib.backends.backend_<name>.NavigationToolbar2<name>
的子类,并重写forward
、back
和home
这几个方法。你还需要根据你使用的具体后端,弄清楚如何将你新的工具栏类整合进去。
如果你想实现一些与设置坐标轴范围没有太大关系的自定义“前进/后退”控制,使用小部件可能会更好。
Matplotlib并没有提供“主页”、“后退”或“前进”按钮的事件。
如果想要添加一个回调函数,让它在点击“主页”、“后退”或“前进”按钮时被调用,通常的做法是创建一个matplotlib后端的子类。
不过,我不太喜欢这种做法。我觉得有两个缺点:
- 如果你想使用不同的后端,就得为每个后端都创建一个子类。
- 在matplotlib之外部署你自己的后端并不简单。你的后端必须是一个模块,并且要在PYTHONPATH中。
因为matplotlib提供的后端都没有重写NavigationToolbar2
的home
、back
和forward
方法,所以我更喜欢一种更简洁的猴子补丁(monkey-patch)方法。
比如,你可以用自己的方法替换掉NavigationToolbar2
的home
。
import matplotlib.pyplot as plt
from matplotlib.backend_bases import NavigationToolbar2
home = NavigationToolbar2.home
def new_home(self, *args, **kwargs):
print 'new home'
home(self, *args, **kwargs)
NavigationToolbar2.home = new_home
fig = plt.figure()
plt.text(0.35, 0.5, 'Hello world!', dict(size=30))
plt.show()
我们甚至可以模仿matplotlib的mpl_connect
风格。
import matplotlib.pyplot as plt
from matplotlib.backend_bases import NavigationToolbar2, Event
home = NavigationToolbar2.home
def new_home(self, *args, **kwargs):
s = 'home_event'
event = Event(s, self)
event.foo = 100
self.canvas.callbacks.process(s, event)
home(self, *args, **kwargs)
NavigationToolbar2.home = new_home
def handle_home(evt):
print 'new home'
print evt.foo
fig = plt.figure()
fig.canvas.mpl_connect('home_event', handle_home)
plt.text(0.35, 0.5, 'Hello world!', dict(size=30))
plt.show()