From 8aafcbf639b9d4870dbca66909231a4d9e4d3f64 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Tue, 11 Jul 2017 21:24:18 -0700 Subject: [PATCH 1/3] Move start_event_loop_default to start_event_loop. There's no benefit to the end user to having a deprecation warning in start_event_loop (which gets called e.g. by ginput) -- it's essentially just there to let the devs know that they could implement a more efficient (GUI toolkit-dependent) version. So just provide the default implementations instead and add a note that GUI toolkits may override them. flush_events doesn't need to raise by default, it can just return nothing for non-interactive backends. --- lib/matplotlib/backend_bases.py | 66 ++++++++------------------ lib/matplotlib/backends/backend_gtk.py | 7 --- lib/matplotlib/backends/backend_gtk3.py | 8 ---- lib/matplotlib/backends/backend_tkagg.py | 8 ---- lib/matplotlib/backends/backend_webagg.py | 11 ----- lib/matplotlib/backends/backend_webagg_core.py | 11 ----- 6 files changed, 19 insertions(+), 92 deletions(-) diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 98aca9251d4..41239b5b5bc 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -2426,55 +2426,24 @@ def new_timer(self, *args, **kwargs): return TimerBase(*args, **kwargs) def flush_events(self): - """ - Flush the GUI events for the figure. Implemented only for - backends with GUIs. - """ - raise NotImplementedError + """Flush the GUI events for the figure. - def start_event_loop(self, timeout): + GUI backends likely need to reimplement this method. """ - Start an event loop. This is used to start a blocking event - loop so that interactive functions, such as ginput and - waitforbuttonpress, can wait for events. This should not be - confused with the main GUI event loop, which is always running - and has nothing to do with this. - This is implemented only for backends with GUIs. - """ - raise NotImplementedError + def start_event_loop(self, timeout=0): + """Start a blocking event loop. - def stop_event_loop(self): - """ - Stop an event loop. This is used to stop a blocking event - loop so that interactive functions, such as ginput and - waitforbuttonpress, can wait for events. + Such an event loop is used by interactive functions, such as ginput and + waitforbuttonpress, to wait for events. This should not be confused + with the main GUI event loop, which is independent and always running. - This is implemented only for backends with GUIs. - """ - raise NotImplementedError - - def start_event_loop_default(self, timeout=0): - """ - Start an event loop. This is used to start a blocking event - loop so that interactive functions, such as ginput and - waitforbuttonpress, can wait for events. This should not be - confused with the main GUI event loop, which is always running - and has nothing to do with this. - - This function provides default event loop functionality based - on time.sleep that is meant to be used until event loop - functions for each of the GUI backends can be written. As - such, it throws a deprecated warning. + The event loop blocks until a callback function triggers + `stop_event_loop`, or *timeout* is reached. If *timeout* is negative, + never timeout. - This call blocks until a callback function triggers - stop_event_loop() or *timeout* is reached. If *timeout* is - <=0, never timeout. + Only GUI backends need to reimplement this method. """ - str = "Using default event loop until function specific" - str += " to this GUI is implemented" - warnings.warn(str, mplDeprecation) - if timeout <= 0: timeout = np.inf timestep = 0.01 @@ -2485,15 +2454,18 @@ def start_event_loop_default(self, timeout=0): time.sleep(timestep) counter += 1 - def stop_event_loop_default(self): - """ - Stop an event loop. This is used to stop a blocking event - loop so that interactive functions, such as ginput and - waitforbuttonpress, can wait for events. + def stop_event_loop(self): + """Stop the current blocking event loop. + Only GUI backends need to reimplement this method. """ self._looping = False + start_event_loop_default = cbook.deprecated( + "2.1", name="start_event_loop_default")(start_event_loop) + stop_event_loop_default = cbook.deprecated( + "2.1", name="stop_event_loop_default")(stop_event_loop) + def key_press_handler(event, canvas, toolbar=None): """ diff --git a/lib/matplotlib/backends/backend_gtk.py b/lib/matplotlib/backends/backend_gtk.py index 757b2b7544c..d8d8387dca3 100644 --- a/lib/matplotlib/backends/backend_gtk.py +++ b/lib/matplotlib/backends/backend_gtk.py @@ -475,13 +475,6 @@ def flush_events(self): gtk.gdk.flush() gtk.gdk.threads_leave() - def start_event_loop(self,timeout): - FigureCanvasBase.start_event_loop_default(self,timeout) - start_event_loop.__doc__=FigureCanvasBase.start_event_loop_default.__doc__ - - def stop_event_loop(self): - FigureCanvasBase.stop_event_loop_default(self) - stop_event_loop.__doc__=FigureCanvasBase.stop_event_loop_default.__doc__ class FigureManagerGTK(FigureManagerBase): diff --git a/lib/matplotlib/backends/backend_gtk3.py b/lib/matplotlib/backends/backend_gtk3.py index a5f223a3875..df8cec6ec4a 100644 --- a/lib/matplotlib/backends/backend_gtk3.py +++ b/lib/matplotlib/backends/backend_gtk3.py @@ -332,14 +332,6 @@ def flush_events(self): Gdk.flush() Gdk.threads_leave() - def start_event_loop(self,timeout): - FigureCanvasBase.start_event_loop_default(self,timeout) - start_event_loop.__doc__=FigureCanvasBase.start_event_loop_default.__doc__ - - def stop_event_loop(self): - FigureCanvasBase.stop_event_loop_default(self) - stop_event_loop.__doc__=FigureCanvasBase.stop_event_loop_default.__doc__ - class FigureManagerGTK3(FigureManagerBase): """ diff --git a/lib/matplotlib/backends/backend_tkagg.py b/lib/matplotlib/backends/backend_tkagg.py index f6190d4f369..cb9971a00db 100644 --- a/lib/matplotlib/backends/backend_tkagg.py +++ b/lib/matplotlib/backends/backend_tkagg.py @@ -463,14 +463,6 @@ def new_timer(self, *args, **kwargs): def flush_events(self): self._master.update() - def start_event_loop(self,timeout): - FigureCanvasBase.start_event_loop_default(self,timeout) - start_event_loop.__doc__=FigureCanvasBase.start_event_loop_default.__doc__ - - def stop_event_loop(self): - FigureCanvasBase.stop_event_loop_default(self) - stop_event_loop.__doc__=FigureCanvasBase.stop_event_loop_default.__doc__ - class FigureManagerTkAgg(FigureManagerBase): """ diff --git a/lib/matplotlib/backends/backend_webagg.py b/lib/matplotlib/backends/backend_webagg.py index e39bf2cb2ba..a1468b39c30 100644 --- a/lib/matplotlib/backends/backend_webagg.py +++ b/lib/matplotlib/backends/backend_webagg.py @@ -59,17 +59,6 @@ def show(self): def new_timer(self, *args, **kwargs): return TimerTornado(*args, **kwargs) - def start_event_loop(self, timeout): - backend_bases.FigureCanvasBase.start_event_loop_default( - self, timeout) - start_event_loop.__doc__ = \ - backend_bases.FigureCanvasBase.start_event_loop_default.__doc__ - - def stop_event_loop(self): - backend_bases.FigureCanvasBase.stop_event_loop_default(self) - stop_event_loop.__doc__ = \ - backend_bases.FigureCanvasBase.stop_event_loop_default.__doc__ - class WebAggApplication(tornado.web.Application): initialized = False diff --git a/lib/matplotlib/backends/backend_webagg_core.py b/lib/matplotlib/backends/backend_webagg_core.py index d22d00704a9..7ef40187f6f 100644 --- a/lib/matplotlib/backends/backend_webagg_core.py +++ b/lib/matplotlib/backends/backend_webagg_core.py @@ -359,17 +359,6 @@ def handle_set_dpi_ratio(self, event): def send_event(self, event_type, **kwargs): self.manager._send_event(event_type, **kwargs) - def start_event_loop(self, timeout): - backend_bases.FigureCanvasBase.start_event_loop_default( - self, timeout) - start_event_loop.__doc__ = \ - backend_bases.FigureCanvasBase.start_event_loop_default.__doc__ - - def stop_event_loop(self): - backend_bases.FigureCanvasBase.stop_event_loop_default(self) - stop_event_loop.__doc__ = \ - backend_bases.FigureCanvasBase.stop_event_loop_default.__doc__ - _JQUERY_ICON_CLASSES = { 'home': 'ui-icon ui-icon-home', From 95dc08fd1604eb30090342598717f4be58a7284a Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Tue, 11 Jul 2017 21:33:27 -0700 Subject: [PATCH 2/3] Let pause always run the event loop. The default `start_event_loop` is not as efficient as it could be for non-interactive backends (it runs a busy loop) but if you really care you should just call time.sleep in that case. Meanwhile, this avoids the need for third-party interactive backends to register themselves into rcsetup.interactive_bk in order to be correctly pause()able. --- lib/matplotlib/pyplot.py | 42 ++++++++++++++++-------------------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index 5c78047db80..79ee0f2decc 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -75,8 +75,7 @@ def _backend_selection(): loop, and if not switches to a compatible one. """ backend = rcParams['backend'] - if not rcParams['backend_fallback'] or \ - backend not in _interactive_bk: + if not rcParams['backend_fallback'] or backend not in _interactive_bk: return is_agg_backend = rcParams['backend'].endswith('Agg') if 'wx' in sys.modules and not backend in ('WX', 'WXAgg'): @@ -275,33 +274,24 @@ def pause(interval): """ Pause for *interval* seconds. - If there is an active figure it will be updated and displayed, - and the GUI event loop will run during the pause. + If there is an active figure, it will be updated and displayed before the + pause, and the GUI event loop (if any) will run during the pause. - If there is no active figure, or if a non-interactive backend - is in use, this executes time.sleep(interval). - - This can be used for crude animation. For more complex - animation, see :mod:`matplotlib.animation`. - - This function is experimental; its behavior may be changed - or extended in a future release. + This can be used for crude animation. For more complex animation, see + :mod:`matplotlib.animation`. + This function is experimental; its behavior may be changed or extended in a + future release. """ - backend = rcParams['backend'] - if backend in _interactive_bk: - figManager = _pylab_helpers.Gcf.get_active() - if figManager is not None: - canvas = figManager.canvas - if canvas.figure.stale: - canvas.draw_idle() - show(block=False) - canvas.start_event_loop(interval) - return - - # No on-screen figure is active, so sleep() is all we need. - import time - time.sleep(interval) + manager = _pylab_helpers.Gcf.get_active() + if manager is not None: + canvas = manager.canvas + if canvas.figure.stale: + canvas.draw_idle() + show(block=False) + canvas.start_event_loop(interval) + else: + time.sleep(interval) @docstring.copy_dedent(matplotlib.rc) From e1484e2e4a99d181ee0d0a3a48937f30ece72b57 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 6 Aug 2017 16:26:33 -0400 Subject: [PATCH 3/3] DOC: Change text start/stop_event_loop docstrings --- lib/matplotlib/backend_bases.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 41239b5b5bc..bbc4e02b377 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -2428,21 +2428,24 @@ def new_timer(self, *args, **kwargs): def flush_events(self): """Flush the GUI events for the figure. - GUI backends likely need to reimplement this method. + Interactive backends need to reimplement this method. """ def start_event_loop(self, timeout=0): """Start a blocking event loop. - Such an event loop is used by interactive functions, such as ginput and - waitforbuttonpress, to wait for events. This should not be confused - with the main GUI event loop, which is independent and always running. + Such an event loop is used by interactive functions, such as `ginput` + and `waitforbuttonpress`, to wait for events. The event loop blocks until a callback function triggers - `stop_event_loop`, or *timeout* is reached. If *timeout* is negative, - never timeout. + `stop_event_loop`, or *timeout* is reached. - Only GUI backends need to reimplement this method. + If *timeout* is negative, never timeout. + + Only interactive backends need to reimplement this method and it relies + on `flush_events` being properly implemented. + + Interactive backends should implement this in a more native way. """ if timeout <= 0: timeout = np.inf @@ -2457,7 +2460,8 @@ def start_event_loop(self, timeout=0): def stop_event_loop(self): """Stop the current blocking event loop. - Only GUI backends need to reimplement this method. + Interactive backends need to reimplement this to match + `start_event_loop` """ self._looping = False