Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
factory for adding zoom callback to matplotlib graphs
import matplotlib.pyplot as plt
def zoom_factory(ax,base_scale = 2.):
def zoom_fun(event):
# get the current x and y limits
cur_xlim = ax.get_xlim()
cur_ylim = ax.get_ylim()
# set the range
cur_xrange = (cur_xlim[1] - cur_xlim[0])*.5
cur_yrange = (cur_ylim[1] - cur_ylim[0])*.5
xdata = event.xdata # get event x location
ydata = event.ydata # get event y location
if event.button == 'up':
# deal with zoom in
scale_factor = 1/base_scale
elif event.button == 'down':
# deal with zoom out
scale_factor = base_scale
# deal with something that should never happen
scale_factor = 1
print event.button
# set new limits
ax.set_xlim([xdata - cur_xrange*scale_factor,
xdata + cur_xrange*scale_factor])
ax.set_ylim([ydata - cur_yrange*scale_factor,
ydata + cur_yrange*scale_factor])
ax.figure.canvas.draw() # force re-draw
fig = ax.get_figure() # get the figure of interest
# attach the call back
#return the function
return zoom_fun

Very nice!

One modification I made for my PyQt4 GUI: instead of using ax as a parameter, I pass in the parent canvas and say ax = on the first line. Then, instead of plt.draw(), I use canvas.draw(). However, your version works better for subplot zooming.


tacaswell commented May 24, 2013

@jdreaver I didn't see your comment till recently. I am glad it was helpful! If you think your changes would be helpful for other people you should fork the gist.

I also tweaked it a bit to not call plt.draw().

giadang commented Jan 17, 2015

IMHO, I think we don't have to calculate the cur_xrange and cur_yrange. Instead we can calculate the relative distance from the cursor position to the frame like this:

        # Get distance from the cursor to the edge of the figure frame
        x_left = xdata - cur_xlim[0]
        x_right = cur_xlim[1] - xdata
        y_top = ydata - cur_ylim[0]
        y_bottom = cur_ylim[1] - ydata

then set the new xlim, ylim like this:

ax.set_xlim([xdata - x_left*scale_factor,
                    xdata + x_right*scale_factor])
ax.set_ylim([ydata - y_top*scale_factor,
                    ydata + y_bottom*scale_factor])

By this way, when zooming in and zooming out, the pixel which the cursor is pointing to won't be changed.


tacaswell commented Jun 19, 2015

@giadang good point! That fixes one of the more annoying behaviors of this.

Thank you for this gist. Zooming in and out with the mousewheel (without moving the mouse pointer) does not restore the original view. It seems that the underlying view is paned.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment