From 381ec5fbdb87f04bc342d6112cbaa8ee6be01b86 Mon Sep 17 00:00:00 2001 From: Sam Parkinson Date: Tue, 6 Oct 2015 10:05:12 +1100 Subject: [PATCH] Add a widget parameter to the Animator (tick based animations) Gtk 3.8 introduces the tick callback [1]. This allows widgets to be signaled before each frame, simmilar to requestAnimationFrame in the browser. This patch adds an optional widget argument to the Animator class so tick based animation can be used. This is much more efficent than using timeouts, as we get a more appropriate frame rate for the user. [1] https://developer.gnome.org/gtk3/stable/GtkWidget.html#gtk-widget-add-tick-callback --- src/sugar3/graphics/animator.py | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/sugar3/graphics/animator.py b/src/sugar3/graphics/animator.py index 91020fb6..37224890 100644 --- a/src/sugar3/graphics/animator.py +++ b/src/sugar3/graphics/animator.py @@ -24,9 +24,6 @@ Example: from gi.repository import Gtk from sugar3.graphics.animator import Animator, Animation - # Construct a 5 second animator - animator = Animator(5) - # Construct a window to animate w = Gtk.Window() w.connect('destroy', Gtk.main_quit) @@ -34,6 +31,9 @@ Example: w.connect('realize', lambda self: animator.start()) w.show() + # Construct a 5 second animator + animator = Animator(5, widget=w) + # Create an animation subclass to animate the widget class SizeAnimation(Animation): def __init__(self): @@ -80,6 +80,11 @@ class Animator(GObject.GObject): per second (frames per second) easing (int): the desired easing mode, either `EASE_OUT_EXPO` or `EASE_IN_EXPO` + widget (:class:`Gtk.Widget`): one of the widgets that the animation + is acting on. If supplied and if the user's Gtk+ version + supports it, the animation will run on the frame clock of the + widget, resulting in a smoother animation and the fps value + will be disregarded. .. note:: @@ -92,12 +97,13 @@ class Animator(GObject.GObject): 'completed': (GObject.SignalFlags.RUN_FIRST, None, ([])), } - def __init__(self, duration, fps=20, easing=EASE_OUT_EXPO): + def __init__(self, duration, fps=20, easing=EASE_OUT_EXPO, widget=None): GObject.GObject.__init__(self) self._animations = [] self._duration = duration self._interval = 1.0 / fps self._easing = easing + self._widget = widget self._timeout_sid = 0 self._start_time = None @@ -127,19 +133,30 @@ class Animator(GObject.GObject): self.stop() self._start_time = time.time() - self._timeout_sid = GLib.timeout_add( - int(self._interval * 1000), self._next_frame_cb) + if hasattr(self._widget, 'add_tick_callback'): + self._timeout_sid = self._widget.add_tick_callback( + self._next_frame_cb, None) + # Make sure the 1st frame is animated so we get ticks + self._next_frame_cb() + else: + self._timeout_sid = GLib.timeout_add( + int(self._interval * 1000), self._next_frame_cb) def stop(self): ''' Stop the animation and emit the `completed` signal ''' - if self._timeout_sid: + if self._timeout_sid and \ + not hasattr(self._widget, 'add_tick_callback'): GObject.source_remove(self._timeout_sid) self._timeout_sid = 0 self.emit('completed') + if self._timeout_sid and hasattr(self._widget, 'add_tick_callback'): + self._widget.remove_tick_callback(self._timeout_sid) + self._timeout_sid = 0 + self.emit('completed') - def _next_frame_cb(self): + def _next_frame_cb(self, *args): current_time = min(self._duration, time.time() - self._start_time) current_time = max(current_time, 0.0)