diff --git a/doc/users/whats_new/2015-01-19_cursor_pixel_data.rst b/doc/users/whats_new/2015-01-19_cursor_pixel_data.rst new file mode 100644 index 000000000000..4490e9df35e4 --- /dev/null +++ b/doc/users/whats_new/2015-01-19_cursor_pixel_data.rst @@ -0,0 +1,6 @@ +Allow Artists to Display Pixel Data in Cursor +--------------------------------------------- + +Adds `get_pixel_data` and `format_pixel_data` methods to artists +which can be used to add zdata to the cursor display +in the status bar. Also adds an implementation for Images. diff --git a/lib/matplotlib/artist.py b/lib/matplotlib/artist.py index 7fb492367f1d..57fdf1bb673a 100644 --- a/lib/matplotlib/artist.py +++ b/lib/matplotlib/artist.py @@ -903,6 +903,22 @@ def matchfunc(x): artists.append(self) return artists + def get_cursor_data(self, event): + """ + Get the cursor data for a given event. + """ + return None + + def format_cursor_data(self, data): + """ + Return *cursor data* string formatted. + """ + try: + data[0] + except (TypeError, IndexError): + data = [data] + return ', '.join('{:0.3g}'.format(item) for item in data) + class ArtistInspector(object): """ diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 02f67136e7d0..a53a084877e6 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -1694,15 +1694,9 @@ def onRemove(self, ev): canvas.mpl_connect('mouse_press_event',canvas.onRemove) """ - def sort_artists(artists): - # This depends on stable sort and artists returned - # from get_children in z order. - L = [(h.zorder, h) for h in artists] - L.sort() - return [h for zorder, h in L] - # Find the top artist under the cursor - under = sort_artists(self.figure.hitlist(ev)) + under = self.figure.hitlist(ev) + under.sort(key=lambda x: x.zorder) h = None if under: h = under[-1] @@ -2800,6 +2794,16 @@ def mouse_move(self, event): except (ValueError, OverflowError): pass else: + artists = event.inaxes.hitlist(event) + if event.inaxes.patch in artists: + artists.remove(event.inaxes.patch) + + if artists: + artists.sort(key=lambda x: x.zorder) + a = artists[-1] + data = a.get_cursor_data(event) + if data is not None: + s += ' [%s]' % a.format_cursor_data(data) if len(self.mode): self.set_message('%s, %s' % (self.mode, s)) else: diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index 2428147218bb..189091d95eb9 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -683,6 +683,20 @@ def get_extent(self): else: return (-0.5, numcols-0.5, -0.5, numrows-0.5) + def get_cursor_data(self, event): + """Get the cursor data for a given event""" + xmin, xmax, ymin, ymax = self.get_extent() + if self.origin == 'upper': + ymin, ymax = ymax, ymin + arr = self.get_array() + data_extent = mtransforms.Bbox([[ymin, xmin], [ymax, xmax]]) + array_extent = mtransforms.Bbox([[0, 0], arr.shape[:2]]) + trans = mtransforms. BboxTransform(boxin=data_extent, + boxout=array_extent) + y, x = event.ydata, event.xdata + i, j = trans.transform_point([y, x]).astype(int) + return arr[i, j] + class NonUniformImage(AxesImage): def __init__(self, ax, **kwargs): diff --git a/lib/matplotlib/tests/test_image.py b/lib/matplotlib/tests/test_image.py index 89338d33fdad..c1e0ec7f9e82 100644 --- a/lib/matplotlib/tests/test_image.py +++ b/lib/matplotlib/tests/test_image.py @@ -158,6 +158,39 @@ def test_imsave_color_alpha(): assert_array_equal(data, arr_buf) + +@cleanup +def test_cursor_data(): + from matplotlib.backend_bases import MouseEvent + + fig, ax = plt.subplots() + im = ax.imshow(np.arange(100).reshape(10, 10), origin='upper') + + x, y = 4, 4 + xdisp, ydisp = ax.transData.transform_point([x, y]) + + event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp) + assert im.get_cursor_data(event) == 44 + + ax.clear() + im = ax.imshow(np.arange(100).reshape(10, 10), origin='lower') + + x, y = 4, 4 + xdisp, ydisp = ax.transData.transform_point([x, y]) + + event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp) + assert im.get_cursor_data(event) == 44 + + fig, ax = plt.subplots() + im = ax.imshow(np.arange(100).reshape(10, 10), extent=[0, 0.5, 0, 0.5]) + + x, y = 0.25, 0.25 + xdisp, ydisp = ax.transData.transform_point([x, y]) + + event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp) + assert im.get_cursor_data(event) == 55 + + @image_comparison(baseline_images=['image_clip']) def test_image_clip(): from math import pi