1"""
2===============
3Pick Event Demo
4===============
5
6
7You can enable picking by setting the "picker" property of an artist
8(for example, a matplotlib Line2D, Text, Patch, Polygon, AxesImage,
9etc...)
10
11There are a variety of meanings of the picker property:
12
13* *None* - picking is disabled for this artist (default)
14
15* bool - if *True* then picking will be enabled and the artist will fire a pick
16  event if the mouse event is over the artist.
17
18  Setting ``pickradius`` will add an epsilon tolerance in points and the artist
19  will fire off an event if its data is within epsilon of the mouse event.  For
20  some artists like lines and patch collections, the artist may provide
21  additional data to the pick event that is generated, for example, the indices
22  of the data within epsilon of the pick event
23
24* function - if picker is callable, it is a user supplied function which
25  determines whether the artist is hit by the mouse event.
26
27     hit, props = picker(artist, mouseevent)
28
29  to determine the hit test.  If the mouse event is over the artist, return
30  hit=True and props is a dictionary of properties you want added to the
31  PickEvent attributes.
32
33After you have enabled an artist for picking by setting the "picker"
34property, you need to connect to the figure canvas pick_event to get
35pick callbacks on mouse press events.  For example,
36
37  def pick_handler(event):
38      mouseevent = event.mouseevent
39      artist = event.artist
40      # now do something with this...
41
42
43The pick event (matplotlib.backend_bases.PickEvent) which is passed to
44your callback is always fired with two attributes:
45
46  mouseevent - the mouse event that generate the pick event.  The
47    mouse event in turn has attributes like x and y (the coordinates in
48    display space, such as pixels from left, bottom) and xdata, ydata (the
49    coords in data space).  Additionally, you can get information about
50    which buttons were pressed, which keys were pressed, which Axes
51    the mouse is over, etc.  See matplotlib.backend_bases.MouseEvent
52    for details.
53
54  artist - the matplotlib.artist that generated the pick event.
55
56Additionally, certain artists like Line2D and PatchCollection may
57attach additional meta data like the indices into the data that meet
58the picker criteria (for example, all the points in the line that are within
59the specified epsilon tolerance)
60
61The examples below illustrate each of these methods.
62"""
63
64import matplotlib.pyplot as plt
65from matplotlib.lines import Line2D
66from matplotlib.patches import Rectangle
67from matplotlib.text import Text
68from matplotlib.image import AxesImage
69import numpy as np
70from numpy.random import rand
71
72
73# Fixing random state for reproducibility
74np.random.seed(19680801)
75
76
77def pick_simple():
78    # simple picking, lines, rectangles and text
79    fig, (ax1, ax2) = plt.subplots(2, 1)
80    ax1.set_title('click on points, rectangles or text', picker=True)
81    ax1.set_ylabel('ylabel', picker=True, bbox=dict(facecolor='red'))
82    line, = ax1.plot(rand(100), 'o', picker=True, pickradius=5)
83
84    # pick the rectangle
85    ax2.bar(range(10), rand(10), picker=True)
86    for label in ax2.get_xticklabels():  # make the xtick labels pickable
87        label.set_picker(True)
88
89    def onpick1(event):
90        if isinstance(event.artist, Line2D):
91            thisline = event.artist
92            xdata = thisline.get_xdata()
93            ydata = thisline.get_ydata()
94            ind = event.ind
95            print('onpick1 line:', np.column_stack([xdata[ind], ydata[ind]]))
96        elif isinstance(event.artist, Rectangle):
97            patch = event.artist
98            print('onpick1 patch:', patch.get_path())
99        elif isinstance(event.artist, Text):
100            text = event.artist
101            print('onpick1 text:', text.get_text())
102
103    fig.canvas.mpl_connect('pick_event', onpick1)
104
105
106def pick_custom_hit():
107    # picking with a custom hit test function
108    # you can define custom pickers by setting picker to a callable
109    # function.  The function has the signature
110    #
111    #  hit, props = func(artist, mouseevent)
112    #
113    # to determine the hit test.  if the mouse event is over the artist,
114    # return hit=True and props is a dictionary of
115    # properties you want added to the PickEvent attributes
116
117    def line_picker(line, mouseevent):
118        """
119        Find the points within a certain distance from the mouseclick in
120        data coords and attach some extra attributes, pickx and picky
121        which are the data points that were picked.
122        """
123        if mouseevent.xdata is None:
124            return False, dict()
125        xdata = line.get_xdata()
126        ydata = line.get_ydata()
127        maxd = 0.05
128        d = np.sqrt(
129            (xdata - mouseevent.xdata)**2 + (ydata - mouseevent.ydata)**2)
130
131        ind, = np.nonzero(d <= maxd)
132        if len(ind):
133            pickx = xdata[ind]
134            picky = ydata[ind]
135            props = dict(ind=ind, pickx=pickx, picky=picky)
136            return True, props
137        else:
138            return False, dict()
139
140    def onpick2(event):
141        print('onpick2 line:', event.pickx, event.picky)
142
143    fig, ax = plt.subplots()
144    ax.set_title('custom picker for line data')
145    line, = ax.plot(rand(100), rand(100), 'o', picker=line_picker)
146    fig.canvas.mpl_connect('pick_event', onpick2)
147
148
149def pick_scatter_plot():
150    # picking on a scatter plot (matplotlib.collections.RegularPolyCollection)
151
152    x, y, c, s = rand(4, 100)
153
154    def onpick3(event):
155        ind = event.ind
156        print('onpick3 scatter:', ind, x[ind], y[ind])
157
158    fig, ax = plt.subplots()
159    ax.scatter(x, y, 100*s, c, picker=True)
160    fig.canvas.mpl_connect('pick_event', onpick3)
161
162
163def pick_image():
164    # picking images (matplotlib.image.AxesImage)
165    fig, ax = plt.subplots()
166    ax.imshow(rand(10, 5), extent=(1, 2, 1, 2), picker=True)
167    ax.imshow(rand(5, 10), extent=(3, 4, 1, 2), picker=True)
168    ax.imshow(rand(20, 25), extent=(1, 2, 3, 4), picker=True)
169    ax.imshow(rand(30, 12), extent=(3, 4, 3, 4), picker=True)
170    ax.set(xlim=(0, 5), ylim=(0, 5))
171
172    def onpick4(event):
173        artist = event.artist
174        if isinstance(artist, AxesImage):
175            im = artist
176            A = im.get_array()
177            print('onpick4 image', A.shape)
178
179    fig.canvas.mpl_connect('pick_event', onpick4)
180
181
182if __name__ == '__main__':
183    pick_simple()
184    pick_custom_hit()
185    pick_scatter_plot()
186    pick_image()
187    plt.show()
188