Skip to content

Commit 518710f

Browse files
Add Clock.sleep_freq
1 parent 2806554 commit 518710f

File tree

1 file changed

+53
-29
lines changed

1 file changed

+53
-29
lines changed

src/asyncgui_ext/clock.py

Lines changed: 53 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
from collections.abc import Callable, Awaitable, AsyncIterator
88
from functools import partial
99
from dataclasses import dataclass
10-
from contextlib import AbstractAsyncContextManager
10+
from contextlib import AbstractAsyncContextManager, asynccontextmanager
1111

12-
from asyncgui import Task, move_on_when, _sleep_forever, _current_task
12+
from asyncgui import Task, move_on_when, _sleep_forever, _current_task, ExclusiveEvent, _wait_args_0, current_task
1313

1414
TimeUnit = TypeVar("TimeUnit")
1515
ClockCallback: TypeAlias = Callable[[TimeUnit], None]
@@ -165,6 +165,52 @@ def callback(dt):
165165
finally:
166166
event.cancel()
167167

168+
@asynccontextmanager
169+
async def sleep_freq(self, duration, *, free_to_await=False) -> AsyncIterator[Callable[[], Awaitable[TimeUnit]]]:
170+
'''
171+
An async form of :meth:`schedule_interval`. The following callback-style code:
172+
173+
.. code-block::
174+
175+
def callback(dt):
176+
print(dt)
177+
if some_condition:
178+
event.cancel()
179+
180+
event = clock.schedule_interval(callback, 10)
181+
182+
is equivalent to the following async-style code:
183+
184+
.. code-block::
185+
186+
async with clock.sleep_freq(10) as sleep:
187+
while True:
188+
dt = await sleep()
189+
print(dt)
190+
if some_condition:
191+
break
192+
193+
.. versionadded:: 0.6.1
194+
195+
The ``free_to_await`` parameter:
196+
197+
If set to False (the default), the only permitted async operation within the with-block is ``await xxx()``,
198+
where ``xxx`` is the identifier specified in the as-clause. To lift this restriction, set ``free_to_await`` to
199+
True — at the cost of slightly reduced performance.
200+
'''
201+
clock_event = self.schedule_interval(None, duration)
202+
try:
203+
if free_to_await:
204+
e = ExclusiveEvent()
205+
clock_event.callback = e.fire
206+
yield e.wait_args_0
207+
else:
208+
task = await current_task()
209+
clock_event.callback = task._step
210+
yield _wait_args_0
211+
finally:
212+
clock_event.cancel()
213+
168214
async def anim_with_dt(self, *, step=0) -> AsyncIterator[TimeUnit]:
169215
'''
170216
An async form of :meth:`schedule_interval`.
@@ -202,7 +248,7 @@ def callback(dt):
202248
203249
This is also true of other ``anim_with_xxx`` APIs.
204250
'''
205-
async with _repeat_sleeping(self, step) as sleep:
251+
async with self.sleep_freq(step) as sleep:
206252
while True:
207253
yield await sleep()
208254

@@ -223,7 +269,7 @@ async def anim_with_et(self, *, step=0) -> AsyncIterator[TimeUnit]:
223269
print(et)
224270
'''
225271
et = 0
226-
async with _repeat_sleeping(self, step) as sleep:
272+
async with self.sleep_freq(step) as sleep:
227273
while True:
228274
et += await sleep()
229275
yield et
@@ -238,7 +284,7 @@ async def anim_with_dt_et(self, *, step=0) -> AsyncIterator[tuple[TimeUnit, Time
238284
...
239285
'''
240286
et = 0
241-
async with _repeat_sleeping(self, step) as sleep:
287+
async with self.sleep_freq(step) as sleep:
242288
while True:
243289
dt = await sleep()
244290
et += dt
@@ -275,7 +321,7 @@ async def anim_with_ratio(self, *, base, step=0) -> AsyncIterator[float]:
275321
The loop no longer stops when the progression reaches 1.0.
276322
'''
277323
et = 0
278-
async with _repeat_sleeping(self, step) as sleep:
324+
async with self.sleep_freq(step) as sleep:
279325
while True:
280326
et += await sleep()
281327
yield et / base
@@ -294,7 +340,7 @@ async def anim_with_dt_et_ratio(self, *, base, step=0) -> AsyncIterator[tuple[Ti
294340
The ``duration`` parameter was replaced with the ``base`` parameter.
295341
The loop no longer stops when the progression reaches 1.0.
296342
'''
297-
async with _repeat_sleeping(self, step) as sleep:
343+
async with self.sleep_freq(step) as sleep:
298344
et = 0.
299345
while True:
300346
dt = await sleep()
@@ -466,25 +512,3 @@ def anim_attrs_abbr(self, obj, *, d, s=0, t=_linear, **animated_properties) -> A
466512
:meth:`anim_attrs` cannot animate attributes named ``step``, ``duration`` and ``transition`` but this one can.
467513
'''
468514
return self._anim_attrs(obj, d, s, t, animated_properties)
469-
470-
471-
class _repeat_sleeping:
472-
__slots__ = ('_timer', '_interval', '_event', )
473-
474-
def __init__(self, clock: Clock, interval):
475-
self._timer = clock
476-
self._interval = interval
477-
478-
@staticmethod
479-
@types.coroutine
480-
def _sleep(_f=_sleep_forever):
481-
return (yield _f)[0][0]
482-
483-
@types.coroutine
484-
def __aenter__(self, _current_task=_current_task) -> Awaitable[Callable[[], Awaitable[TimeUnit]]]:
485-
task = (yield _current_task)[0][0]
486-
self._event = self._timer.schedule_interval(task._step, self._interval)
487-
return self._sleep
488-
489-
async def __aexit__(self, exc_type, exc_val, exc_tb):
490-
self._event.cancel()

0 commit comments

Comments
 (0)