0

I have a matplotlib figure in which I have several plots. One has an image with a large number of points in it. The second has an axis in which I want to update a point location with fast updates.

The piece of code below is condensed down to show effectively what I would like to do. As I move the mouse around the ax_large plot I want it to update quite quickly.

I found numerous examples on SO and other places and was trying some different options. But none seem to fit the bill quite correctly (or at least as I would hope / expect, so maybe my expectations need to change).

The code:

class Test:

    def __init__(self):
        self.fig = plt.figure(1)

        # Axis with large plot
        self.ax_large = plt.subplot(121)
        self.ax_large.imshow(np.random.random((5000,5000)))

        # Follow the point
        self.ax = plt.subplot(122)
        self.ax.grid('on')
        self.fig.canvas.callbacks.connect('motion_notify_event', self.callback)

        self.point = self.ax.plot(0,0, 'go')

        plt.show()

    def callback(self, event):
        if event.inaxes == self.ax:
            print('Updating to {} {}'.format(event.xdata, event.ydata))

            self.point[0].set_data(event.xdata, event.ydata)

            # Option 1. Works, bu super slow if there are other large sub-plots
            plt.draw()

            # Option 2. Doesn't update
            # self.fig.canvas.blit(self.ax.bbox)

            # Option 3. Works but then grid goes away
            # self.ax.redraw_in_frame()
            # self.fig.canvas.blit(self.ax.bbox)

            # Option 4. Doesn't update
            # self.ax.draw_artist(self.point[0])

            # Option 5. Draws new point but does not remove the "old" one
            # self.ax.draw_artist(self.point[0])
            # self.fig.canvas.blit(self.ax.bbox)

if __name__ == '__main__':
    tt = Test()

As you move around the ax_large I was hoping it would be a fast update for the point's location.

Any ideas on how to do this would be helpful.

Thanks...

brechmos
  • 1,278
  • 1
  • 11
  • 22

1 Answers1

3

You are essentially ignoring most of what is needed for blitting. See e.g.

As usual you need to

  • Draw the canvas, fig.canvas.draw()
  • Store the background for later, fig.canvas.copy_from_bbox()
  • Update the point, point.set_data
  • Restore the background, fig.canvas.restore_region
  • draw the point, ax.draw_artist
  • blit the axes, fig.canvas.blit

Hence

import matplotlib.pyplot as plt
import numpy as np

class Test:
    def __init__(self):
        self.fig = plt.figure(1)
        # Axis with large plot
        self.ax_large = plt.subplot(121)
        self.ax_large.imshow(np.random.random((5000,5000)))
        # Follow the point
        self.ax = plt.subplot(122)
        self.ax.grid(True)

        # set some limits to the axes
        self.ax.set_xlim(-5,5)
        self.ax.set_ylim(-5,5)
        # Draw the canvas once
        self.fig.canvas.draw()
        # Store the background for later
        self.background = self.fig.canvas.copy_from_bbox(self.ax.bbox)
        # Now create some point
        self.point, = self.ax.plot(0,0, 'go')
        # Create callback to mouse movement
        self.cid = self.fig.canvas.callbacks.connect('motion_notify_event', 
                                                     self.callback)
        plt.show()

    def callback(self, event):
        if event.inaxes == self.ax:
            # Update point's location            
            self.point.set_data(event.xdata, event.ydata)
            # Restore the background
            self.fig.canvas.restore_region(self.background)
            # draw the point on the screen
            self.ax.draw_artist(self.point)
            # blit the axes
            self.fig.canvas.blit(self.ax.bbox)


tt = Test()
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712