115 lines
3.7 KiB
Python
115 lines
3.7 KiB
Python
|
__all__ = ('anim_attrs', 'anim_attrs_abbr', )
|
||
|
import typing as T
|
||
|
import types
|
||
|
from functools import partial
|
||
|
import kivy.clock
|
||
|
from kivy.animation import AnimationTransition
|
||
|
import asyncgui
|
||
|
|
||
|
|
||
|
def _update(setattr, zip, min, obj, duration, transition, output_seq_type, anim_params, task, p_time, dt):
|
||
|
time = p_time[0] + dt
|
||
|
p_time[0] = time
|
||
|
|
||
|
# calculate progression
|
||
|
progress = min(1., time / duration)
|
||
|
t = transition(progress)
|
||
|
|
||
|
# apply progression on obj
|
||
|
for attr_name, org_value, slope, is_seq in anim_params:
|
||
|
if is_seq:
|
||
|
new_value = output_seq_type(
|
||
|
slope_elem * t + org_elem
|
||
|
for org_elem, slope_elem in zip(org_value, slope)
|
||
|
)
|
||
|
setattr(obj, attr_name, new_value)
|
||
|
else:
|
||
|
setattr(obj, attr_name, slope * t + org_value)
|
||
|
|
||
|
# time to stop ?
|
||
|
if progress >= 1.:
|
||
|
task._step()
|
||
|
return False
|
||
|
|
||
|
|
||
|
_update = partial(_update, setattr, zip, min)
|
||
|
|
||
|
|
||
|
@types.coroutine
|
||
|
def _anim_attrs(
|
||
|
obj, duration, step, transition, output_seq_type, animated_properties,
|
||
|
getattr=getattr, isinstance=isinstance, tuple=tuple, str=str, partial=partial, native_seq_types=(tuple, list),
|
||
|
zip=zip, Clock=kivy.clock.Clock, AnimationTransition=AnimationTransition,
|
||
|
_update=_update, _current_task=asyncgui._current_task, _sleep_forever=asyncgui._sleep_forever, /):
|
||
|
if isinstance(transition, str):
|
||
|
transition = getattr(AnimationTransition, transition)
|
||
|
|
||
|
# get current values & calculate slopes
|
||
|
anim_params = tuple(
|
||
|
(
|
||
|
org_value := getattr(obj, attr_name),
|
||
|
is_seq := isinstance(org_value, native_seq_types),
|
||
|
(
|
||
|
org_value := tuple(org_value),
|
||
|
slope := tuple(goal_elem - org_elem for goal_elem, org_elem in zip(goal_value, org_value)),
|
||
|
) if is_seq else (slope := goal_value - org_value),
|
||
|
) and (attr_name, org_value, slope, is_seq, )
|
||
|
for attr_name, goal_value in animated_properties.items()
|
||
|
)
|
||
|
|
||
|
try:
|
||
|
clock_event = Clock.schedule_interval(
|
||
|
partial(_update, obj, duration, transition, output_seq_type, anim_params, (yield _current_task)[0][0],
|
||
|
[0., ]),
|
||
|
step,
|
||
|
)
|
||
|
yield _sleep_forever
|
||
|
finally:
|
||
|
clock_event.cancel()
|
||
|
|
||
|
|
||
|
def anim_attrs(obj, *, duration=1.0, step=0, transition=AnimationTransition.linear, output_seq_type=tuple,
|
||
|
**animated_properties) -> T.Awaitable:
|
||
|
'''
|
||
|
Animates attibutes of any object.
|
||
|
|
||
|
.. code-block::
|
||
|
|
||
|
import types
|
||
|
|
||
|
obj = types.SimpleNamespace(x=0, size=(200, 300))
|
||
|
await anim_attrs(obj, x=100, size=(400, 400))
|
||
|
|
||
|
The ``output_seq_type`` parameter:
|
||
|
|
||
|
.. code-block::
|
||
|
|
||
|
obj = types.SimpleNamespace(size=(200, 300))
|
||
|
await anim_attrs(obj, size=(400, 400), output_seq_type=list)
|
||
|
assert type(obj.size) is list
|
||
|
|
||
|
.. warning::
|
||
|
|
||
|
Unlike :class:`kivy.animation.Animation`, this one does not support dictionary-type and nested-sequence.
|
||
|
|
||
|
.. code-block::
|
||
|
|
||
|
await anim_attrs(obj, pos_hint={'x': 1.}) # not supported
|
||
|
await anim_attrs(obj, nested_sequence=[[10, 20, ]]) # not supported
|
||
|
|
||
|
await anim_attrs(obj, color=(1, 0, 0, 1), pos=(100, 200)) # OK
|
||
|
|
||
|
.. versionadded:: 0.6.1
|
||
|
'''
|
||
|
return _anim_attrs(obj, duration, step, transition, output_seq_type, animated_properties)
|
||
|
|
||
|
|
||
|
def anim_attrs_abbr(obj, *, d=1.0, s=0, t=AnimationTransition.linear, output_seq_type=tuple,
|
||
|
**animated_properties) -> T.Awaitable:
|
||
|
'''
|
||
|
:func:`anim_attrs` cannot animate attributes named ``step``, ``duration`` and ``transition`` but this one can.
|
||
|
|
||
|
.. versionadded:: 0.6.1
|
||
|
'''
|
||
|
return _anim_attrs(obj, d, s, t, output_seq_type, animated_properties)
|