How can I link axes of imshow plots for zooming and panning?

Posted by Adam Fraser on Stack Overflow See other posts from Stack Overflow or by Adam Fraser
Published on 2010-05-27T18:13:09Z Indexed on 2010/05/27 19:21 UTC
Read the original article Hit count: 215

Filed under:
|
|

Suppose I have a figure canvas with 3 plots... 2 are images of the same dimensions plotted with imshow, and the other is some other kind of subplot. I'd like to be able to link the x and y axes of the imshow plots so that when I zoom in one (using the zoom tool provided by the NavigationToolbar), the other zooms to the same coordinates, and when I pan in one, the other pans as well.

Subplot methods such as scatter and histogram can be passed kwargs specifying an axes for sharex and sharey, but imshow has no such configuration.

I started hacking my way around this by subclassing NavigationToolbar2WxAgg (shown below)... but there are several problems here. 1) This will link the axes of all plots in a canvas since all I've done is get rid of the checks for a.in_axes() 2) This worked well for panning, but zooming caused all subplots to zoom from the same global point, rather than from the same point in each of their respective axes.

Can anyone suggest a workaround?

Much thanks! -Adam

from matplotlib.backends.backend_wxagg import NavigationToolbar2WxAgg
class MyNavToolbar(NavigationToolbar2WxAgg):
    def __init__(self, canvas, cpfig):
        NavigationToolbar2WxAgg.__init__(self, canvas)

    # overrided
    # As mentioned in the code below, the only difference here from overridden
    # method is that this one doesn't check a.in_axes(event) when deciding which
    # axes to start the pan in...
    def press_pan(self, event):
        'the press mouse button in pan/zoom mode callback'

        if event.button == 1:
            self._button_pressed=1
        elif  event.button == 3:
            self._button_pressed=3
        else:
            self._button_pressed=None
            return

        x, y = event.x, event.y

        # push the current view to define home if stack is empty
        if self._views.empty(): self.push_current()

        self._xypress=[]
        for i, a in enumerate(self.canvas.figure.get_axes()):
            # only difference from overridden method is that this one doesn't
            # check a.in_axes(event)
            if x is not None and y is not None and a.get_navigate():
                a.start_pan(x, y, event.button)
                self._xypress.append((a, i))
                self.canvas.mpl_disconnect(self._idDrag)
                self._idDrag=self.canvas.mpl_connect('motion_notify_event', self.drag_pan)

    # overrided
    def press_zoom(self, event):
        'the press mouse button in zoom to rect mode callback'
        if event.button == 1:
            self._button_pressed=1
        elif  event.button == 3:
            self._button_pressed=3
        else:
            self._button_pressed=None
            return

        x, y = event.x, event.y

        # push the current view to define home if stack is empty
        if self._views.empty(): self.push_current()

        self._xypress=[]
        for i, a in enumerate(self.canvas.figure.get_axes()):
            # only difference from overridden method is that this one doesn't
            # check a.in_axes(event) 
            if x is not None and y is not None and a.get_navigate() and a.can_zoom():
                self._xypress.append(( x, y, a, i, a.viewLim.frozen(), a.transData.frozen()))

        self.press(event)

© Stack Overflow or respective owner

Related posts about image

Related posts about matplotlib