Does anyone know how to 'get' the 'home', 'back' and 'forward' button events from a matplotlib figure?
I need the events to call some of my functions such that my plots behave correctly when those button are pressed, i.e. the default behaviour isn't doing what i need it to do
Matplotlib assumes the underlying dataset is constant and that all it need do is reset the x/y axis limits and replot for those buttons - unfortunately that assumption is untrue for my case - I have a data stack that needs to be pushed and popped as those button events are triggered
Press the right mouse button to zoom, dragging it to a new position. The x axis will be zoomed in proportionately to the rightward movement and zoomed out proportionately to the leftward movement. The same is true for the y axis and up/down motions.
%matplotlib inline turns on “inline plotting”, where plot graphics will appear in your notebook. This has important implications for interactivity: for inline plotting, commands in cells below the cell that outputs a plot will not affect the plot.
ioff() Function: The ioff() function in pyplot module of matplotlib library is used to turn the interactive mode off.
Matplotlib doesn't provide 'home', 'back' or 'forward' button event.
To add a callback that will be called with 'home', 'back' or 'forward' button event, a common approach is to subclass a matplotlib backend.
But I am not in favor of this approach. I think it has two cons:
Since none of backends provided by matplotlib overrides NavigationToolbar2
's home
, back
and forward
methods. I prefer the more concise monkey-patched approach.
For example, you can replace NavigationToolbar2
's home
by your own method.
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()
We can even mimic matplotlib's mpl_connect
style.
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()
I'm not aware of a backend-independent solution for this problem. But when you are using the Qt4Agg-backend, you can try this:
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)
First I get the current figure manager so I can access its toolbar. Then I can connect additional callbacks to its actions.
If using QT4Agg as backend is not an option for you we could try to do something similar for other backends.
If you could get away with just calling some function whenever the x or y axis gets resized, the easiest hack would be to bind to xlim_changed
and ylim_changed
events generated by your axes. For example:
def on_xlim_change(*args):
print "do your pushing and popping here..."
ax = gca()
ax.callbacks.connect('xlim_changed',on_xlim_change)
This callback executes whenever you hit the forward, backward or home keys as well as when you use the pan or zoom tools (at least with the WX and GTK backends). However, it still executes after matplotlib has already done its usual rescaling of the axes.
If you really want access to those button callbacks directly then I can't see an easy backend-independent way since event handling will work differently depending on which backend you're using. I think the basic approach would be to subclass matplotlib.backends.backend_<name>.NavigationToolbar2<name>
and override the forward
, back
and home
methods. You'll still have to figure out exactly how to incorporate your new toolbar class depending on the specific backend you're using.
If you want to implement some custom 'forward/backward' control that has nothing much to do with setting the axis limits, you'd probably be better off using widgets instead.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With