from gillcup.action import EffectAction
class AnimatedObject(object):
[docs] """An objects whose attributes can be animated
Animated attribute must be instance (not class) attribute in order to
work.
Every animated attribute must be assigned a normal value before it can be
animated. Usually this is done in the constructor.
Each AnimatedObject needs a timer to animate. This can be either
through an argument to the animate method, or in an instance attribute.
The constructor takes a timer value to set the instance attribute to.
"""
def __init__(self, timer=None):
super(AnimatedObject, self).__init__()
self._anim_data_ = {}
self.timer = timer
def _animate(self, attribute, animation):
"""Animate the given attribute by the given animation
animation can be any object with a getValue method; but an Effect
works best.
"""
try:
oldValue = self.__dict__[attribute]
del self.__dict__[attribute]
except KeyError:
old = self._anim_data_[attribute]
else:
old = _Constant(oldValue)
self._anim_data_[attribute] = animation
return old
def __getattr__(self, attr):
try:
effect = self._anim_data_[attr]
except KeyError:
raise AttributeError(attr)
except RuntimeError:
# Probably _anim_data_ doesn't exist => infinite recursion
raise RuntimeError(
'Runtime error while getting attribute %s. '
'Likely the base __init__ method was not called'
% attr
)
return effect.getValue()
def _replace_effect(self, attribute, oldEffect, newEffect):
""" Replace the given effct by a new effect
"""
try:
currentEffect = self._anim_data_[attribute]
except KeyError:
return False
else:
if currentEffect is oldEffect:
if getattr(newEffect, '_is_constant', False):
del self._anim_data_[attribute]
setattr(self, attribute, newEffect.getValue())
else:
self._anim_data_[attribute] = newEffect
return True
else:
try:
replaceMethod = currentEffect._replace_effect_
except AttributeError:
return False
else:
return replaceMethod(oldEffect, newEffect)
def _dump_effects(self, indentLevel=0):
for attr, effect in self._anim_data_.items():
print ' ' * indentLevel + '.' + attr + ':' + str(getattr(self, attr))
try:
dump = effect.dump
except AttributeError:
print ' ' * (indentLevel + 1) + str(effect)
else:
effect.dump(indentLevel + 1)
def animate(self, attribute, value, dt=0, timer=None, **options):
[docs] """Animate the given attribute
Calls self.apply(self.animation(...), dt=dt).
Returns the resulting Action.
"""
anim = self.animation(attribute, value, **options)
return self.apply(anim, dt=dt, timer=timer)
def animation(self, attribute, value, **options):
[docs] """Return an animation Action for the given attribute.
When this Action is run, the given attribute will be gradually set
to the new value. The style of the animation is given by options.
See :py:func:`gillcup.effect.animation` for what options are available.
"""
from gillcup.effect import animation
return animation(self, attribute, value, **options)
def apply(self, action, dt=0, timer=None):
[docs] """Schedule action on this object's timer
dt is the time in which the Action is to be executed (measured from the
timer's current time).
timer can be given to specify the timer to use; if None, self's timer
will be used
"""
timer = timer or self.timer
return timer.schedule(dt, action)
def dynamicAttributeSetter(self, attribute, getter):
[docs] """Returns an Action to set an attribute getter
After the returned Action runs, the given getter function will be used
to provide values for the given attribute.
"""
return EffectAction(_GetterObject(getter), self, attribute)
def setDynamicAttribute(self, attribute, getter, dt=0, timer=None):
[docs] """Set a getter for an attribute
If dt==0, sets getter as the attribute getter for the given attribute.
Otherwise, calls
self.apply(self.dynamicAttributeSetter(attribute, getter), dt=dt).
"""
from gillcup import effect
if not dt:
self._animate(attribute, effect.GetterObject(getter))
else:
setter = self.dynamicAttributeSetter(attribute, getter)
self.apply(setter, dt, timer)
class _Constant(object):
_is_constant = True
def __init__(self, value):
self.value = value
def getValue(self):
return self.value
def dump(self, indentationLevel):
o = str(self.value)
print ' ' * indentationLevel + type(self).__name__ + ': ' + o