How to make zoom persistent?

This code:

import matplotlib.pyplot as plt
import numpy as np

def draw(image, title):
  ax.clear()
  plt.imshow(image, origin='lower', interpolation='None', aspect='equal')
  plt.title(title)
  plt.show()


def onclick(event):
  draw(image, f'({event.xdata}, {event.ydata})')


def onpress(event):
  if event.key in ['q', 'escape']:
    plt.close()
    return

  draw(image, f'{event.key}')


fig, ax = plt.subplots()
fig.canvas.mpl_connect('button_press_event', onclick)
fig.canvas.mpl_connect('key_press_event', onpress)
plt.axis('off')
plt.subplots_adjust(top = 1, bottom = 0, right = 1, left = 0, hspace = 0, wspace = 0)
plt.get_current_fig_manager().full_screen_toggle()
image = np.random.rand(8, 8)
draw(image, '')

allows for zooming and panning of image, but when I click on it or press a key, it goes back to original size (zoom level). How to make the zoom persistent? How to get the current zoom level?

It seems that calling ax.clear() is responsible for zoom level reset. I have to clear all plot elements, since the plot is modified on each event. This code is not shown here for conciseness.

Probably the easiest thing is to avoid using ax.clear. You can manage that by using img.set_data like so:

import matplotlib.pyplot as plt
import numpy as np

def draw(image, title):
  img.set_data(image)
  ax.set_title(title)
  fig.canvas.draw()


def onclick(event):
  new_image = np.random.rand(8,8)
  draw(new_image, f'({event.xdata}, {event.ydata})')


def onpress(event):
  if event.key in ['q', 'escape']:
    plt.close()
    return

  draw(image, f'{event.key}')


fig, ax = plt.subplots()
fig.canvas.mpl_connect('button_press_event', onclick)
fig.canvas.mpl_connect('key_press_event', onpress)
plt.axis('off')
plt.subplots_adjust(top = 1, bottom = 0, right = 1, left = 0, hspace = 0, wspace = 0)
plt.get_current_fig_manager().full_screen_toggle()
image = np.random.rand(8, 8)
img = ax.imshow(image, origin='lower', interpolation='None', aspect='equal')

plt.show()

You may also be interested in the zooming and panning utilities in mpl-interactions: Zooming and Panning — mpl-interactions

those will let you scroll to zoom and click and draw to pan (without selecting that from the toolbar)
e.g. here use right mouse button to pan, and left to trigger a new image

import matplotlib.pyplot as plt
import numpy as np
from mpl_interactions import zoom_factory, panhandler


def draw(image, title):
    img.set_data(image)
    ax.set_title(title)
    fig.canvas.draw()


def onclick(event):
    if event.button != 1:
        # only listen to left clicks
        return
    new_image = np.random.rand(8, 8)
    draw(new_image, f"({event.xdata}, {event.ydata})")


def onpress(event):
    if event.key in ["q", "escape"]:
        plt.close()
        return

    draw(image, f"{event.key}")


fig, ax = plt.subplots()
fig.canvas.mpl_connect("button_press_event", onclick)
fig.canvas.mpl_connect("key_press_event", onpress)
plt.axis("off")
plt.subplots_adjust(top=1, bottom=0, right=1, left=0, hspace=0, wspace=0)
plt.get_current_fig_manager().full_screen_toggle()
image = np.random.rand(8, 8)
img = ax.imshow(image, origin="lower", interpolation="None", aspect="equal")
ph = panhandler(fig, button=3)  # pan with right mouse button
zoom_factory(ax)

plt.show()

Thanks, I will take a look at them. Any pointers about how to store/restore the current zoom/pan state? Not using ax.clear() would be very painful, since I would have to keep track of numerous plot elements (labels, lines, etc.).

would be very painful, since I would have to keep track of numerous plot elements (labels, lines, etc.)

Maybe not easiest here. But in general mpl-interactions can handle all of that updating for you and make that pretty easy.

Any pointers about how to store/restore the current zoom/pan state?

Adding this seems to do the trick:

    fig.canvas.toolbar.push_current()
    ax.clear()
    fig.canvas.toolbar.back()
2 Likes