diff --git a/examples/event_handling/timers.py b/examples/event_handling/timers.py index 2541f11aae3e..f5ca20e3d035 100644 --- a/examples/event_handling/timers.py +++ b/examples/event_handling/timers.py @@ -1,6 +1,7 @@ # Simple example of using general timer objects. This is used to update # the time placed in the title of the figure. import matplotlib.pyplot as plt +from matplotlib import events import numpy as np from datetime import datetime @@ -16,7 +17,7 @@ def update_title(axes): # Create a new timer object. Set the interval 500 milliseconds (1000 is default) # and tell the timer what function should be called. -timer = fig.canvas.new_timer(interval=100) +timer = events.Timer(interval=100) timer.add_callback(update_title, ax) timer.start() diff --git a/lib/matplotlib/animation.py b/lib/matplotlib/animation.py index de2d17a30eae..ac75802fde02 100644 --- a/lib/matplotlib/animation.py +++ b/lib/matplotlib/animation.py @@ -24,6 +24,8 @@ from matplotlib.compat import subprocess from matplotlib import verbose from matplotlib import rcParams +from matplotlib import events + # Other potential writing methods: # * http://pymedia.org/ @@ -845,7 +847,7 @@ def __init__(self, fig, interval=200, repeat_delay=None, repeat=True, # If we're not given an event source, create a new timer. This permits # sharing timers between animation objects for syncing animations. if event_source is None: - event_source = fig.canvas.new_timer() + event_source = events.Timer() event_source.interval = self._interval Animation.__init__(self, fig, event_source=event_source, diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 52752aa99825..f2c79178e3a8 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -1049,9 +1049,6 @@ def __init__(self, interval=None, callbacks=None): self._single = False - # Default attribute for holding the GUI-specific timer object - self._timer = None - def __del__(self): 'Need to stop timer and possibly disconnect timer.' self._timer_stop() @@ -2258,23 +2255,6 @@ def mpl_disconnect(self, cid): """ return self.callbacks.disconnect(cid) - def new_timer(self, *args, **kwargs): - """ - Creates a new backend-specific subclass of - :class:`backend_bases.Timer`. This is useful for getting periodic - events through the backend's native event loop. Implemented only for - backends with GUIs. - - optional arguments: - - *interval* - Timer interval in milliseconds - *callbacks* - Sequence of (func, args, kwargs) where func(*args, **kwargs) will - be executed by the timer every *interval*. - """ - return TimerBase(*args, **kwargs) - def flush_events(self): """ Flush the GUI events for the figure. Implemented only for diff --git a/lib/matplotlib/backends/backend_gtk.py b/lib/matplotlib/backends/backend_gtk.py index 3b30686b86a4..0412c3d18463 100644 --- a/lib/matplotlib/backends/backend_gtk.py +++ b/lib/matplotlib/backends/backend_gtk.py @@ -102,19 +102,13 @@ def new_figure_manager_given_figure(num, figure): return manager -class TimerGTK(TimerBase): - ''' - Subclass of :class:`backend_bases.TimerBase` that uses GTK for timer events. - - Attributes: - * interval: The time between timer events in milliseconds. Default - is 1000 ms. - * single_shot: Boolean flag indicating whether this timer should - operate as single shot (run once and then stop). Defaults to False. - * callbacks: Stores list of (func, args) tuples that will be called - upon timer events. This list can be manipulated directly, or the - functions add_callback and remove_callback can be used. - ''' +class Timer(TimerBase): + __doc__ = TimerBase.__doc__ + + def __init__(self, *args, **kwargs): + TimerBase.__init__(self, *args, **kwargs) + self._timer = None + def _timer_start(self): # Need to stop it, otherwise we potentially leak a timer id that will # never be stopped. @@ -487,22 +481,6 @@ def save_callback(buf, data=None): else: raise ValueError("filename must be a path or a file-like object") - def new_timer(self, *args, **kwargs): - """ - Creates a new backend-specific subclass of :class:`backend_bases.Timer`. - This is useful for getting periodic events through the backend's native - event loop. Implemented only for backends with GUIs. - - optional arguments: - - *interval* - Timer interval in milliseconds - *callbacks* - Sequence of (func, args, kwargs) where func(*args, **kwargs) will - be executed by the timer every *interval*. - """ - return TimerGTK(*args, **kwargs) - def flush_events(self): gtk.gdk.threads_enter() while gtk.events_pending(): diff --git a/lib/matplotlib/backends/backend_gtk3.py b/lib/matplotlib/backends/backend_gtk3.py index be12f4e68c38..6467473f8e3a 100644 --- a/lib/matplotlib/backends/backend_gtk3.py +++ b/lib/matplotlib/backends/backend_gtk3.py @@ -57,19 +57,13 @@ def mainloop(self): show = Show() -class TimerGTK3(TimerBase): - ''' - Subclass of :class:`backend_bases.TimerBase` that uses GTK3 for timer events. - - Attributes: - * interval: The time between timer events in milliseconds. Default - is 1000 ms. - * single_shot: Boolean flag indicating whether this timer should - operate as single shot (run once and then stop). Defaults to False. - * callbacks: Stores list of (func, args) tuples that will be called - upon timer events. This list can be manipulated directly, or the - functions add_callback and remove_callback can be used. - ''' +class Timer(TimerBase): + __doc__ = TimerBase.__doc__ + + def __init__(self, *args, **kwargs): + TimerBase.__init__(self, *args, **kwargs) + self._timer = None + def _timer_start(self): # Need to stop it, otherwise we potentially leak a timer id that will # never be stopped. @@ -310,22 +304,6 @@ def idle_draw(*args): if self._idle_draw_id == 0: self._idle_draw_id = GObject.idle_add(idle_draw) - def new_timer(self, *args, **kwargs): - """ - Creates a new backend-specific subclass of :class:`backend_bases.Timer`. - This is useful for getting periodic events through the backend's native - event loop. Implemented only for backends with GUIs. - - optional arguments: - - *interval* - Timer interval in milliseconds - *callbacks* - Sequence of (func, args, kwargs) where func(*args, **kwargs) will - be executed by the timer every *interval*. - """ - return TimerGTK3(*args, **kwargs) - def flush_events(self): Gdk.threads_enter() while Gtk.events_pending(): diff --git a/lib/matplotlib/backends/backend_gtk3agg.py b/lib/matplotlib/backends/backend_gtk3agg.py index ada6da4d5d50..18719119b3dc 100644 --- a/lib/matplotlib/backends/backend_gtk3agg.py +++ b/lib/matplotlib/backends/backend_gtk3agg.py @@ -104,3 +104,4 @@ def new_figure_manager_given_figure(num, figure): FigureManager = FigureManagerGTK3Agg show = backend_gtk3.show +Timer = backend_gtk3.Timer diff --git a/lib/matplotlib/backends/backend_gtkagg.py b/lib/matplotlib/backends/backend_gtkagg.py index cdd0bbae8723..dc96d9c883eb 100644 --- a/lib/matplotlib/backends/backend_gtkagg.py +++ b/lib/matplotlib/backends/backend_gtkagg.py @@ -10,7 +10,7 @@ from matplotlib.backends.backend_gtk import gtk, FigureManagerGTK, FigureCanvasGTK,\ show, draw_if_interactive,\ error_msg_gtk, NavigationToolbar, PIXELS_PER_INCH, backend_version, \ - NavigationToolbar2GTK + NavigationToolbar2GTK, Timer from matplotlib.backends._gtkagg import agg_to_gtk_drawable diff --git a/lib/matplotlib/backends/backend_macosx.py b/lib/matplotlib/backends/backend_macosx.py index ed17506e4a79..b313d473bef7 100644 --- a/lib/matplotlib/backends/backend_macosx.py +++ b/lib/matplotlib/backends/backend_macosx.py @@ -245,20 +245,8 @@ def new_figure_manager_given_figure(num, figure): return manager -class TimerMac(_macosx.Timer, TimerBase): - ''' - Subclass of :class:`backend_bases.TimerBase` that uses CoreFoundation - run loops for timer events. - - Attributes: - * interval: The time between timer events in milliseconds. Default - is 1000 ms. - * single_shot: Boolean flag indicating whether this timer should - operate as single shot (run once and then stop). Defaults to False. - * callbacks: Stores list of (func, args) tuples that will be called - upon timer events. This list can be manipulated directly, or the - functions add_callback and remove_callback can be used. - ''' +class Timer(_macosx.Timer, TimerBase): + __doc__ = TimerBase.__doc__ # completely implemented at the C-level (in _macosx.Timer) @@ -329,22 +317,6 @@ def print_tiff(self, filename, *args, **kwargs): def print_gif(self, filename, *args, **kwargs): self._print_bitmap(filename, *args, **kwargs) - def new_timer(self, *args, **kwargs): - """ - Creates a new backend-specific subclass of :class:`backend_bases.Timer`. - This is useful for getting periodic events through the backend's native - event loop. Implemented only for backends with GUIs. - - optional arguments: - - *interval* - Timer interval in milliseconds - *callbacks* - Sequence of (func, args, kwargs) where func(*args, **kwargs) will - be executed by the timer every *interval*. - """ - return TimerMac(*args, **kwargs) - class FigureManagerMac(_macosx.FigureManager, FigureManagerBase): """ diff --git a/lib/matplotlib/backends/backend_qt4.py b/lib/matplotlib/backends/backend_qt4.py index 3caad0bf90c1..afffa4efbd15 100644 --- a/lib/matplotlib/backends/backend_qt4.py +++ b/lib/matplotlib/backends/backend_qt4.py @@ -85,50 +85,38 @@ def new_figure_manager_given_figure(num, figure): return manager -class TimerQT(TimerBase): - ''' - Subclass of :class:`backend_bases.TimerBase` that uses Qt4 timer events. - - Attributes: - * interval: The time between timer events in milliseconds. Default - is 1000 ms. - * single_shot: Boolean flag indicating whether this timer should - operate as single shot (run once and then stop). Defaults to False. - * callbacks: Stores list of (func, args) tuples that will be called - upon timer events. This list can be manipulated directly, or the - functions add_callback and remove_callback can be used. - ''' +class Timer(TimerBase, QtCore.QTimer): + __doc__ = TimerBase.__doc__ + def __init__(self, *args, **kwargs): TimerBase.__init__(self, *args, **kwargs) # Create a new timer and connect the timeout() signal to the # _on_timer method. - self._timer = QtCore.QTimer() - QtCore.QObject.connect(self._timer, QtCore.SIGNAL('timeout()'), - self._on_timer) + QtCore.QTimer.__init__(self) + self.timeout.connect(self._on_timer) self._timer_set_interval() def __del__(self): # Probably not necessary in practice, but is good behavior to disconnect try: TimerBase.__del__(self) - QtCore.QObject.disconnect(self._timer, - QtCore.SIGNAL('timeout()'), self._on_timer) + self.timeout.disconnect(self._on_timer) except RuntimeError: # Timer C++ object already deleted pass def _timer_set_single_shot(self): - self._timer.setSingleShot(self._single) + self.setSingleShot(self._single) def _timer_set_interval(self): - self._timer.setInterval(self._interval) + self.setInterval(self._interval) def _timer_start(self): - self._timer.start() + QtCore.QTimer.start(self) def _timer_stop(self): - self._timer.stop() + QtCore.QTimer.stop(self) class FigureCanvasQT( QtGui.QWidget, FigureCanvasBase ): @@ -331,22 +319,6 @@ def _get_key( self, event ): return key - def new_timer(self, *args, **kwargs): - """ - Creates a new backend-specific subclass of :class:`backend_bases.Timer`. - This is useful for getting periodic events through the backend's native - event loop. Implemented only for backends with GUIs. - - optional arguments: - - *interval* - Timer interval in milliseconds - *callbacks* - Sequence of (func, args, kwargs) where func(*args, **kwargs) will - be executed by the timer every *interval*. - """ - return TimerQT(*args, **kwargs) - def flush_events(self): QtGui.qApp.processEvents() diff --git a/lib/matplotlib/backends/backend_qt4agg.py b/lib/matplotlib/backends/backend_qt4agg.py index 659f4a8ddae1..d88514a5d10d 100644 --- a/lib/matplotlib/backends/backend_qt4agg.py +++ b/lib/matplotlib/backends/backend_qt4agg.py @@ -12,7 +12,7 @@ from backend_agg import FigureCanvasAgg from backend_qt4 import QtCore, QtGui, FigureManagerQT, FigureCanvasQT,\ show, draw_if_interactive, backend_version, \ - NavigationToolbar2QT + NavigationToolbar2QT, Timer DEBUG = False diff --git a/lib/matplotlib/backends/backend_tkagg.py b/lib/matplotlib/backends/backend_tkagg.py index 5de248ab7657..39e9d8116b98 100644 --- a/lib/matplotlib/backends/backend_tkagg.py +++ b/lib/matplotlib/backends/backend_tkagg.py @@ -110,31 +110,21 @@ def new_figure_manager_given_figure(num, figure): return figManager -class TimerTk(TimerBase): - ''' - Subclass of :class:`backend_bases.TimerBase` that uses Tk's timer events. - - Attributes: - * interval: The time between timer events in milliseconds. Default - is 1000 ms. - * single_shot: Boolean flag indicating whether this timer should - operate as single shot (run once and then stop). Defaults to False. - * callbacks: Stores list of (func, args) tuples that will be called - upon timer events. This list can be manipulated directly, or the - functions add_callback and remove_callback can be used. - ''' - def __init__(self, parent, *args, **kwargs): +class Timer(TimerBase): + __doc__ = TimerBase.__doc__ + + def __init__(self, *args, **kwargs): TimerBase.__init__(self, *args, **kwargs) - self.parent = parent self._timer = None + self._tcl = Tk.Tcl() def _timer_start(self): self._timer_stop() - self._timer = self.parent.after(self._interval, self._on_timer) + self._timer = self._tcl.after(self._interval, self._on_timer) def _timer_stop(self): if self._timer is not None: - self.parent.after_cancel(self._timer) + self._tcl.after_cancel(self._timer) self._timer = None def _on_timer(self): @@ -143,7 +133,7 @@ def _on_timer(self): # Tk after() is only a single shot, so we need to add code here to # reset the timer if we're not operating in single shot mode. if not self._single and len(self.callbacks) > 0: - self._timer = self.parent.after(self._interval, self._on_timer) + self._timer = self._tcl.after(self._interval, self._on_timer) else: self._timer = None @@ -479,22 +469,6 @@ def key_release(self, event): key = self._get_key(event) FigureCanvasBase.key_release_event(self, key, guiEvent=event) - def new_timer(self, *args, **kwargs): - """ - Creates a new backend-specific subclass of :class:`backend_bases.Timer`. - This is useful for getting periodic events through the backend's native - event loop. Implemented only for backends with GUIs. - - optional arguments: - - *interval* - Timer interval in milliseconds - *callbacks* - Sequence of (func, args, kwargs) where func(*args, **kwargs) will - be executed by the timer every *interval*. - """ - return TimerTk(self._tkcanvas, *args, **kwargs) - def flush_events(self): self._master.update() diff --git a/lib/matplotlib/backends/backend_webagg.py b/lib/matplotlib/backends/backend_webagg.py index 19702c0bc2da..baa3a80632de 100644 --- a/lib/matplotlib/backends/backend_webagg.py +++ b/lib/matplotlib/backends/backend_webagg.py @@ -76,7 +76,9 @@ def new_figure_manager_given_figure(num, figure): return manager -class TimerTornado(backend_bases.TimerBase): +class Timer(backend_bases.TimerBase): + __doc__ = backend_bases.TimerBase.__doc__ + def _timer_start(self): self._timer_stop() if self._single: @@ -257,9 +259,6 @@ def handle_event(self, event): def send_event(self, event_type, **kwargs): self.manager.send_event(event_type, **kwargs) - 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) diff --git a/lib/matplotlib/backends/backend_wx.py b/lib/matplotlib/backends/backend_wx.py index f9cd226f1001..c597bf1e0146 100644 --- a/lib/matplotlib/backends/backend_wx.py +++ b/lib/matplotlib/backends/backend_wx.py @@ -165,38 +165,18 @@ def raise_msg_to_str(msg): return msg -class TimerWx(TimerBase): - ''' - Subclass of :class:`backend_bases.TimerBase` that uses WxTimer events. - - Attributes: - * interval: The time between timer events in milliseconds. Default - is 1000 ms. - * single_shot: Boolean flag indicating whether this timer should - operate as single shot (run once and then stop). Defaults to False. - * callbacks: Stores list of (func, args) tuples that will be called - upon timer events. This list can be manipulated directly, or the - functions add_callback and remove_callback can be used. - ''' - def __init__(self, parent, *args, **kwargs): - TimerBase.__init__(self, *args, **kwargs) - - # Create a new timer and connect the timer event to our handler. - # For WX, the events have to use a widget for binding. - self.parent = parent - self._timer = wx.Timer(self.parent, wx.NewId()) - self.parent.Bind(wx.EVT_TIMER, self._on_timer, self._timer) +class Timer(wx.Timer, TimerBase): + __doc__ = TimerBase.__doc__ - # Unbinding causes Wx to stop for some reason. Disabling for now. -# def __del__(self): -# TimerBase.__del__(self) -# self.parent.Bind(wx.EVT_TIMER, None, self._timer) + def __init__(self, *args, **kwargs): + TimerBase.__init__(self, *args, **kwargs) + wx.Timer.__init__(self) def _timer_start(self): - self._timer.Start(self._interval, self._single) + self.Start(self._interval, self._single) def _timer_stop(self): - self._timer.Stop() + self.Stop() def _timer_set_interval(self): self._timer_start() @@ -204,8 +184,8 @@ def _timer_set_interval(self): def _timer_set_single_shot(self): self._timer.start() - def _on_timer(self, *args): - TimerBase._on_timer(self) + def Notify(self): + self._on_timer() class RendererWx(RendererBase): @@ -1006,22 +986,6 @@ def draw(self, drawDC=None): self._isDrawn = True self.gui_repaint(drawDC=drawDC) - def new_timer(self, *args, **kwargs): - """ - Creates a new backend-specific subclass of :class:`backend_bases.Timer`. - This is useful for getting periodic events through the backend's native - event loop. Implemented only for backends with GUIs. - - optional arguments: - - *interval* - Timer interval in milliseconds - *callbacks* - Sequence of (func, args, kwargs) where func(*args, **kwargs) will - be executed by the timer every *interval*. - """ - return TimerWx(self, *args, **kwargs) - def flush_events(self): wx.Yield() diff --git a/lib/matplotlib/backends/backend_wxagg.py b/lib/matplotlib/backends/backend_wxagg.py index 5b301a045f04..b1edf14b781e 100644 --- a/lib/matplotlib/backends/backend_wxagg.py +++ b/lib/matplotlib/backends/backend_wxagg.py @@ -23,7 +23,7 @@ import backend_wx # already uses wxversion.ensureMinimal('2.8') from backend_wx import FigureManager, FigureManagerWx, FigureCanvasWx, \ FigureFrameWx, DEBUG_MSG, NavigationToolbar2Wx, error_msg_wx, \ - draw_if_interactive, show, Toolbar, backend_version + draw_if_interactive, show, Toolbar, Timer, backend_version import wx class FigureFrameWxAgg(FigureFrameWx): diff --git a/lib/matplotlib/events.py b/lib/matplotlib/events.py new file mode 100644 index 000000000000..2da8452708b8 --- /dev/null +++ b/lib/matplotlib/events.py @@ -0,0 +1,20 @@ +import matplotlib + +def _initialize(): + backend = matplotlib.get_backend() + # Import the requested backend into a generic module object + if backend.startswith('module://'): + backend_name = backend[9:] + else: + backend_name = 'backend_'+backend + backend_name = backend_name.lower() # until we banish mixed case + backend_name = 'matplotlib.backends.%s'%backend_name.lower() + backend_module = __import__(backend_name, globals(), locals(), [backend_name]) + return backend_module + +backend = _initialize() +try: + Timer = backend.Timer +except AttributeError: + from matplotlib.backend_bases import TimerBase as Timer +del backend, _initialize