From bd4b764618dc1dfc1efad29d8e291d0bc0b8cd8c Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Tue, 9 Oct 2018 16:55:26 +0200 Subject: [PATCH 1/2] Improve formatting of imshow() cursor data when a colorbar exists. When a colorbar exists, use its formatter to format cursor data. For example, after ``` imshow([[10000, 10001]]); colorbar() ``` currently the cursor data on either pixel is rendered as 1e4, but after this patch it becomes 0.0+1e4 / 1.0+1e4, or 10000.0 / 10001.0 if `rcParams["axes.formatter.useoffset"]` is set to False. (Even though the version with the offset text may not be the most esthetic, it's clearly more informative than the current behavior...) It would be nice if this worked even for ScalarMappables that don't have a colorbar; this may include extracting the Formatter selection code out of the colorbar code into something generally applicable to ScalarMappables, or just generating a hidden colorbar "on-the-fly" if needed just for the purpose of getting its Formatter. --- doc/users/next_whats_new/2018-10-10-AL.rst | 9 +++++++++ lib/matplotlib/artist.py | 5 +++-- lib/matplotlib/cbook/__init__.py | 16 +++++++++++----- lib/matplotlib/image.py | 9 +++++++++ lib/matplotlib/tests/test_image.py | 18 ++++++++++++++++++ 5 files changed, 50 insertions(+), 7 deletions(-) create mode 100644 doc/users/next_whats_new/2018-10-10-AL.rst diff --git a/doc/users/next_whats_new/2018-10-10-AL.rst b/doc/users/next_whats_new/2018-10-10-AL.rst new file mode 100644 index 000000000000..ec12e55e88ca --- /dev/null +++ b/doc/users/next_whats_new/2018-10-10-AL.rst @@ -0,0 +1,9 @@ +Improved formatting of image values under cursor when a colorbar is present +``````````````````````````````````````````````````````````````````````````` + +When a colorbar is present, its formatter is now used to format the image +values under the mouse cursor in the status bar. For example, for an image +displaying the values 10,000 and 10,001, the statusbar will now (using default +settings) display the values as ``0.0+1e4`` and ``1.0+1e4`` (or ``10000.0`` +and ``10001.0`` if the offset-text is disabled on the colorbar), whereas both +values were previously displayed as ``1e+04``. diff --git a/lib/matplotlib/artist.py b/lib/matplotlib/artist.py index d2629951734e..3cd33728310f 100644 --- a/lib/matplotlib/artist.py +++ b/lib/matplotlib/artist.py @@ -2,6 +2,7 @@ from functools import wraps import inspect import logging +from numbers import Number import re import warnings @@ -1167,8 +1168,8 @@ def format_cursor_data(self, data): data[0] except (TypeError, IndexError): data = [data] - data_str = ', '.join('{:0.3g}'.format(item) for item in data if - isinstance(item, (np.floating, np.integer, int, float))) + data_str = ', '.join('{:0.3g}'.format(item) for item in data + if isinstance(item, Number)) return "[" + data_str + "]" @property diff --git a/lib/matplotlib/cbook/__init__.py b/lib/matplotlib/cbook/__init__.py index 22c804b88da5..22e1866dfe16 100644 --- a/lib/matplotlib/cbook/__init__.py +++ b/lib/matplotlib/cbook/__init__.py @@ -302,11 +302,17 @@ def local_over_kwdict(local_var, kwargs, *keys): def strip_math(s): - """remove latex formatting from mathtext""" - remove = (r'\mathdefault', r'\rm', r'\cal', r'\tt', r'\it', '\\', '{', '}') - s = s[1:-1] - for r in remove: - s = s.replace(r, '') + """ + Remove latex formatting from mathtext. + + Only handles fully math and fully non-math strings. + """ + if len(s) >= 2 and s[0] == s[-1] == "$": + s = s[1:-1] + remove = [ + r'\mathdefault', r'\rm', r'\cal', r'\tt', r'\it', '\\', '{', '}'] + for r in remove: + s = s.replace(r, '') return s diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index d3ce8f4bd15e..c8126ba58c95 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -906,6 +906,15 @@ def get_cursor_data(self, event): else: return arr[i, j] + def format_cursor_data(self, data): + if self.colorbar: + return ("[" + + cbook.strip_math(self.colorbar.formatter(data)) + + cbook.strip_math(self.colorbar.formatter.get_offset()) + + "]") + else: + return super().format_cursor_data(data) + class NonUniformImage(AxesImage): def __init__(self, ax, *, interpolation='nearest', **kwargs): diff --git a/lib/matplotlib/tests/test_image.py b/lib/matplotlib/tests/test_image.py index 800895067db5..4281cdfcdde3 100644 --- a/lib/matplotlib/tests/test_image.py +++ b/lib/matplotlib/tests/test_image.py @@ -262,6 +262,24 @@ def test_cursor_data(): assert im.get_cursor_data(event) is None +def test_format_cursor_data(): + from matplotlib.backend_bases import MouseEvent + + fig, ax = plt.subplots() + im = ax.imshow([[10000, 10001]]) + + xdisp, ydisp = ax.transData.transform_point([0, 0]) + event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp) + assert im.get_cursor_data(event) == 10000 + assert im.format_cursor_data(im.get_cursor_data(event)) == "[1e+04]" + + fig.colorbar(im) + fig.canvas.draw() # This is necessary to set up the colorbar formatter. + + assert im.get_cursor_data(event) == 10000 + assert im.format_cursor_data(im.get_cursor_data(event)) == "[0.0+1e4]" + + @image_comparison(baseline_images=['image_clip'], style='mpl20') def test_image_clip(): d = [[1, 2], [3, 4]] From 3b37b081c6a21e0757171bf9e7145a64d2000e2b Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Thu, 11 Oct 2018 10:22:23 +0200 Subject: [PATCH 2/2] Handle \times in strip_math to better handle LogFormatter. --- lib/matplotlib/cbook/__init__.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/cbook/__init__.py b/lib/matplotlib/cbook/__init__.py index 22e1866dfe16..4758c802a7ef 100644 --- a/lib/matplotlib/cbook/__init__.py +++ b/lib/matplotlib/cbook/__init__.py @@ -309,10 +309,18 @@ def strip_math(s): """ if len(s) >= 2 and s[0] == s[-1] == "$": s = s[1:-1] - remove = [ - r'\mathdefault', r'\rm', r'\cal', r'\tt', r'\it', '\\', '{', '}'] - for r in remove: - s = s.replace(r, '') + for tex, plain in [ + (r"\times", "x"), # Specifically for Formatter support. + (r"\mathdefault", ""), + (r"\rm", ""), + (r"\cal", ""), + (r"\tt", ""), + (r"\it", ""), + ("\\", ""), + ("{", ""), + ("}", ""), + ]: + s = s.replace(tex, plain) return s