7
7
from collections .abc import Callable , Awaitable , AsyncIterator
8
8
from functools import partial
9
9
from dataclasses import dataclass
10
- from contextlib import AbstractAsyncContextManager
10
+ from contextlib import AbstractAsyncContextManager , asynccontextmanager
11
11
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
13
13
14
14
TimeUnit = TypeVar ("TimeUnit" )
15
15
ClockCallback : TypeAlias = Callable [[TimeUnit ], None ]
@@ -165,6 +165,52 @@ def callback(dt):
165
165
finally :
166
166
event .cancel ()
167
167
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
+
168
214
async def anim_with_dt (self , * , step = 0 ) -> AsyncIterator [TimeUnit ]:
169
215
'''
170
216
An async form of :meth:`schedule_interval`.
@@ -202,7 +248,7 @@ def callback(dt):
202
248
203
249
This is also true of other ``anim_with_xxx`` APIs.
204
250
'''
205
- async with _repeat_sleeping ( self , step ) as sleep :
251
+ async with self . sleep_freq ( step ) as sleep :
206
252
while True :
207
253
yield await sleep ()
208
254
@@ -223,7 +269,7 @@ async def anim_with_et(self, *, step=0) -> AsyncIterator[TimeUnit]:
223
269
print(et)
224
270
'''
225
271
et = 0
226
- async with _repeat_sleeping ( self , step ) as sleep :
272
+ async with self . sleep_freq ( step ) as sleep :
227
273
while True :
228
274
et += await sleep ()
229
275
yield et
@@ -238,7 +284,7 @@ async def anim_with_dt_et(self, *, step=0) -> AsyncIterator[tuple[TimeUnit, Time
238
284
...
239
285
'''
240
286
et = 0
241
- async with _repeat_sleeping ( self , step ) as sleep :
287
+ async with self . sleep_freq ( step ) as sleep :
242
288
while True :
243
289
dt = await sleep ()
244
290
et += dt
@@ -275,7 +321,7 @@ async def anim_with_ratio(self, *, base, step=0) -> AsyncIterator[float]:
275
321
The loop no longer stops when the progression reaches 1.0.
276
322
'''
277
323
et = 0
278
- async with _repeat_sleeping ( self , step ) as sleep :
324
+ async with self . sleep_freq ( step ) as sleep :
279
325
while True :
280
326
et += await sleep ()
281
327
yield et / base
@@ -294,7 +340,7 @@ async def anim_with_dt_et_ratio(self, *, base, step=0) -> AsyncIterator[tuple[Ti
294
340
The ``duration`` parameter was replaced with the ``base`` parameter.
295
341
The loop no longer stops when the progression reaches 1.0.
296
342
'''
297
- async with _repeat_sleeping ( self , step ) as sleep :
343
+ async with self . sleep_freq ( step ) as sleep :
298
344
et = 0.
299
345
while True :
300
346
dt = await sleep ()
@@ -466,25 +512,3 @@ def anim_attrs_abbr(self, obj, *, d, s=0, t=_linear, **animated_properties) -> A
466
512
:meth:`anim_attrs` cannot animate attributes named ``step``, ``duration`` and ``transition`` but this one can.
467
513
'''
468
514
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