diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 6108f42774af..8d80964b631a 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -2342,11 +2342,11 @@ def key_press_handler(event, canvas, toolbar=None): # pan mnemonic (default key 'p') elif event.key in pan_keys: toolbar.pan() - toolbar._set_cursor(event) + toolbar._update_cursor(event) # zoom mnemonic (default key 'o') elif event.key in zoom_keys: toolbar.zoom() - toolbar._set_cursor(event) + toolbar._update_cursor(event) # saving current figure (default key 's') elif event.key in save_keys: toolbar.save_figure() @@ -2700,7 +2700,10 @@ class implementation. """ raise NotImplementedError - def _set_cursor(self, event): + def _update_cursor(self, event): + """ + Update the cursor after a mouse move event or a tool (de)activation. + """ if not event.inaxes or not self._active: if self._lastCursor != cursors.POINTER: self.set_cursor(cursors.POINTER) @@ -2715,8 +2718,30 @@ def _set_cursor(self, event): self.set_cursor(cursors.MOVE) self._lastCursor = cursors.MOVE + @contextmanager + def _wait_cursor_for_draw_cm(self): + """ + Set the cursor to a wait cursor when drawing the canvas. + + In order to avoid constantly changing the cursor when the canvas + changes frequently, do nothing if this context was triggered during the + last second. (Optimally we'd prefer only setting the wait cursor if + the *current* draw takes too long, but the current draw blocks the GUI + thread). + """ + self._draw_time, last_draw_time = ( + time.time(), getattr(self, "_draw_time", -np.inf)) + if self._draw_time - last_draw_time > 1: + try: + self.set_cursor(cursors.WAIT) + yield + finally: + self.set_cursor(self._lastCursor) + else: + yield + def mouse_move(self, event): - self._set_cursor(event) + self._update_cursor(event) if event.inaxes and event.inaxes.get_navigate(): diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py index 901b6f03fa95..f105107e8f08 100644 --- a/lib/matplotlib/backends/backend_agg.py +++ b/lib/matplotlib/backends/backend_agg.py @@ -23,8 +23,14 @@ import threading except ImportError: import dummy_threading as threading -import numpy as np +try: + from contextlib import nullcontext +except ImportError: + from contextlib import ExitStack as nullcontext # Py 3.6. from math import radians, cos, sin + +import numpy as np + from matplotlib import cbook, rcParams, __version__ from matplotlib.backend_bases import ( _Backend, FigureCanvasBase, FigureManagerBase, RendererBase) @@ -383,7 +389,10 @@ def draw(self): Draw the figure using the renderer. """ self.renderer = self.get_renderer(cleared=True) - with RendererAgg.lock: + # Acquire a lock on the shared font cache. + with RendererAgg.lock, \ + (self.toolbar._wait_cursor_for_draw_cm() if self.toolbar + else nullcontext()): self.figure.draw(self.renderer) # A GUI class may be need to update a window using this draw, so # don't forget to call the superclass. diff --git a/lib/matplotlib/backends/backend_gtk3cairo.py b/lib/matplotlib/backends/backend_gtk3cairo.py index fae367a79cb3..af1355e49d7c 100644 --- a/lib/matplotlib/backends/backend_gtk3cairo.py +++ b/lib/matplotlib/backends/backend_gtk3cairo.py @@ -1,3 +1,8 @@ +try: + from contextlib import nullcontext +except ImportError: + from contextlib import ExitStack as nullcontext # Py 3.6. + from . import backend_cairo, backend_gtk3 from .backend_gtk3 import Gtk, _BackendGTK3 from matplotlib import cbook @@ -22,18 +27,15 @@ def _render_figure(self, width, height): def on_draw_event(self, widget, ctx): """GtkDrawable draw event.""" - # toolbar = self.toolbar - # if toolbar: - # toolbar.set_cursor(cursors.WAIT) - self._renderer.set_context(ctx) - allocation = self.get_allocation() - Gtk.render_background( - self.get_style_context(), ctx, - allocation.x, allocation.y, allocation.width, allocation.height) - self._render_figure(allocation.width, allocation.height) - # if toolbar: - # toolbar.set_cursor(toolbar._lastCursor) - return False # finish event propagation? + with (self.toolbar._wait_cursor_for_draw_cm() if self.toolbar + else nullcontext()): + self._renderer.set_context(ctx) + allocation = self.get_allocation() + Gtk.render_background( + self.get_style_context(), ctx, + allocation.x, allocation.y, + allocation.width, allocation.height) + self._render_figure(allocation.width, allocation.height) @cbook.deprecated("3.1", alternative="backend_gtk3.FigureManagerGTK3")