From 9a2311d67ed16e1091568a50fc9f9f72a9cd1a09 Mon Sep 17 00:00:00 2001 From: Kyle Sunden Date: Wed, 1 Feb 2023 17:00:28 -0600 Subject: [PATCH 1/6] Revert "Fix deprecations of *Cursor widget event handlers" This reverts commit d2a326639d609e2ad7e2224ceaa4214acf8997c3. --- lib/matplotlib/widgets.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index 9668c0233d9f..559ba1b2aed2 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -1971,7 +1971,7 @@ def __init__(self, ax, horizOn=True, vertOn=True, useblit=False, needclear = _api.deprecate_privatize_attribute("3.7") - @_api.deprecated('3.7') + @_api.deprecated('3.5') def clear(self, event): """Internal event handler to clear the cursor.""" self._clear(event) @@ -1987,7 +1987,7 @@ def _clear(self, event): if self.useblit: self.background = self.canvas.copy_from_bbox(self.ax.bbox) - onmove = _api.deprecate_privatize_attribute('3.7') + onmove = _api.deprecate_privatize_attribute('3.5') def _onmove(self, event): """Internal event handler to draw the cursor when the mouse moves.""" @@ -2117,7 +2117,7 @@ def disconnect(self): canvas.mpl_disconnect(cid) info["cids"].clear() - @_api.deprecated('3.7') + @_api.deprecated('3.5') def clear(self, event): """Clear the cursor.""" if self.ignore(event): @@ -2134,7 +2134,7 @@ def _clear(self, event): for canvas, info in self._canvas_infos.items(): info["background"] = canvas.copy_from_bbox(canvas.figure.bbox) - onmove = _api.deprecate_privatize_attribute('3.7') + onmove = _api.deprecate_privatize_attribute('3.5') def _onmove(self, event): if (self.ignore(event) From 1965b091038478013f343bd3bb84f2bbff1ce2ef Mon Sep 17 00:00:00 2001 From: Kyle Sunden Date: Wed, 1 Feb 2023 17:19:59 -0600 Subject: [PATCH 2/6] Revert: Deprecate access to Cursor/MultiCursor event handlers. --- lib/matplotlib/tests/test_widgets.py | 4 +-- lib/matplotlib/widgets.py | 48 ++++++++-------------------- 2 files changed, 16 insertions(+), 36 deletions(-) diff --git a/lib/matplotlib/tests/test_widgets.py b/lib/matplotlib/tests/test_widgets.py index 4f0bfb325b95..6e49ae794d87 100644 --- a/lib/matplotlib/tests/test_widgets.py +++ b/lib/matplotlib/tests/test_widgets.py @@ -1654,7 +1654,7 @@ def test_MultiCursor(horizOn, vertOn): # Can't use `do_event` as that helper requires the widget # to have a single .ax attribute. event = mock_event(ax1, xdata=.5, ydata=.25) - multi._onmove(event) + multi.onmove(event) # the lines in the first two ax should both move for l in multi.vlines: @@ -1680,7 +1680,7 @@ def test_MultiCursor(horizOn, vertOn): # test a move event in an Axes not part of the MultiCursor # the lines in ax1 and ax2 should not have moved. event = mock_event(ax3, xdata=.75, ydata=.75) - multi._onmove(event) + multi.onmove(event) for l in multi.vlines: assert l.get_xdata() == (.5, .5) for l in multi.hlines: diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index 559ba1b2aed2..f05c3e228463 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -1953,8 +1953,8 @@ def __init__(self, ax, horizOn=True, vertOn=True, useblit=False, **lineprops): super().__init__(ax) - self.connect_event('motion_notify_event', self._onmove) - self.connect_event('draw_event', self._clear) + self.connect_event('motion_notify_event', self.onmove) + self.connect_event('draw_event', self.clear) self.visible = True self.horizOn = horizOn @@ -1967,29 +1967,18 @@ def __init__(self, ax, horizOn=True, vertOn=True, useblit=False, self.linev = ax.axvline(ax.get_xbound()[0], visible=False, **lineprops) self.background = None - self._needclear = False - - needclear = _api.deprecate_privatize_attribute("3.7") + self.needclear = False - @_api.deprecated('3.5') def clear(self, event): - """Internal event handler to clear the cursor.""" - self._clear(event) - if self.ignore(event): - return - self.linev.set_visible(False) - self.lineh.set_visible(False) - - def _clear(self, event): """Internal event handler to clear the cursor.""" if self.ignore(event): return if self.useblit: self.background = self.canvas.copy_from_bbox(self.ax.bbox) + self.linev.set_visible(False) + self.lineh.set_visible(False) - onmove = _api.deprecate_privatize_attribute('3.5') - - def _onmove(self, event): + def onmove(self, event): """Internal event handler to draw the cursor when the mouse moves.""" if self.ignore(event): return @@ -1999,11 +1988,11 @@ def _onmove(self, event): self.linev.set_visible(False) self.lineh.set_visible(False) - if self._needclear: + if self.needclear: self.canvas.draw() - self._needclear = False + self.needclear = False return - self._needclear = True + self.needclear = True self.linev.set_xdata((event.xdata, event.xdata)) self.linev.set_visible(self.visible and self.vertOn) @@ -2106,8 +2095,8 @@ def connect(self): """Connect events.""" for canvas, info in self._canvas_infos.items(): info["cids"] = [ - canvas.mpl_connect('motion_notify_event', self._onmove), - canvas.mpl_connect('draw_event', self._clear), + canvas.mpl_connect('motion_notify_event', self.onmove), + canvas.mpl_connect('draw_event', self.clear), ] def disconnect(self): @@ -2117,26 +2106,17 @@ def disconnect(self): canvas.mpl_disconnect(cid) info["cids"].clear() - @_api.deprecated('3.5') def clear(self, event): - """Clear the cursor.""" - if self.ignore(event): - return - self._clear(event) - for line in self.vlines + self.hlines: - line.set_visible(False) - - def _clear(self, event): """Clear the cursor.""" if self.ignore(event): return if self.useblit: for canvas, info in self._canvas_infos.items(): info["background"] = canvas.copy_from_bbox(canvas.figure.bbox) + for line in self.vlines + self.hlines: + line.set_visible(False) - onmove = _api.deprecate_privatize_attribute('3.5') - - def _onmove(self, event): + def onmove(self, event): if (self.ignore(event) or event.inaxes not in self.axes or not event.canvas.widgetlock.available(self)): From 7d5c8a517b228e928d464b2a57b98a919571645b Mon Sep 17 00:00:00 2001 From: Kyle Sunden Date: Wed, 1 Feb 2023 18:29:11 -0600 Subject: [PATCH 3/6] Add simulated mouse events to cursor demos So that the cursors show up on online docs --- examples/event_handling/cursor_demo.py | 21 +++++++++++++++++++++ examples/widgets/annotated_cursor.py | 14 ++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/examples/event_handling/cursor_demo.py b/examples/event_handling/cursor_demo.py index 96dacee09004..6c12d67ae6d4 100644 --- a/examples/event_handling/cursor_demo.py +++ b/examples/event_handling/cursor_demo.py @@ -28,6 +28,8 @@ import matplotlib.pyplot as plt import numpy as np +from matplotlib.backend_bases import MouseEvent + class Cursor: """ @@ -71,6 +73,11 @@ def on_mouse_move(self, event): cursor = Cursor(ax) fig.canvas.mpl_connect('motion_notify_event', cursor.on_mouse_move) +# Simulate a mouse move to (0.5, 0.5), needed for online docs +t = ax.transData +MouseEvent( + "motion_notify_event", ax.figure.canvas, *t.transform((0.5, 0.5)) +)._process() # %% # Faster redrawing using blitting @@ -85,6 +92,7 @@ def on_mouse_move(self, event): # created whenever the figure changes. This is achieved by connecting to the # ``'draw_event'``. + class BlittedCursor: """ A cross-hair cursor using blitting for faster redraw. @@ -152,6 +160,11 @@ def on_mouse_move(self, event): blitted_cursor = BlittedCursor(ax) fig.canvas.mpl_connect('motion_notify_event', blitted_cursor.on_mouse_move) +# Simulate a mouse move to (0.5, 0.5), needed for online docs +t = ax.transData +MouseEvent( + "motion_notify_event", ax.figure.canvas, *t.transform((0.5, 0.5)) +)._process() # %% # Snapping to data points @@ -165,6 +178,7 @@ def on_mouse_move(self, event): # the lag due to many redraws. Of course, blitting could still be added on top # for additional speedup. + class SnappingCursor: """ A cross-hair cursor that snaps to the data point of a line, which is @@ -218,4 +232,11 @@ def on_mouse_move(self, event): line, = ax.plot(x, y, 'o') snap_cursor = SnappingCursor(ax, line) fig.canvas.mpl_connect('motion_notify_event', snap_cursor.on_mouse_move) + +# Simulate a mouse move to (0.5, 0.5), needed for online docs +t = ax.transData +MouseEvent( + "motion_notify_event", ax.figure.canvas, *t.transform((0.5, 0.5)) +)._process() + plt.show() diff --git a/examples/widgets/annotated_cursor.py b/examples/widgets/annotated_cursor.py index eabec859fecb..a10a010942e0 100644 --- a/examples/widgets/annotated_cursor.py +++ b/examples/widgets/annotated_cursor.py @@ -24,6 +24,8 @@ import numpy as np import matplotlib.pyplot as plt +from matplotlib.backend_bases import MouseEvent + class AnnotatedCursor(Cursor): """ @@ -312,6 +314,12 @@ def _update(self): color='red', linewidth=2) +# Simulate a mouse move to (-2, 10), needed for online docs +t = ax.transData +MouseEvent( + "motion_notify_event", ax.figure.canvas, *t.transform((-2, 10)) +)._process() + plt.show() # %% @@ -339,4 +347,10 @@ def _update(self): useblit=True, color='red', linewidth=2) +# Simulate a mouse move to (-2, 10), needed for online docs +t = ax.transData +MouseEvent( + "motion_notify_event", ax.figure.canvas, *t.transform((-2, 10)) +)._process() + plt.show() From 701042d9c01c500b2eca5e976594051df7623bb6 Mon Sep 17 00:00:00 2001 From: Kyle Sunden Date: Wed, 1 Feb 2023 19:19:22 -0600 Subject: [PATCH 4/6] remove calls to set_visible(False) --- lib/matplotlib/widgets.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index f05c3e228463..6315450a1573 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -1975,8 +1975,6 @@ def clear(self, event): return if self.useblit: self.background = self.canvas.copy_from_bbox(self.ax.bbox) - self.linev.set_visible(False) - self.lineh.set_visible(False) def onmove(self, event): """Internal event handler to draw the cursor when the mouse moves.""" @@ -2113,8 +2111,6 @@ def clear(self, event): if self.useblit: for canvas, info in self._canvas_infos.items(): info["background"] = canvas.copy_from_bbox(canvas.figure.bbox) - for line in self.vlines + self.hlines: - line.set_visible(False) def onmove(self, event): if (self.ignore(event) From 825059e70da3ba7c955e593303dbcb308091f24b Mon Sep 17 00:00:00 2001 From: Kyle Sunden Date: Wed, 1 Feb 2023 21:38:45 -0600 Subject: [PATCH 5/6] One last _onmove in tests that was not caught by revert --- lib/matplotlib/tests/test_widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/test_widgets.py b/lib/matplotlib/tests/test_widgets.py index 6e49ae794d87..9809dde58201 100644 --- a/lib/matplotlib/tests/test_widgets.py +++ b/lib/matplotlib/tests/test_widgets.py @@ -1671,7 +1671,7 @@ def test_MultiCursor(horizOn, vertOn): multi.horizOn = not multi.horizOn multi.vertOn = not multi.vertOn event = mock_event(ax1, xdata=.5, ydata=.25) - multi._onmove(event) + multi.onmove(event) assert len([line for line in multi.vlines if line.get_visible()]) == ( 0 if vertOn else 2) assert len([line for line in multi.hlines if line.get_visible()]) == ( From 17542db2d8caaba9407ea7d6c1d56ac84489a6d1 Mon Sep 17 00:00:00 2001 From: Kyle Sunden Date: Thu, 2 Feb 2023 12:19:13 -0600 Subject: [PATCH 6/6] Force extra draw to excercise clear machinery Co-authored-by: Thomas A Caswell --- lib/matplotlib/tests/test_widgets.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/matplotlib/tests/test_widgets.py b/lib/matplotlib/tests/test_widgets.py index 9809dde58201..53fd95526ef9 100644 --- a/lib/matplotlib/tests/test_widgets.py +++ b/lib/matplotlib/tests/test_widgets.py @@ -1655,6 +1655,8 @@ def test_MultiCursor(horizOn, vertOn): # to have a single .ax attribute. event = mock_event(ax1, xdata=.5, ydata=.25) multi.onmove(event) + # force a draw + draw event to exercise clear + ax1.figure.canvas.draw() # the lines in the first two ax should both move for l in multi.vlines: