test-kivy-app/kivy_venv/lib/python3.11/site-packages/asynckivy/_animation.py
2024-09-15 20:57:02 +03:00

116 lines
3.5 KiB
Python

__all__ = ('animate', )
import typing as T
import types
from functools import partial
from kivy.clock import Clock
from kivy.animation import AnimationTransition
from asyncgui import _sleep_forever, _current_task
@types.coroutine
def animate(obj, *, duration=1.0, step=0, transition=AnimationTransition.linear, **animated_properties) -> T.Awaitable:
'''
Animates attibutes of any object. This is basically an async form of :class:`kivy.animation.Animation`.
.. code-block::
import types
obj = types.SimpleNamespace(x=0, size=(200, 300, ))
await animate(obj, x=100, size=(400, 400))
Kivy has two compound animations, :class:`kivy.animation.Sequence` and :class:`kivy.animation.Parallel`.
You can achieve the same functionality as them in asynckivy as follows:
.. code-block::
import asynckivy as ak
async def sequential_animation(widget):
await ak.animate(widget, x=100)
await ak.animate(widget, x=0)
async def parallel_animation(widget):
await ak.wait_all(
ak.animate(widget, x=100),
ak.animate(widget, y=100, duration=2),
)
.. deprecated:: 0.6.1
This will be removed before version 1.0.0.
Use :func:`asynckivy.anim_attrs` or :func:`asynckivy.anim_attrs_abbr` instead.
'''
if not duration:
for key, value in animated_properties.items():
setattr(obj, key, value)
return
if isinstance(transition, str):
transition = getattr(AnimationTransition, transition)
# get current values
properties = {}
for key, value in animated_properties.items():
original_value = getattr(obj, key)
if isinstance(original_value, (tuple, list)):
original_value = original_value[:]
elif isinstance(original_value, dict):
original_value = original_value.copy()
properties[key] = (original_value, value)
try:
clock_event = Clock.schedule_interval(
partial(_update, obj, duration, transition, properties, (yield _current_task)[0][0], [0., ]),
step,
)
yield _sleep_forever
finally:
clock_event.cancel()
def _calculate(isinstance, list, tuple, dict, range, len, a, b, t):
'''The logic of this function is identical to 'kivy.animation.Animation._calculate()'
'''
if isinstance(a, list) or isinstance(a, tuple):
if isinstance(a, list):
tp = list
else:
tp = tuple
return tp([_calculate(a[x], b[x], t) for x in range(len(a))])
elif isinstance(a, dict):
d = {}
for x in a:
if x not in b:
# User requested to animate only part of the dict.
# Copy the rest
d[x] = a[x]
else:
d[x] = _calculate(a[x], b[x], t)
return d
else:
return (a * (1. - t)) + (b * t)
def _update(setattr, _calculate, obj, duration, transition, properties, 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 key, values in properties.items():
a, b = values
value = _calculate(a, b, t)
setattr(obj, key, value)
# time to stop ?
if progress >= 1.:
task._step()
return False
_calculate = partial(_calculate, isinstance, list, tuple, dict, range, len)
_update = partial(_update, setattr, _calculate)