diff --git a/pyproject.toml b/pyproject.toml index 072d8a0..bbe6331 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "asyncgui-ext-clock" -version = "0.4.0" +version = "0.5.0.dev0" description = "An event scheduler for asyncgui programs" authors = ["Nattōsai Mitō "] license = "MIT" diff --git a/src/asyncgui_ext/clock.py b/src/asyncgui_ext/clock.py index 6e476b5..4d3e959 100644 --- a/src/asyncgui_ext/clock.py +++ b/src/asyncgui_ext/clock.py @@ -210,16 +210,19 @@ def callback(dt): async def anim_with_et(self, *, step=0) -> AsyncIterator[TimeUnit]: ''' - Same as :meth:`anim_with_dt` except this one generates the total elapsed time of the loop instead of the elapsed - time between frames. - .. code-block:: - timeout = ... async for et in clock.anim_with_et(): - ... - if et > timeout: - break + print(et) + + The code above is equivalent to the below. + + .. code-block:: + + et = 0 + async for dt in clock.anim_with_dt(): + et += dt + print(et) ''' et = 0 async with _repeat_sleeping(self, step) as sleep: @@ -243,36 +246,43 @@ async def anim_with_dt_et(self, *, step=0) -> AsyncIterator[tuple[TimeUnit, Time et += dt yield dt, et - async def anim_with_ratio(self, *, duration, step=0) -> AsyncIterator[float]: + async def anim_with_ratio(self, *, base, step=0) -> AsyncIterator[float]: ''' - Same as :meth:`anim_with_et` except this one generates the total progression ratio of the loop. - .. code-block:: - async for p in self.anim_with_ratio(duration=...): + async for p in clock.anim_with_ratio(base=100): print(p * 100, "%") + The code above is equivalent to the below. + + .. code-block:: + + base = 100 + async for et in clock.anim_with_et(): + print(et / base * 100, "%") + If you want to progress at a non-consistant rate, you may find the `source code `__ of the :class:`kivy.animation.AnimationTransition` helpful. .. code-block:: - async for p in clock.anim_with_ratio(duration=...): + async for p in clock.anim_with_ratio(base=...): p = p * p # quadratic print(p * 100, "%") + + .. versionchanged:: 0.5.0 + + The ``duration`` parameter was replaced with the ``base`` parameter. + The loop no longer stops when the progression reaches 1.0. ''' - if not duration: - await self.sleep(step) - yield 1.0 - return et = 0 async with _repeat_sleeping(self, step) as sleep: - while et < duration: + while True: et += await sleep() - yield et / duration + yield et / base - async def anim_with_dt_et_ratio(self, *, duration, step=0) -> AsyncIterator[tuple[TimeUnit, TimeUnit, float]]: + async def anim_with_dt_et_ratio(self, *, base, step=0) -> AsyncIterator[tuple[TimeUnit, TimeUnit, float]]: ''' :meth:`anim_with_dt`, :meth:`anim_with_et` and :meth:`anim_with_ratio` combined. @@ -280,17 +290,18 @@ async def anim_with_dt_et_ratio(self, *, duration, step=0) -> AsyncIterator[tupl async for dt, et, p in clock.anim_with_dt_et_ratio(...): ... + + .. versionchanged:: 0.5.0 + + The ``duration`` parameter was replaced with the ``base`` parameter. + The loop no longer stops when the progression reaches 1.0. ''' async with _repeat_sleeping(self, step) as sleep: - if not duration: - dt = await sleep() - yield dt, dt, 1.0 - return et = 0. - while et < duration: + while True: dt = await sleep() et += dt - yield dt, et, et / duration + yield dt, et, et / base def _linear(p): return p @@ -316,10 +327,13 @@ async def interpolate_scalar(self, start, end, *, duration, step=0, transition=_ ''' slope = end - start yield transition(0.) * slope + start - async for p in self.anim_with_ratio(step=step, duration=duration): - if p >= 1.0: - break - yield transition(p) * slope + start + if duration: + async for p in self.anim_with_ratio(step=step, base=duration): + if p >= 1.0: + break + yield transition(p) * slope + start + else: + await self.sleep(0) yield transition(1.) * slope + start async def interpolate_sequence(self, start, end, *, duration, step=0, transition=_linear, output_type=tuple) -> AsyncIterator: @@ -347,15 +361,20 @@ async def interpolate_sequence(self, start, end, *, duration, step=0, transition p = transition(0.) yield output_type(p * slope_elem + start_elem for slope_elem, start_elem in zip_(slope, start)) - async for p in self.anim_with_ratio(step=step, duration=duration): - if p >= 1.0: - break - p = transition(p) - yield output_type(p * slope_elem + start_elem for slope_elem, start_elem in zip_(slope, start)) + if duration: + async for p in self.anim_with_ratio(step=step, base=duration): + if p >= 1.0: + break + p = transition(p) + yield output_type(p * slope_elem + start_elem for slope_elem, start_elem in zip_(slope, start)) + else: + await self.sleep(0) p = transition(1.) yield output_type(p * slope_elem + start_elem for slope_elem, start_elem in zip_(slope, start)) + interpolate_seq = interpolate_sequence + async def run_in_thread(self, func, *, daemon=None, polling_interval) -> Awaitable: ''' Creates a new thread, runs a function within it, then waits for the completion of that function. diff --git a/tests/clock/test_anim_with_xxx.py b/tests/clock/test_anim_with_xxx.py index a595e9f..9f9142f 100644 --- a/tests/clock/test_anim_with_xxx.py +++ b/tests/clock/test_anim_with_xxx.py @@ -1,3 +1,6 @@ +import pytest + + def test_anim_with_dt(clock): from asyncgui import start dt_list = [] @@ -50,8 +53,10 @@ def test_anim_with_ratio(clock): p_list = [] async def async_fn(): - async for p in clock.anim_with_ratio(step=10, duration=100): + async for p in clock.anim_with_ratio(step=10, base=100): p_list.append(p) + if p >= 1: + break task = start(async_fn()) assert p_list == [] @@ -70,21 +75,22 @@ async def async_fn(): assert task.finished -def test_anim_with_ratio_zero_duration(clock): +def test_anim_with_ratio_zero_base(clock): from asyncgui import start p_list = [] async def async_fn(): - async for p in clock.anim_with_ratio(step=10, duration=0): + async for p in clock.anim_with_ratio(step=10, base=0): p_list.append(p) task = start(async_fn()) assert p_list == [] clock.tick(6) assert p_list == [] - clock.tick(6) - assert p_list == [1.0, ] - assert task.finished + with pytest.raises(ZeroDivisionError): + clock.tick(6) + assert p_list == [] + assert task.cancelled def test_anim_with_dt_et(clock): @@ -117,8 +123,10 @@ def test_anim_with_dt_et_ratio(clock): values = [] async def async_fn(): - async for v in clock.anim_with_dt_et_ratio(step=10, duration=100): + async for v in clock.anim_with_dt_et_ratio(step=10, base=100): values.extend(v) + if values[2] >= 1: + break task = start(async_fn()) assert values == [] @@ -146,18 +154,19 @@ async def async_fn(): assert task.finished -def test_anim_with_dt_et_ratio_zero_duration(clock): +def test_anim_with_dt_et_ratio_zero_base(clock): from asyncgui import start values = [] async def async_fn(): - async for v in clock.anim_with_dt_et_ratio(step=10, duration=0): + async for v in clock.anim_with_dt_et_ratio(step=10, base=0): values.extend(v) task = start(async_fn()) assert values == [] clock.tick(6) assert values == [] - clock.tick(6) - assert values == [12, 12, 1.0] - assert task.finished + with pytest.raises(ZeroDivisionError): + clock.tick(6) + assert values == [] + assert task.cancelled