Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

matplotlib hooking in to home/back/forward button events

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

like image 975
bph Avatar asked Feb 15 '13 14:02

bph


People also ask

How do I make matplotlib zoomable?

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.

What is the %Matplotlib inline?

%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.

What does PLT IOFF () do?

ioff() Function: The ioff() function in pyplot module of matplotlib library is used to turn the interactive mode off.


3 Answers

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:

  1. If you want to use different backends, you have to subclass each of them.
  2. Deploy your own backend outside of matplotlib is not trivial. Your backend has to be a module which must be in the PYTHONPATH.

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()
like image 90
nymk Avatar answered Sep 28 '22 05:09

nymk


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.

like image 32
Thorsten Kranz Avatar answered Sep 28 '22 03:09

Thorsten Kranz


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.

like image 26
ali_m Avatar answered Sep 28 '22 04:09

ali_m