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

Skip to content

Switch the cursor to a busy cursor while redrawing. #6603

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 29, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions lib/matplotlib/backend_bases.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand Down
2 changes: 1 addition & 1 deletion lib/matplotlib/backend_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 6 additions & 1 deletion lib/matplotlib/backends/backend_agg.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is going to fail with new tool manager, toolbar doesn't have a set_cursor method anymore.
I have to think in a clean way of doing it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a completely different note... you are doing this to the AGG backend, a non-interactive backend. How does this code interact with the combined backends like GtkAgg and such? Would this code get called twice?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I never got an answer about why this is being done for the AGG backend.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aye, but from what I gather set_cursor will move from the toolbar to the canvas.

I'm +1 on doing this now so that we get a clean PR, but @tacaswell disagrees.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@OceanWolf, was that a response to my question? I am not sure how your response applies, since the AGG backend has a canvas, too. Why is there any cursor-handling code for the agg backend?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, It applies only in the sense that the code here has already been slated for removal. Only a partial answer to your question.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, missed the comments.
The reason to put this here is the same reason why canvas.toolbar exists on all canvases (possibly None) rather than only on interactive ones: because there is no InteractiveCanvas class in the hierarchy :-) (Note that this is orthogonal with the toolmanager question)

self.figure.draw(self.renderer)
finally:
if toolbar:
toolbar.set_cursor(toolbar._lastCursor)
RendererAgg.lock.release()

def get_renderer(self, cleared=False):
Expand Down
8 changes: 7 additions & 1 deletion lib/matplotlib/backends/backend_gtk.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion lib/matplotlib/backends/backend_gtk3.py
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}


Expand Down Expand Up @@ -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()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are you adding this cal to Gtk.main_iteration?

Copy link
Contributor Author

@anntzer anntzer Jun 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because otherwise the cursor is actually not set on the GUI (see docstring changed in CanvasBase.set_cursor). Basically, what happens is

Notification that a redraw is necessary.
Redraw starts and cursor is set.
[possibly long computation]
Return to GUI thread.

Note that the GUI event loop doesn't get to run between [cursor is set] and [possibly long computation], so depending on the details of the toolkit, the cursor change may not actually appear to the user before the end of the computation. Letting the GUI event loop run once allows it to change the cursor. (And upon testing, I see that Qt does not need this, probably because its version of setCursor triggers(?) the event loop.)


def release(self, event):
try: del self._pixmapBack
Expand Down
6 changes: 6 additions & 0 deletions lib/matplotlib/backends/backend_gtk3cairo.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -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?


Expand Down
1 change: 1 addition & 0 deletions lib/matplotlib/backends/backend_qt5.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}


Expand Down
2 changes: 2 additions & 0 deletions lib/matplotlib/backends/backend_tkagg.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
cursors.HAND: "hand2",
cursors.POINTER: "arrow",
cursors.SELECT_REGION: "tcross",
cursors.WAIT: "watch",
}


Expand Down Expand Up @@ -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(
Expand Down
2 changes: 2 additions & 0 deletions lib/matplotlib/backends/backend_wx.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}


Expand Down Expand Up @@ -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:
Expand Down