From a8d1677de1c14cf4a3827c6f77abbe5020d02ea8 Mon Sep 17 00:00:00 2001 From: daniilS Date: Wed, 8 Mar 2023 18:05:23 +0000 Subject: [PATCH] Change cursor when hovering over draggable artists Co-Authored-By: Elliott Sales de Andrade --- lib/matplotlib/axes/_base.py | 3 +++ lib/matplotlib/offsetbox.py | 34 ++++++++++++++++++++++++++++++- lib/matplotlib/tests/test_axes.py | 12 ++++++++--- 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 9c2ba63901fd..616b89401da2 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -1298,6 +1298,9 @@ def __clear(self): self.child_axes = [] self._current_image = None # strictly for pyplot via _sci, _gci self._projection_init = None # strictly for pyplot.subplot + + if hasattr(self, "legend_") and self.legend_ is not None: + self.legend_.axes = self.legend_.figure = None self.legend_ = None self.containers = [] diff --git a/lib/matplotlib/offsetbox.py b/lib/matplotlib/offsetbox.py index 13af941552be..fc5567738502 100644 --- a/lib/matplotlib/offsetbox.py +++ b/lib/matplotlib/offsetbox.py @@ -32,6 +32,7 @@ import matplotlib.path as mpath import matplotlib.text as mtext import matplotlib.transforms as mtransforms +from matplotlib.backend_tools import Cursors from matplotlib.font_manager import FontProperties from matplotlib.image import BboxImage from matplotlib.patches import ( @@ -1489,6 +1490,7 @@ def __init__(self, ref_artist, use_blit=False): if not ref_artist.pickable(): ref_artist.set_picker(True) self.got_artist = False + self._hover = False self._use_blit = use_blit and self.canvas.supports_blit callbacks = ref_artist.figure._canvas_callbacks self._disconnectors = [ @@ -1508,7 +1510,33 @@ def __init__(self, ref_artist, use_blit=False): disconnect.args[0] for disconnect in self._disconnectors[:2]]) def on_motion(self, evt): - if self._check_still_parented() and self.got_artist: + # Only check if the widget lock is available, setting it would prevent + # picking. + if not ( + self._check_still_parented() + and self.canvas.widgetlock.available(self) + and self.ref_artist.pickable() + ): + return + + picker = self.ref_artist.get_picker() + if callable(picker): + inside, _ = picker(self, evt) + else: + inside, _ = self.ref_artist.contains(evt) + + # If the mouse is moving quickly while dragging, it may leave the artist, + # but should still use the move cursor. + if inside or self.got_artist: + self._hover = True + self.canvas.set_cursor(Cursors.MOVE) + elif self._hover: + # Only change the cursor back if this is the widget that set it, to + # avoid multiple draggable widgets fighting over the cursor. + self._hover = False + self.canvas.set_cursor(Cursors.POINTER) + + if self.got_artist: dx = evt.x - self.mouse_x dy = evt.y - self.mouse_y self.update_offset(dx, dy) @@ -1554,6 +1582,10 @@ def disconnect(self): for disconnector in self._disconnectors: disconnector() + if self._hover: + self._hover = False + self.canvas.set_cursor(Cursors.POINTER) + def save_offset(self): pass diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 79c67a208364..faef3bd5a877 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -8644,13 +8644,19 @@ def test_extent_units(): def test_cla_clears_children_axes_and_fig(): fig, ax = plt.subplots() - lines = ax.plot([], [], [], []) + + lines = ax.plot([], [], [], [], label="line") img = ax.imshow([[1]]) - for art in lines + [img]: + legend = ax.legend() + + children = [*lines, img, legend] + + for art in children: assert art.axes is ax assert art.figure is fig + ax.clear() - for art in lines + [img]: + for art in children: assert art.axes is None assert art.figure is None