Of course! What you want is beating. If you did not write gui, you can simplify some of them using matplotlib.animation , but you will need to process it directly if you want something to be interactive.
In matplotlib terms, you need a combination of fig.canvas.copy_from_bbox , and then call fig.canvas.restore_region(background) , ax.draw_artist(what_you_want_to_draw) and fig.canvas.blit :
background = fig.canvas.copy_from_bbox(ax.bbox) for x, y in user_interactions: fig.canvas.restore_region(background) points.append([x, y]) scatter.set_offsets(points) ax.draw_artist(scatter) fig.canvas.blit(ax.bbox)
Simple Blitting Example: Adding Points
In your case, if you add only points, you can actually skip saving and restoring the background. However, if you go along this route, you will encounter some minor changes to the plot due to the repeated intersection of smoothing points on top of each other.
In any case, here is the simplest example of the type of thing you want. This only applies to adding points and skips saving and restoring the background, as I mentioned above:
import matplotlib.pyplot as plt import numpy as np def main(): fig, ax = plt.subplots() ax.pcolormesh(np.random.random((100, 100)), cmap='gray') ClickToDrawPoints(ax).show() class ClickToDrawPoints(object): def __init__(self, ax): self.ax = ax self.fig = ax.figure self.xy = [] self.points = ax.scatter([], [], s=200, color='red', picker=20) self.fig.canvas.mpl_connect('button_press_event', self.on_click) def on_click(self, event): if event.inaxes is None: return self.xy.append([event.xdata, event.ydata]) self.points.set_offsets(self.xy) self.ax.draw_artist(self.points) self.fig.canvas.blit(self.ax.bbox) def show(self): plt.show() main()
Sometimes Simple is Too Easy
However, let's say we wanted right-clicks to delete a point.
In this case, we should be able to restore the background without redrawing it.
Good, all is well and good. We will use something similar to the pseudo-code fragment that I mentioned at the beginning of the answer.
However, there is a caveat: if the image size changes, we need to update the background. Similarly, if the axes are lit interactively, we need to update the background. Basically, you need to update the background at any time when the chart is drawn.
Pretty soon you will need to be pretty hard.
More complicated: add / drag / drop points
Here is a general example of the kind of "forest" that you are completing.
This is somewhat inefficient, as the plot is obtained twice. (e.g. panning will be slow). This can be circumvented, but I will leave these examples at a different time.
This allows you to add points, drag and drop points, and delete points. To add / drag a point after interactively zooming / panning, click again to enlarge / pan the tool in the toolbar to disable it.
This is a rather complicated example, but I hope it gives an idea of ββthe type of structure that is usually assembled for interactive drawing / dragging / editing / deleting matplotlib artists without redrawing the entire graph.
import numpy as np import matplotlib.pyplot as plt class DrawDragPoints(object): """ Demonstrates a basic example of the "scaffolding" you need to efficiently blit drawable/draggable/deleteable artists on top of a background. """ def __init__(self): self.fig, self.ax = self.setup_axes() self.xy = [] self.tolerance = 10 self._num_clicks = 0