8000 Only set the wait cursor if the last draw was >1s ago. · matplotlib/matplotlib@5ca2e45 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5ca2e45

Browse files
committed
Only set the wait cursor if the last draw was >1s ago.
This avoids setting the wait cursor when panning or zooming (that behavior was somewhat annoying), or during an animation that renders at least one frame per second. Rename `_set_cursor` to `_update_cursor` as it is fairly different from `set_cursor` and the previous name could cause confusion. Move the defintion of cursor-related methods to a single place. In the case of gtk, just strip out the offending cursor related code as the backend is deprecated and slated for removal anyways.
1 parent 32a67d3 commit 5ca2e45

File tree

5 files changed

+62
-41
lines changed

5 files changed

+62
-41
lines changed

lib/matplotlib/backend_bases.py

Original file line numberDiff line numberDiff line change
@@ -2518,11 +2518,11 @@ def key_press_handler(event, canvas, toolbar=None):
25182518
# pan mnemonic (default key 'p')
25192519
elif event.key in pan_keys:
25202520
toolbar.pan()
2521-
toolbar._set_cursor(event)
2521+
toolbar._update_cursor(event)
25222522
# zoom mnemonic (default key 'o')
25232523
elif event.key in zoom_keys:
25242524
toolbar.zoom()
2525-
toolbar._set_cursor(event)
2525+
toolbar._update_cursor(event)
25262526
# saving current figure (default key 's')
25272527
elif event.key in save_keys:
25282528
toolbar.save_figure()
@@ -2847,7 +2847,18 @@ class implementation.
28472847
"""
28482848
raise NotImplementedError
28492849

2850-
def _set_cursor(self, event):
2850+
def set_cursor(self, cursor):
2851+
"""Set the current cursor to one of the :class:`Cursors` enums values.
2852+
2853+
If required by the backend, this method should trigger an update in
2854+
the backend event loop after the cursor is set, as this method may be
2855+
called e.g. before a long-running task during which the GUI is not
2856+
updated.
2857+
"""
2858+
2859+
def _update_cursor(self, event):
2860+
"""Update the cursor after a mouse move event or a tool (de)activation.
2861+
"""
28512862
if not event.inaxes or not self._active:
28522863
if self._lastCursor != cursors.POINTER:
28532864
self.set_cursor(cursors.POINTER)
@@ -2862,8 +2873,29 @@ def _set_cursor(self, event):
28622873
self.set_cursor(cursors.MOVE)
28632874
self._lastCursor = cursors.MOVE
28642875

2876+
@contextmanager
2877+
def _wait_cursor_for_draw_cm(self):
2878+
"""Set the cursor to a wait cursor when drawing the canvas.
2879+
2880+
In order to avoid constantly changing the cursor when the canvas
2881+
changes frequently, do nothing if this context was triggered during the
2882+
last second. (Optimally we'd prefer only setting the wait cursor if
2883+
the *current* draw takes too long, but the current draw blocks the GUI
2884+
thread.
2885+
"""
2886+
self._draw_time, last_draw_time = (
2887+
time.time(), getattr(self, "_draw_time", -np.inf))
2888+
if self._draw_time - last_draw_time > 1:
2889+
try:
2890+
self.set_cursor(cursors.WAIT)
2891+
yield
2892+
finally:
2893+
self.set_cursor(self._lastCursor)
2894+
else:
2895+
yield
2896+
28652897
def mouse_move(self, event):
2866-
self._set_cursor(event)
2898+
self._update_cursor(event)
28672899

28682900
if event.inaxes and event.inaxes.get_navigate():
28692901

@@ -3150,15 +3182,6 @@ def save_figure(self, *args):
31503182
"""Save the current figure."""
31513183
raise NotImplementedError
31523184

3153-
def set_cursor(self, cursor):
3154-
"""Set the current cursor to one of the :class:`Cursors` enums values.
3155-
3156-
If required by the backend, this method should trigger an update in
3157-
the backend event loop after the cursor is set, as this method may be
3158-
called e.g. before a long-running task during which the GUI is not
3159-
updated.
3160-
"""
3161-
31623185
def update(self):
31633186
"""Reset the axes stack."""
31643187
self._nav_stack.clear()

lib/matplotlib/backends/backend_agg.py

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,16 @@
2424

2525
import six
2626

27-
import threading
28-
import numpy as np
2927
from collections import OrderedDict
28+
try:
29+
from contextlib import ExitStack
30+
except ImportError:
31+
from contextlib2 import ExitStack # Py2
32+
import threading
3033
from math import radians, cos, sin
34+
35+
import numpy as np
36+
3137
from matplotlib import cbook, rcParams, __version__
3238
from matplotlib.backend_bases import (
3339
_Backend, FigureCanvasBase, FigureManagerBase, RendererBase, cursors)
@@ -422,18 +428,11 @@ def draw(self):
422428
Draw the figure using the renderer
423429
"""
424430
self.renderer = self.get_renderer(cleared=True)
425-
# acquire a lock on the shared font cache
426-
RendererAgg.lock.acquire()
427-
428-
toolbar = self.toolbar
429-
try:
430-
if toolbar:
431-
toolbar.set_cursor(cursors.WAIT)
431+
# Acquire a lock on the shared font cache.
432+
with RendererAgg.lock, (
433+
self.toolbar._wait_cursor_for_draw_cm() if self.toolbar
434+
else ExitStack()):
432435
self.figure.draw(self.renderer)
433-
finally:
434-
if toolbar:
435-
toolbar.set_cursor(toolbar._lastCursor)
436-
RendererAgg.lock.release()
437436

438437
def get_renderer(self, cleared=False):
439438
l, b, w, h = self.figure.bbox.bounds

lib/matplotlib/backends/backend_gtk.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -392,9 +392,6 @@ def _render_figure(self, pixmap, width, height):
392392
def expose_event(self, widget, event):
393393
"""Expose_event for all GTK backends. Should not be overridden.
394394
"""
395-
toolbar = self.toolbar
396-
if toolbar:
397-
toolbar.set_cursor(cursors.WAIT)
398395
if GTK_WIDGET_DRAWABLE(self):
399396
if self._need_redraw:
400397
x, y, w, h = self.allocation
@@ -404,8 +401,6 @@ def expose_event(self, widget, event):
404401
x, y, w, h = event.area
405402
self.window.draw_drawable (self.style.fg_gc[self.state],
406403
self._pixmap, x, y, x, y, w, h)
407-
if toolbar:
408-
toolbar.set_cursor(toolbar._lastCursor)
409404
return False # finish event propagation?
410405

411406
filetypes = FigureCanvasBase.filetypes.copy()

lib/matplotlib/backends/backend_gtk3cairo.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33

44
import six
55

6+
try:
7+
from contextlib import ExitStack
8+
except ImportError:
9+
from contextlib2 import ExitStack # Py2
10+
611
from . import backend_cairo, backend_gtk3
712
from .backend_cairo import cairo, HAS_CAIRO_CFFI
813
from .backend_gtk3 import _BackendGTK3
@@ -34,15 +39,11 @@ def _render_figure(self, width, height):
3439

3540
def on_draw_event(self, widget, ctx):
3641
"""GtkDrawable draw event."""
37-
toolbar = self.toolbar
38-
if toolbar:
39-
toolbar.set_cursor(cursors.WAIT)
40-
self._renderer.set_context(ctx)
41-
allocation = self.get_allocation()
42-
self._render_figure(allocation.width, allocation.height)
43-
if toolbar:
44-
toolbar.set_cursor(toolbar._lastCursor)
45-
return False # finish event propagation?
42+
with (self.toolbar._wait_cursor_for_draw_cm() if self.toolbar
43+
else ExitStack()):
44+
self._renderer.set_context(ctx)
45+
allocation = self.get_allocation()
46+
self._render_figure(allocation.width, allocation.height)
4647

4748

4849
class FigureManagerGTK3Cairo(backend_gtk3.FigureManagerGTK3):

setupext.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1462,7 +1462,10 @@ def get_install_requires(self):
14621462
"six>=1.10",
14631463
]
14641464
if sys.version_info < (3,):
1465-
install_requires += ["backports.functools_lru_cache"]
1465+
install_requires += [
1466+
"backports.functools_lru_cache",
1467+
"contextlib2",
1468+
]
14661469
if sys.version_info < (3,) and os.name == "posix":
14671470
install_requires += ["subprocess32"]
14681471
return install_requires

0 commit comments

Comments
 (0)
0