diff --git a/lib/matplotlib/artist.py b/lib/matplotlib/artist.py index 65225adcdc50..5cc74b05a43c 100644 --- a/lib/matplotlib/artist.py +++ b/lib/matplotlib/artist.py @@ -1251,19 +1251,33 @@ def format_cursor_data(self, data): method yourself. The default implementation converts ints and floats and arrays of ints - and floats into a comma-separated string enclosed in square brackets. + and floats into a comma-separated string enclosed in square brackets, + unless the artist has an associated colorbar, in which case scalar + values are formatted using the colorbar's formatter. See Also -------- get_cursor_data """ - try: - data[0] - except (TypeError, IndexError): - data = [data] - data_str = ', '.join('{:0.3g}'.format(item) for item in data - if isinstance(item, Number)) - return "[" + data_str + "]" + if np.ndim(data) == 0 and getattr(self, "colorbar", None): + # This block logically belongs to ScalarMappable, but can't be + # implemented in it because most ScalarMappable subclasses inherit + # from Artist first and from ScalarMappable second, so + # Artist.format_cursor_data would always have precedence over + # ScalarMappable.format_cursor_data. + return ( + "[" + + cbook.strip_math( + self.colorbar.formatter.format_data_short(data)).strip() + + "]") + else: + try: + data[0] + except (TypeError, IndexError): + data = [data] + data_str = ', '.join('{:0.3g}'.format(item) for item in data + if isinstance(item, Number)) + return "[" + data_str + "]" @property def mouseover(self): diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index 58b305b7ab53..18d303391d39 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -1930,7 +1930,7 @@ def draw(self, renderer): class QuadMesh(Collection): - """ + r""" Class for the efficient drawing of a quadrilateral mesh. A quadrilateral mesh is a grid of M by N adjacent qudrilaterals that are @@ -1957,6 +1957,10 @@ class QuadMesh(Collection): Notes ----- + Unlike other `.Collection`\s, the default *pickradius* of `.QuadMesh` is 0, + i.e. `~.Artist.contains` checks whether the test point is within any of the + mesh quadrilaterals. + There exists a deprecated API version ``QuadMesh(M, N, coords)``, where the dimensions are given explicitly and ``coords`` is a (M*N, 2) array-like. This has been deprecated in Matplotlib 3.5. The following @@ -1984,8 +1988,8 @@ class QuadMesh(Collection): For example, the first entry in *coordinates* is the coordinates of the vertex at mesh coordinates (0, 0), then the one at (0, 1), then at (0, 2) .. (0, meshWidth), (1, 0), (1, 1), and so on. - """ + def __init__(self, *args, **kwargs): # signature deprecation since="3.5": Change to new signature after the # deprecation has expired. Also remove setting __init__.__signature__, @@ -2017,6 +2021,7 @@ def __init__(self, *args, **kwargs): "coordinates must be 2D; all parameters except " "coordinates will be keyword-only.") coords = np.asarray(coords, np.float64).reshape((h + 1, w + 1, 2)) + kwargs.setdefault("pickradius", 0) # end of signature deprecation code super().__init__(**kwargs) @@ -2031,7 +2036,7 @@ def __init__(self, *args, **kwargs): # Only needed during signature deprecation __init__.__signature__ = inspect.signature( lambda self, coordinates, *, - antialiased=True, shading='flat', **kwargs: None) + antialiased=True, shading='flat', pickradius=0, **kwargs: None) def get_paths(self): if self._paths is None: @@ -2162,3 +2167,11 @@ def draw(self, renderer): gc.restore() renderer.close_group(self.__class__.__name__) self.stale = False + + def get_cursor_data(self, event): + contained, info = self.contains(event) + if len(info["ind"]) == 1: + ind, = info["ind"] + return self.get_array()[ind] + else: + return None diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index 49fcb4e27092..95bb42d75c61 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -995,16 +995,6 @@ def get_cursor_data(self, event): else: return arr[i, j] - def format_cursor_data(self, data): - if np.ndim(data) == 0 and self.colorbar: - return ( - "[" - + cbook.strip_math( - self.colorbar.formatter.format_data_short(data)).strip() - + "]") - else: - return super().format_cursor_data(data) - class NonUniformImage(AxesImage): mouseover = False # This class still needs its own get_cursor_data impl. diff --git a/lib/matplotlib/tests/test_collections.py b/lib/matplotlib/tests/test_collections.py index 0f6200e6bad1..83972a31e7df 100644 --- a/lib/matplotlib/tests/test_collections.py +++ b/lib/matplotlib/tests/test_collections.py @@ -7,6 +7,7 @@ import matplotlib as mpl import matplotlib.pyplot as plt +from matplotlib.backend_bases import MouseEvent import matplotlib.collections as mcollections import matplotlib.colors as mcolors import matplotlib.transforms as mtransforms @@ -972,3 +973,16 @@ def test_array_wrong_dimensions(): pc = plt.pcolormesh(z) pc.set_array(z) # 2D is OK for Quadmesh pc.update_scalarmappable() + + +def test_quadmesh_cursor_data(): + fig, ax = plt.subplots() + *_, qm = ax.hist2d( + np.arange(11)**2, 100 + np.arange(11)**2) # width-10 bins + x, y = ax.transData.transform([1, 101]) + event = MouseEvent('motion_notify_event', fig.canvas, x, y) + assert qm.get_cursor_data(event) == 4 # (0**2, 1**2, 2**2, 3**2) + for out_xydata in []: + x, y = ax.transData.transform([-1, 101]) + event = MouseEvent('motion_notify_event', fig.canvas, x, y) + assert qm.get_cursor_data(event) is None