1""" 2=========== 3Path Editor 4=========== 5 6Sharing events across GUIs. 7 8This example demonstrates a cross-GUI application using Matplotlib event 9handling to interact with and modify objects on the canvas. 10""" 11 12import numpy as np 13from matplotlib.backend_bases import MouseButton 14from matplotlib.path import Path 15from matplotlib.patches import PathPatch 16import matplotlib.pyplot as plt 17 18 19fig, ax = plt.subplots() 20 21pathdata = [ 22 (Path.MOVETO, (1.58, -2.57)), 23 (Path.CURVE4, (0.35, -1.1)), 24 (Path.CURVE4, (-1.75, 2.0)), 25 (Path.CURVE4, (0.375, 2.0)), 26 (Path.LINETO, (0.85, 1.15)), 27 (Path.CURVE4, (2.2, 3.2)), 28 (Path.CURVE4, (3, 0.05)), 29 (Path.CURVE4, (2.0, -0.5)), 30 (Path.CLOSEPOLY, (1.58, -2.57)), 31] 32 33codes, verts = zip(*pathdata) 34path = Path(verts, codes) 35patch = PathPatch( 36 path, facecolor='green', edgecolor='yellow', alpha=0.5) 37ax.add_patch(patch) 38 39 40class PathInteractor: 41 """ 42 An path editor. 43 44 Press 't' to toggle vertex markers on and off. When vertex markers are on, 45 they can be dragged with the mouse. 46 """ 47 48 showverts = True 49 epsilon = 5 # max pixel distance to count as a vertex hit 50 51 def __init__(self, pathpatch): 52 53 self.ax = pathpatch.axes 54 canvas = self.ax.figure.canvas 55 self.pathpatch = pathpatch 56 self.pathpatch.set_animated(True) 57 58 x, y = zip(*self.pathpatch.get_path().vertices) 59 60 self.line, = ax.plot( 61 x, y, marker='o', markerfacecolor='r', animated=True) 62 63 self._ind = None # the active vertex 64 65 canvas.mpl_connect('draw_event', self.on_draw) 66 canvas.mpl_connect('button_press_event', self.on_button_press) 67 canvas.mpl_connect('key_press_event', self.on_key_press) 68 canvas.mpl_connect('button_release_event', self.on_button_release) 69 canvas.mpl_connect('motion_notify_event', self.on_mouse_move) 70 self.canvas = canvas 71 72 def get_ind_under_point(self, event): 73 """ 74 Return the index of the point closest to the event position or *None* 75 if no point is within ``self.epsilon`` to the event position. 76 """ 77 # display coords 78 xy = np.asarray(self.pathpatch.get_path().vertices) 79 xyt = self.pathpatch.get_transform().transform(xy) 80 xt, yt = xyt[:, 0], xyt[:, 1] 81 d = np.sqrt((xt - event.x)**2 + (yt - event.y)**2) 82 ind = d.argmin() 83 84 if d[ind] >= self.epsilon: 85 ind = None 86 87 return ind 88 89 def on_draw(self, event): 90 """Callback for draws.""" 91 self.background = self.canvas.copy_from_bbox(self.ax.bbox) 92 self.ax.draw_artist(self.pathpatch) 93 self.ax.draw_artist(self.line) 94 self.canvas.blit(self.ax.bbox) 95 96 def on_button_press(self, event): 97 """Callback for mouse button presses.""" 98 if (event.inaxes is None 99 or event.button != MouseButton.LEFT 100 or not self.showverts): 101 return 102 self._ind = self.get_ind_under_point(event) 103 104 def on_button_release(self, event): 105 """Callback for mouse button releases.""" 106 if (event.button != MouseButton.LEFT 107 or not self.showverts): 108 return 109 self._ind = None 110 111 def on_key_press(self, event): 112 """Callback for key presses.""" 113 if not event.inaxes: 114 return 115 if event.key == 't': 116 self.showverts = not self.showverts 117 self.line.set_visible(self.showverts) 118 if not self.showverts: 119 self._ind = None 120 self.canvas.draw() 121 122 def on_mouse_move(self, event): 123 """Callback for mouse movements.""" 124 if (self._ind is None 125 or event.inaxes is None 126 or event.button != MouseButton.LEFT 127 or not self.showverts): 128 return 129 130 vertices = self.pathpatch.get_path().vertices 131 132 vertices[self._ind] = event.xdata, event.ydata 133 self.line.set_data(zip(*vertices)) 134 135 self.canvas.restore_region(self.background) 136 self.ax.draw_artist(self.pathpatch) 137 self.ax.draw_artist(self.line) 138 self.canvas.blit(self.ax.bbox) 139 140 141interactor = PathInteractor(patch) 142ax.set_title('drag vertices to update path') 143ax.set_xlim(-3, 4) 144ax.set_ylim(-3, 4) 145 146plt.show() 147