matplotlib 3.5.1

>>> """
===========
Path Editor
===========

Sharing events across GUIs.

This example demonstrates a cross-GUI application using Matplotlib event
handling to interact with and modify objects on the canvas.
"""
... 
... import numpy as np
... from matplotlib.backend_bases import MouseButton
... from matplotlib.path import Path
... from matplotlib.patches import PathPatch
... import matplotlib.pyplot as plt
... 
... 
... fig, ax = plt.subplots()
... 
... pathdata = [
...  (Path.MOVETO, (1.58, -2.57)),
...  (Path.CURVE4, (0.35, -1.1)),
...  (Path.CURVE4, (-1.75, 2.0)),
...  (Path.CURVE4, (0.375, 2.0)),
...  (Path.LINETO, (0.85, 1.15)),
...  (Path.CURVE4, (2.2, 3.2)),
...  (Path.CURVE4, (3, 0.05)),
...  (Path.CURVE4, (2.0, -0.5)),
...  (Path.CLOSEPOLY, (1.58, -2.57)),
... ]
... 
... codes, verts = zip(*pathdata)
... path = Path(verts, codes)
... patch = PathPatch(
...  path, facecolor='green', edgecolor='yellow', alpha=0.5)
... ax.add_patch(patch)
... 
... 
... class PathInteractor:
...  """ An path editor. Press 't' to toggle vertex markers on and off. When vertex markers are on, they can be dragged with the mouse. """
... 
...  showverts = True
...  epsilon = 5 # max pixel distance to count as a vertex hit
... 
...  def __init__(self, pathpatch):
... 
...  self.ax = pathpatch.axes
...  canvas = self.ax.figure.canvas
...  self.pathpatch = pathpatch
...  self.pathpatch.set_animated(True)
... 
...  x, y = zip(*self.pathpatch.get_path().vertices)
... 
...  self.line, = ax.plot(
...  x, y, marker='o', markerfacecolor='r', animated=True)
... 
...  self._ind = None # the active vertex
... 
...  canvas.mpl_connect('draw_event', self.on_draw)
...  canvas.mpl_connect('button_press_event', self.on_button_press)
...  canvas.mpl_connect('key_press_event', self.on_key_press)
...  canvas.mpl_connect('button_release_event', self.on_button_release)
...  canvas.mpl_connect('motion_notify_event', self.on_mouse_move)
...  self.canvas = canvas
... 
...  def get_ind_under_point(self, event):
...  """ Return the index of the point closest to the event position or *None* if no point is within ``self.epsilon`` to the event position. """
...  # display coords
...  xy = np.asarray(self.pathpatch.get_path().vertices)
...  xyt = self.pathpatch.get_transform().transform(xy)
...  xt, yt = xyt[:, 0], xyt[:, 1]
...  d = np.sqrt((xt - event.x)**2 + (yt - event.y)**2)
...  ind = d.argmin()
... 
...  if d[ind] >= self.epsilon:
...  ind = None
... 
...  return ind
... 
...  def on_draw(self, event):
...  """Callback for draws."""
...  self.background = self.canvas.copy_from_bbox(self.ax.bbox)
...  self.ax.draw_artist(self.pathpatch)
...  self.ax.draw_artist(self.line)
...  self.canvas.blit(self.ax.bbox)
... 
...  def on_button_press(self, event):
...  """Callback for mouse button presses."""
...  if (event.inaxes is None
...  or event.button != MouseButton.LEFT
...  or not self.showverts):
...  return
...  self._ind = self.get_ind_under_point(event)
... 
...  def on_button_release(self, event):
...  """Callback for mouse button releases."""
...  if (event.button != MouseButton.LEFT
...  or not self.showverts):
...  return
...  self._ind = None
... 
...  def on_key_press(self, event):
...  """Callback for key presses."""
...  if not event.inaxes:
...  return
...  if event.key == 't':
...  self.showverts = not self.showverts
...  self.line.set_visible(self.showverts)
...  if not self.showverts:
...  self._ind = None
...  self.canvas.draw()
... 
...  def on_mouse_move(self, event):
...  """Callback for mouse movements."""
...  if (self._ind is None
...  or event.inaxes is None
...  or event.button != MouseButton.LEFT
...  or not self.showverts):
...  return
... 
...  vertices = self.pathpatch.get_path().vertices
... 
...  vertices[self._ind] = event.xdata, event.ydata
...  self.line.set_data(zip(*vertices))
... 
...  self.canvas.restore_region(self.background)
...  self.ax.draw_artist(self.pathpatch)
...  self.ax.draw_artist(self.line)
...  self.canvas.blit(self.ax.bbox)
... 
... 
... interactor = PathInteractor(patch)
... ax.set_title('drag vertices to update path')
... ax.set_xlim(-3, 4)
... ax.set_ylim(-3, 4)
... 
... plt.show()
...