|
| 1 | +import matplotlib.pyplot as plt |
| 2 | +from itertools import cycle |
| 3 | +import numpy as np |
| 4 | +plt.ion() |
| 5 | + |
| 6 | + |
| 7 | +class LineMaker: |
| 8 | + def __init__(self, ln): |
| 9 | + # stash the current data |
| 10 | + self.xdata = list(ln.get_xdata()) |
| 11 | + self.ydata = list(ln.get_ydata()) |
| 12 | + # stash the Line2D artist |
| 13 | + self.ln = ln |
| 14 | + self.color_cyle = cycle(['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', |
| 15 | + '#9467bd', '#8c564b', '#e377c2', '#7f7f7f', |
| 16 | + '#bcbd22', '#17becf']) |
| 17 | + self.button_cid = ln.figure.canvas.mpl_connect('button_press_event', |
| 18 | + self.on_button) |
| 19 | + self.key_cid = ln.figure.canvas.mpl_connect('key_press_event', |
| 20 | + self.on_key) |
| 21 | + |
| 22 | + def on_button(self, event): |
| 23 | + # only consider events from the lines Axes |
| 24 | + if event.inaxes is not self.ln.axes: |
| 25 | + return |
| 26 | + |
| 27 | + # if not the left mouse button or a modifier key |
| 28 | + # is held down, bail |
| 29 | + if event.button != 1 or event.key not in (None, 'shift'): |
| 30 | + print('key+button: {!r}+{!r}'.format(event.key, event.button)) |
| 31 | + return |
| 32 | + |
| 33 | + if event.key == 'shift': |
| 34 | + # compute the distance to each point *in data space* |
| 35 | + d = np.hypot(np.asarray(self.xdata) - event.xdata, |
| 36 | + np.asarray(self.ydata) - event.ydata) |
| 37 | + # find the closest point |
| 38 | + ix = np.argmin(d) |
| 39 | + # remove that data point |
| 40 | + del self.xdata[ix] |
| 41 | + del self.ydata[ix] |
| 42 | + else: |
| 43 | + # get the event location in data-space |
| 44 | + # and add to internal data list |
| 45 | + self.xdata.append(event.xdata) |
| 46 | + self.ydata.append(event.ydata) |
| 47 | + # update the line |
| 48 | + self._update_line() |
| 49 | + |
| 50 | + def _update_line(self): |
| 51 | + # update the artist data |
| 52 | + self.ln.set_data(self.xdata, self.ydata) |
| 53 | + # ask the GUI to re-draw the next time it can |
| 54 | + self.ln.figure.canvas.draw_idle() |
| 55 | + |
| 56 | + def on_key(self, event): |
| 57 | + # This is _super_ useful for debugging! |
| 58 | + # print(event.key) |
| 59 | + |
| 60 | + # if the escape key is hit, clear the data |
| 61 | + if event.key == 'escape': |
| 62 | + # clear the internal data structures |
| 63 | + self.xdata.clear() |
| 64 | + self.ydata.clear() |
| 65 | + # update the internal line |
| 66 | + self._update_line() |
| 67 | + |
| 68 | + # if the key is c (any case) |
| 69 | + if event.key.lower() == 'c': |
| 70 | + # change the color |
| 71 | + self.ln.set_color(next(self.color_cyle)) |
| 72 | + |
| 73 | + # ask the GUI to re-draw the next time it can |
| 74 | + self.ln.figure.canvas.draw_idle() |
| 75 | + |
| 76 | +fig, ax = plt.subplots() |
| 77 | +ln, = ax.plot([], [], '-o') |
| 78 | +line_maker = LineMaker(ln) |
0 commit comments