Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 01a1c49

Browse files
authored
Merge pull request #20620 from QuLogic/canvas-cursor
Move set_cursor from the toolbar to FigureCanvas.
2 parents 3ff6d11 + d380f53 commit 01a1c49

File tree

10 files changed

+115
-61
lines changed

10 files changed

+115
-61
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
``NavigationToolbar2.set_cursor`` and ``backend_tools.SetCursorBase.set_cursor``
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
Instead, use the `.FigureCanvasBase.set_cursor` method on the canvas (available
4+
as the ``canvas`` attribute on the toolbar or the Figure.)
5+
6+
``backend_tools.SetCursorBase`` and subclasses
7+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8+
``backend_tools.SetCursorBase`` was subclassed to provide backend-specific
9+
implementations of ``set_cursor``. As that is now deprecated, the subclassing
10+
is no longer necessary. Consequently, the following subclasses are also
11+
deprecated:
12+
13+
- ``matplotlib.backends.backend_gtk3.SetCursorGTK3``
14+
- ``matplotlib.backends.backend_qt5.SetCursorQt``
15+
- ``matplotlib.backends._backend_tk.SetCursorTk``
16+
- ``matplotlib.backends.backend_wx.SetCursorWx``
17+
18+
Instead, use the `.backend_tools.ToolSetCursor` class.

lib/matplotlib/backend_bases.py

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2011,6 +2011,24 @@ def release_mouse(self, ax):
20112011
if self.mouse_grabber is ax:
20122012
self.mouse_grabber = None
20132013

2014+
def set_cursor(self, cursor):
2015+
"""
2016+
Set the current cursor.
2017+
2018+
This may have no effect if the backend does not display anything.
2019+
2020+
If required by the backend, this method should trigger an update in
2021+
the backend event loop after the cursor is set, as this method may be
2022+
called e.g. before a long-running task during which the GUI is not
2023+
updated.
2024+
2025+
Parameters
2026+
----------
2027+
cursor : `.Cursors`
2028+
The cursor to dispay over the canvas. Note: some backends may
2029+
change the cursor for the entire window.
2030+
"""
2031+
20142032
def draw(self, *args, **kwargs):
20152033
"""
20162034
Render the `.Figure`.
@@ -2864,9 +2882,6 @@ class NavigationToolbar2:
28642882
:meth:`save_figure`
28652883
save the current figure
28662884
2867-
:meth:`set_cursor`
2868-
if you want the pointer icon to change
2869-
28702885
:meth:`draw_rubberband` (optional)
28712886
draw the zoom to rect "rubberband" rectangle
28722887
@@ -2914,7 +2929,7 @@ def __init__(self, canvas):
29142929
canvas.toolbar = self
29152930
self._nav_stack = cbook.Stack()
29162931
# This cursor will be set after the initial draw.
2917-
self._lastCursor = cursors.POINTER
2932+
self._lastCursor = tools.Cursors.POINTER
29182933

29192934
self._id_press = self.canvas.mpl_connect(
29202935
'button_press_event', self._zoom_pan_handler)
@@ -2983,16 +2998,16 @@ def _update_cursor(self, event):
29832998
"""
29842999
if self.mode and event.inaxes and event.inaxes.get_navigate():
29853000
if (self.mode == _Mode.ZOOM
2986-
and self._lastCursor != cursors.SELECT_REGION):
2987-
self.set_cursor(cursors.SELECT_REGION)
2988-
self._lastCursor = cursors.SELECT_REGION
3001+
and self._lastCursor != tools.Cursors.SELECT_REGION):
3002+
self.canvas.set_cursor(tools.Cursors.SELECT_REGION)
3003+
self._lastCursor = tools.Cursors.SELECT_REGION
29893004
elif (self.mode == _Mode.PAN
2990-
and self._lastCursor != cursors.MOVE):
2991-
self.set_cursor(cursors.MOVE)
2992-
self._lastCursor = cursors.MOVE
2993-
elif self._lastCursor != cursors.POINTER:
2994-
self.set_cursor(cursors.POINTER)
2995-
self._lastCursor = cursors.POINTER
3005+
and self._lastCursor != tools.Cursors.MOVE):
3006+
self.canvas.set_cursor(tools.Cursors.MOVE)
3007+
self._lastCursor = tools.Cursors.MOVE
3008+
elif self._lastCursor != tools.Cursors.POINTER:
3009+
self.canvas.set_cursor(tools.Cursors.POINTER)
3010+
self._lastCursor = tools.Cursors.POINTER
29963011

29973012
@contextmanager
29983013
def _wait_cursor_for_draw_cm(self):
@@ -3009,10 +3024,10 @@ def _wait_cursor_for_draw_cm(self):
30093024
time.time(), getattr(self, "_draw_time", -np.inf))
30103025
if self._draw_time - last_draw_time > 1:
30113026
try:
3012-
self.set_cursor(cursors.WAIT)
3027+
self.canvas.set_cursor(tools.Cursors.WAIT)
30133028
yield
30143029
finally:
3015-
self.set_cursor(self._lastCursor)
3030+
self.canvas.set_cursor(self._lastCursor)
30163031
else:
30173032
yield
30183033

@@ -3230,6 +3245,7 @@ def save_figure(self, *args):
32303245
"""Save the current figure."""
32313246
raise NotImplementedError
32323247

3248+
@_api.deprecated("3.5", alternative="canvas.set_cursor")
32333249
def set_cursor(self, cursor):
32343250
"""
32353251
Set the current cursor to one of the :class:`Cursors` enums values.
@@ -3239,6 +3255,7 @@ def set_cursor(self, cursor):
32393255
called e.g. before a long-running task during which the GUI is not
32403256
updated.
32413257
"""
3258+
self.canvas.set_cursor(cursor)
32423259

32433260
def update(self):
32443261
"""Reset the axes stack."""

lib/matplotlib/backend_managers.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,16 @@ def add_tool(self, name, tool, *args, **kwargs):
254254
'exists, not added')
255255
return self._tools[name]
256256

257+
if name == 'cursor' and tool_cls != tools.SetCursorBase:
258+
_api.warn_deprecated("3.5",
259+
message="Overriding ToolSetCursor with "
260+
f"{tool_cls.__qualname__} was only "
261+
"necessary to provide the .set_cursor() "
262+
"method, which is deprecated since "
263+
"%(since)s and will be removed "
264+
"%(removal)s. Please report this to the "
265+
f"{tool_cls.__module__} author.")
266+
257267
tool_obj = tool_cls(self, name, *args, **kwargs)
258268
self._tools[name] = tool_obj
259269

lib/matplotlib/backend_tools.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
import matplotlib as mpl
2424
from matplotlib._pylab_helpers import Gcf
25-
from matplotlib import cbook
25+
from matplotlib import _api, cbook
2626

2727

2828
class Cursors(enum.IntEnum): # Must subclass int for the macOS backend.
@@ -266,24 +266,28 @@ def _add_tool_cbk(self, event):
266266
self._add_tool(event.tool)
267267

268268
def _set_cursor_cbk(self, event):
269-
if not event:
269+
if not event or not self.canvas:
270270
return
271271
if (self._current_tool and getattr(event, "inaxes", None)
272272
and event.inaxes.get_navigate()):
273273
if self._last_cursor != self._current_tool.cursor:
274-
self.set_cursor(self._current_tool.cursor)
274+
self.canvas.set_cursor(self._current_tool.cursor)
275275
self._last_cursor = self._current_tool.cursor
276276
elif self._last_cursor != self._default_cursor:
277-
self.set_cursor(self._default_cursor)
277+
self.canvas.set_cursor(self._default_cursor)
278278
self._last_cursor = self._default_cursor
279279

280+
@_api.deprecated("3.5", alternative="figure.canvas.set_cursor")
280281
def set_cursor(self, cursor):
281282
"""
282283
Set the cursor.
283-
284-
This method has to be implemented per backend.
285284
"""
286-
raise NotImplementedError
285+
self.canvas.set_cursor(cursor)
286+
287+
288+
# This exists solely for deprecation warnings; remove with
289+
# SetCursorBase.set_cursor.
290+
ToolSetCursor = SetCursorBase
287291

288292

289293
class ToolCursorPosition(ToolBase):

lib/matplotlib/backends/_backend_tk.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -794,6 +794,7 @@ def remove_rubberband(self):
794794
del self.lastrect
795795

796796

797+
@_api.deprecated("3.5", alternative="ToolSetCursor")
797798
class SetCursorTk(backend_tools.SetCursorBase):
798799
def set_cursor(self, cursor):
799800
NavigationToolbar2Tk.set_cursor(
@@ -907,7 +908,6 @@ def trigger(self, *args):
907908

908909
backend_tools.ToolSaveFigure = SaveFigureTk
909910
backend_tools.ToolConfigureSubplots = ConfigureSubplotsTk
910-
backend_tools.ToolSetCursor = SetCursorTk
911911
backend_tools.ToolRubberband = RubberbandTk
912912
backend_tools.ToolHelp = HelpTk
913913
backend_tools.ToolCopyToClipboard = backend_tools.ToolCopyToClipboardBase

lib/matplotlib/backends/backend_gtk3.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -90,15 +90,15 @@ def _create_application():
9090

9191
@functools.lru_cache()
9292
def _mpl_to_gtk_cursor(mpl_cursor):
93-
name = {
93+
name = _api.check_getitem({
9494
Cursors.MOVE: "move",
9595
Cursors.HAND: "pointer",
9696
Cursors.POINTER: "default",
9797
Cursors.SELECT_REGION: "crosshair",
9898
Cursors.WAIT: "wait",
9999
Cursors.RESIZE_HORIZONTAL: "ew-resize",
100100
Cursors.RESIZE_VERTICAL: "ns-resize",
101-
}[mpl_cursor]
101+
}, cursor=mpl_cursor)
102102
return Gdk.Cursor.new_from_name(Gdk.Display.get_default(), name)
103103

104104

@@ -188,6 +188,14 @@ def __init__(self, figure=None):
188188
def destroy(self):
189189
self.close_event()
190190

191+
def set_cursor(self, cursor):
192+
# docstring inherited
193+
window = self.get_property("window")
194+
if window is not None:
195+
window.set_cursor(_mpl_to_gtk_cursor(cursor))
196+
context = GLib.MainContext.default()
197+
context.iteration(True)
198+
191199
def scroll_event(self, widget, event):
192200
x = event.x
193201
# flipy so y=0 is bottom of canvas
@@ -533,13 +541,6 @@ def set_message(self, s):
533541
escaped = GLib.markup_escape_text(s)
534542
self.message.set_markup(f'<small>{escaped}</small>')
535543

536-
def set_cursor(self, cursor):
537-
window = self.canvas.get_property("window")
538-
if window is not None:
539-
window.set_cursor(_mpl_to_gtk_cursor(cursor))
540-
context = GLib.MainContext.default()
541-
context.iteration(True)
542-
543544
def draw_rubberband(self, event, x0, y0, x1, y1):
544545
height = self.canvas.figure.bbox.height
545546
y1 = height - y1
@@ -717,6 +718,7 @@ class PseudoToolbar:
717718
return NavigationToolbar2GTK3.save_figure(PseudoToolbar())
718719

719720

721+
@_api.deprecated("3.5", alternative="ToolSetCursor")
720722
class SetCursorGTK3(backend_tools.SetCursorBase):
721723
def set_cursor(self, cursor):
722724
NavigationToolbar2GTK3.set_cursor(
@@ -850,7 +852,6 @@ def error_msg_gtk(msg, parent=None):
850852

851853
backend_tools.ToolSaveFigure = SaveFigureGTK3
852854
backend_tools.ToolConfigureSubplots = ConfigureSubplotsGTK3
853-
backend_tools.ToolSetCursor = SetCursorGTK3
854855
backend_tools.ToolRubberband = RubberbandGTK3
855856
backend_tools.ToolHelp = HelpGTK3
856857
backend_tools.ToolCopyToClipboard = ToolCopyToClipboardGTK3

lib/matplotlib/backends/backend_macosx.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ def _set_device_scale(self, value):
3939
self._dpi_ratio, old_value = value, self._dpi_ratio
4040
self.figure.dpi = self.figure.dpi / old_value * self._dpi_ratio
4141

42+
def set_cursor(self, cursor):
43+
# docstring inherited
44+
_macosx.set_cursor(cursor)
45+
4246
def _draw(self):
4347
renderer = self.get_renderer(cleared=self.figure.stale)
4448
if self.figure.stale:
@@ -108,9 +112,6 @@ def release_zoom(self, event):
108112
super().release_zoom(event)
109113
self.canvas.remove_rubberband()
110114

111-
def set_cursor(self, cursor):
112-
_macosx.set_cursor(cursor)
113-
114115
def save_figure(self, *args):
115116
filename = _macosx.choose_save_file('Save the figure',
116117
self.canvas.get_default_filename())

lib/matplotlib/backends/backend_qt5.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,10 @@ def showEvent(self, event):
241241
window.screenChanged.connect(self._update_screen)
242242
self._update_screen(window.screen())
243243

244+
def set_cursor(self, cursor):
245+
# docstring inherited
246+
self.setCursor(_api.check_getitem(cursord, cursor=cursor))
247+
244248
def enterEvent(self, event):
245249
try:
246250
x, y = self.mouseEventCoords(event.pos())
@@ -702,9 +706,6 @@ def set_message(self, s):
702706
if self.coordinates:
703707
self.locLabel.setText(s)
704708

705-
def set_cursor(self, cursor):
706-
self.canvas.setCursor(cursord[cursor])
707-
708709
def draw_rubberband(self, event, x0, y0, x1, y1):
709710
height = self.canvas.figure.bbox.height
710711
y1 = height - y1
@@ -931,6 +932,7 @@ def trigger(self, *args):
931932
self._make_classic_style_pseudo_toolbar())
932933

933934

935+
@_api.deprecated("3.5", alternative="ToolSetCursor")
934936
class SetCursorQt(backend_tools.SetCursorBase):
935937
def set_cursor(self, cursor):
936938
NavigationToolbar2QT.set_cursor(
@@ -960,7 +962,6 @@ def trigger(self, *args, **kwargs):
960962

961963
backend_tools.ToolSaveFigure = SaveFigureQt
962964
backend_tools.ToolConfigureSubplots = ConfigureSubplotsQt
963-
backend_tools.ToolSetCursor = SetCursorQt
964965
backend_tools.ToolRubberband = RubberbandQt
965966
backend_tools.ToolHelp = HelpQt
966967
backend_tools.ToolCopyToClipboard = ToolCopyToClipboardQT

lib/matplotlib/backends/backend_webagg_core.py

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,19 @@ def blit(self, bbox=None):
157157
def draw_idle(self):
158158
self.send_event("draw")
159159

160+
def set_cursor(self, cursor):
161+
# docstring inherited
162+
cursor = _api.check_getitem({
163+
backend_tools.Cursors.HAND: 'pointer',
164+
backend_tools.Cursors.POINTER: 'default',
165+
backend_tools.Cursors.SELECT_REGION: 'crosshair',
166+
backend_tools.Cursors.MOVE: 'move',
167+
backend_tools.Cursors.WAIT: 'wait',
168+
backend_tools.Cursors.RESIZE_HORIZONTAL: 'ew-resize',
169+
backend_tools.Cursors.RESIZE_VERTICAL: 'ns-resize',
170+
}, cursor=cursor)
171+
self.send_event('cursor', cursor=cursor)
172+
160173
def set_image_mode(self, mode):
161174
"""
162175
Set the image mode for any subsequent images which will be sent
@@ -362,30 +375,18 @@ class NavigationToolbar2WebAgg(backend_bases.NavigationToolbar2):
362375
if name_of_method in _ALLOWED_TOOL_ITEMS
363376
]
364377

378+
cursor = _api.deprecate_privatize_attribute("3.5")
379+
365380
def __init__(self, canvas):
366381
self.message = ''
367-
self.cursor = None
382+
self._cursor = None # Remove with deprecation.
368383
super().__init__(canvas)
369384

370385
def set_message(self, message):
371386
if message != self.message:
372387
self.canvas.send_event("message", message=message)
373388
self.message = message
374389

375-
def set_cursor(self, cursor):
376-
if cursor != self.cursor:
377-
cursor = {
378-
backend_tools.Cursors.HAND: 'pointer',
379-
backend_tools.Cursors.POINTER: 'default',
380-
backend_tools.Cursors.SELECT_REGION: 'crosshair',
381-
backend_tools.Cursors.MOVE: 'move',
382-
backend_tools.Cursors.WAIT: 'wait',
383-
backend_tools.Cursors.RESIZE_HORIZONTAL: 'ew-resize',
384-
backend_tools.Cursors.RESIZE_VERTICAL: 'ns-resize',
385-
}[cursor]
386-
self.canvas.send_event("cursor", cursor=cursor)
387-
self.cursor = cursor
388-
389390
def draw_rubberband(self, event, x0, y0, x1, y1):
390391
self.canvas.send_event(
391392
"rubberband", x0=x0, y0=y0, x1=x1, y1=y1)

0 commit comments

Comments
 (0)