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
master
Sam Parkinson 9 years ago
parent 1bd8349b84
commit 381ec5fbdb

@ -24,9 +24,6 @@ Example:
from gi.repository import Gtk from gi.repository import Gtk
from sugar3.graphics.animator import Animator, Animation from sugar3.graphics.animator import Animator, Animation
# Construct a 5 second animator
animator = Animator(5)
# Construct a window to animate # Construct a window to animate
w = Gtk.Window() w = Gtk.Window()
w.connect('destroy', Gtk.main_quit) w.connect('destroy', Gtk.main_quit)
@ -34,6 +31,9 @@ Example:
w.connect('realize', lambda self: animator.start()) w.connect('realize', lambda self: animator.start())
w.show() w.show()
# Construct a 5 second animator
animator = Animator(5, widget=w)
# Create an animation subclass to animate the widget # Create an animation subclass to animate the widget
class SizeAnimation(Animation): class SizeAnimation(Animation):
def __init__(self): def __init__(self):
@ -80,6 +80,11 @@ class Animator(GObject.GObject):
per second (frames per second) per second (frames per second)
easing (int): the desired easing mode, either `EASE_OUT_EXPO` easing (int): the desired easing mode, either `EASE_OUT_EXPO`
or `EASE_IN_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:: .. note::
@ -92,12 +97,13 @@ class Animator(GObject.GObject):
'completed': (GObject.SignalFlags.RUN_FIRST, None, ([])), '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) GObject.GObject.__init__(self)
self._animations = [] self._animations = []
self._duration = duration self._duration = duration
self._interval = 1.0 / fps self._interval = 1.0 / fps
self._easing = easing self._easing = easing
self._widget = widget
self._timeout_sid = 0 self._timeout_sid = 0
self._start_time = None self._start_time = None
@ -127,19 +133,30 @@ class Animator(GObject.GObject):
self.stop() self.stop()
self._start_time = time.time() self._start_time = time.time()
self._timeout_sid = GLib.timeout_add( if hasattr(self._widget, 'add_tick_callback'):
int(self._interval * 1000), self._next_frame_cb) 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): def stop(self):
''' '''
Stop the animation and emit the `completed` signal 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) GObject.source_remove(self._timeout_sid)
self._timeout_sid = 0 self._timeout_sid = 0
self.emit('completed') 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 = min(self._duration, time.time() - self._start_time)
current_time = max(current_time, 0.0) current_time = max(current_time, 0.0)

Loading…
Cancel
Save