diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 98aca9251d4c..ea5ac3eee2cb 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -2818,7 +2818,8 @@ def __init__(self, canvas): self._idPress = None self._idRelease = None self._active = None - self._lastCursor = None + # This cursor will be set after the initial draw. + self._lastCursor = cursors.POINTER self._init_toolbar() self._idDrag = self.canvas.mpl_connect( 'motion_notify_event', self.mouse_move) @@ -2904,14 +2905,13 @@ def _set_cursor(self, event): self.set_cursor(cursors.POINTER) self._lastCursor = cursors.POINTER else: - if self._active == 'ZOOM': - if self._lastCursor != cursors.SELECT_REGION: - self.set_cursor(cursors.SELECT_REGION) - self._lastCursor = cursors.SELECT_REGION + if (self._active == 'ZOOM' + and self._lastCursor != cursors.SELECT_REGION): + self.set_cursor(cursors.SELECT_REGION) + self._lastCursor = cursors.SELECT_REGION elif (self._active == 'PAN' and self._lastCursor != cursors.MOVE): self.set_cursor(cursors.MOVE) - self._lastCursor = cursors.MOVE def mouse_move(self, event): @@ -3201,6 +3201,11 @@ def save_figure(self, *args): def set_cursor(self, cursor): """Set the current cursor to one of the :class:`Cursors` enums values. + + If required by the backend, this method should trigger an update in + the backend event loop after the cursor is set, as this method may be + called e.g. before a long-running task during which the GUI is not + updated. """ def update(self): diff --git a/lib/matplotlib/backend_tools.py b/lib/matplotlib/backend_tools.py index 2fcc8a2bebfb..73f15fd42613 100644 --- a/lib/matplotlib/backend_tools.py +++ b/lib/matplotlib/backend_tools.py @@ -24,7 +24,7 @@ class Cursors(object): """Simple namespace for cursor reference""" - HAND, POINTER, SELECT_REGION, MOVE = list(range(4)) + HAND, POINTER, SELECT_REGION, MOVE, WAIT = list(range(5)) cursors = Cursors() # Views positions tool diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py index b8b0eca92b0c..984638ab6657 100644 --- a/lib/matplotlib/backends/backend_agg.py +++ b/lib/matplotlib/backends/backend_agg.py @@ -30,7 +30,7 @@ from math import radians, cos, sin from matplotlib import verbose, rcParams, __version__ from matplotlib.backend_bases import ( - _Backend, FigureCanvasBase, FigureManagerBase, RendererBase) + _Backend, FigureCanvasBase, FigureManagerBase, RendererBase, cursors) from matplotlib.cbook import maxdict from matplotlib.figure import Figure from matplotlib.font_manager import findfont, get_font @@ -422,9 +422,14 @@ def draw(self): # acquire a lock on the shared font cache RendererAgg.lock.acquire() + toolbar = self.toolbar try: + if toolbar: + toolbar.set_cursor(cursors.WAIT) self.figure.draw(self.renderer) finally: + if toolbar: + toolbar.set_cursor(toolbar._lastCursor) RendererAgg.lock.release() def get_renderer(self, cleared=False): diff --git a/lib/matplotlib/backends/backend_gtk.py b/lib/matplotlib/backends/backend_gtk.py index 757b2b7544c0..01616f13c046 100644 --- a/lib/matplotlib/backends/backend_gtk.py +++ b/lib/matplotlib/backends/backend_gtk.py @@ -54,6 +54,7 @@ cursors.HAND : gdk.Cursor(gdk.HAND2), cursors.POINTER : gdk.Cursor(gdk.LEFT_PTR), cursors.SELECT_REGION : gdk.Cursor(gdk.TCROSS), + cursors.WAIT : gdk.Cursor(gdk.WATCH), } # ref gtk+/gtk/gtkwidget.h @@ -386,16 +387,20 @@ def _render_figure(self, pixmap, width, height): def expose_event(self, widget, event): """Expose_event for all GTK backends. Should not be overridden. """ + toolbar = self.toolbar + if toolbar: + toolbar.set_cursor(cursors.WAIT) if GTK_WIDGET_DRAWABLE(self): if self._need_redraw: x, y, w, h = self.allocation self._pixmap_prepare (w, h) self._render_figure(self._pixmap, w, h) self._need_redraw = False - x, y, w, h = event.area self.window.draw_drawable (self.style.fg_gc[self.state], self._pixmap, x, y, x, y, w, h) + if toolbar: + toolbar.set_cursor(toolbar._lastCursor) return False # finish event propagation? filetypes = FigureCanvasBase.filetypes.copy() @@ -619,6 +624,7 @@ def set_message(self, s): def set_cursor(self, cursor): self.canvas.window.set_cursor(cursord[cursor]) + gtk.main_iteration() def release(self, event): try: del self._pixmapBack diff --git a/lib/matplotlib/backends/backend_gtk3.py b/lib/matplotlib/backends/backend_gtk3.py index a5f223a38753..163224e98dd3 100644 --- a/lib/matplotlib/backends/backend_gtk3.py +++ b/lib/matplotlib/backends/backend_gtk3.py @@ -51,6 +51,7 @@ cursors.HAND : Gdk.Cursor.new(Gdk.CursorType.HAND2), cursors.POINTER : Gdk.Cursor.new(Gdk.CursorType.LEFT_PTR), cursors.SELECT_REGION : Gdk.Cursor.new(Gdk.CursorType.TCROSS), + cursors.WAIT : Gdk.Cursor.new(Gdk.CursorType.WATCH), } @@ -499,7 +500,7 @@ def set_message(self, s): def set_cursor(self, cursor): self.canvas.get_property("window").set_cursor(cursord[cursor]) - #self.canvas.set_cursor(cursord[cursor]) + Gtk.main_iteration() def release(self, event): try: del self._pixmapBack diff --git a/lib/matplotlib/backends/backend_gtk3cairo.py b/lib/matplotlib/backends/backend_gtk3cairo.py index 958689d8e6ba..79ba1fc2d24d 100644 --- a/lib/matplotlib/backends/backend_gtk3cairo.py +++ b/lib/matplotlib/backends/backend_gtk3cairo.py @@ -6,6 +6,7 @@ from . import backend_cairo, backend_gtk3 from .backend_cairo import cairo, HAS_CAIRO_CFFI from .backend_gtk3 import _BackendGTK3 +from matplotlib.backend_bases import cursors from matplotlib.figure import Figure @@ -35,10 +36,15 @@ def _render_figure(self, width, height): def on_draw_event(self, widget, ctx): """ GtkDrawable draw event, like expose_event in GTK 2.X """ + toolbar = self.toolbar + if toolbar: + toolbar.set_cursor(cursors.WAIT) self._renderer.set_context(ctx) allocation = self.get_allocation() x, y, w, h = allocation.x, allocation.y, allocation.width, allocation.height self._render_figure(w, h) + if toolbar: + toolbar.set_cursor(toolbar._lastCursor) return False # finish event propagation? diff --git a/lib/matplotlib/backends/backend_qt5.py b/lib/matplotlib/backends/backend_qt5.py index 9b924ce8ecb1..8407a6111d22 100644 --- a/lib/matplotlib/backends/backend_qt5.py +++ b/lib/matplotlib/backends/backend_qt5.py @@ -90,6 +90,7 @@ cursors.HAND: QtCore.Qt.PointingHandCursor, cursors.POINTER: QtCore.Qt.ArrowCursor, cursors.SELECT_REGION: QtCore.Qt.CrossCursor, + cursors.WAIT: QtCore.Qt.WaitCursor, } diff --git a/lib/matplotlib/backends/backend_tkagg.py b/lib/matplotlib/backends/backend_tkagg.py index f6190d4f369e..781ef28d7c4a 100644 --- a/lib/matplotlib/backends/backend_tkagg.py +++ b/lib/matplotlib/backends/backend_tkagg.py @@ -45,6 +45,7 @@ cursors.HAND: "hand2", cursors.POINTER: "arrow", cursors.SELECT_REGION: "tcross", + cursors.WAIT: "watch", } @@ -697,6 +698,7 @@ def release(self, event): def set_cursor(self, cursor): self.window.configure(cursor=cursord[cursor]) + self.window.update_idletasks() def _Button(self, text, file, command, extension='.gif'): img_file = os.path.join( diff --git a/lib/matplotlib/backends/backend_wx.py b/lib/matplotlib/backends/backend_wx.py index f7df0707e637..ae99ffdbe3db 100644 --- a/lib/matplotlib/backends/backend_wx.py +++ b/lib/matplotlib/backends/backend_wx.py @@ -1474,6 +1474,7 @@ def updateButtonText(self, lst): cursors.HAND: wx.CURSOR_HAND, cursors.POINTER: wx.CURSOR_ARROW, cursors.SELECT_REGION: wx.CURSOR_CROSS, + cursors.WAIT: wx.CURSOR_WAIT, } @@ -1594,6 +1595,7 @@ def save_figure(self, *args): def set_cursor(self, cursor): cursor = wxc.Cursor(cursord[cursor]) self.canvas.SetCursor(cursor) + self.canvas.Update() def release(self, event): try: