Skip to content

Commit 0650135

Browse files
(api-break) remove Clock.schedule_once()
1 parent c4225ed commit 0650135

File tree

4 files changed

+34
-163
lines changed

4 files changed

+34
-163
lines changed

README.md

Lines changed: 11 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,6 @@
11
# Clock
22

3-
*An event scheduler designed for asyncgui programs.*
4-
5-
First, take a look at the callback-style code below that has nothing to do with `asyncgui`.
6-
If you've ever used `Kivy` or `Pyglet`, you may find it familiar.
7-
8-
```python
9-
from asyncgui_ext.clock import Clock
10-
11-
clock = Clock()
12-
13-
# Schedules a function to be called after a delay of 20 time units.
14-
clock.schedule_once(lambda dt: print("Hello"), 20)
15-
16-
# Advances the clock by 10 time units.
17-
clock.tick(10)
18-
19-
# The clock advanced by a total of 20 time units.
20-
# The callback function will be called.
21-
clock.tick(10) # => Hello
22-
```
23-
24-
Next one is async/await-style code that involves `asyncgui`, and does the same thing as the previous.
3+
*Event scheduler designed for asyncgui programs.*
254

265
```python
276
import asyncgui
@@ -30,37 +9,37 @@ from asyncgui_ext.clock import Clock
309
clock = Clock()
3110

3211
async def async_fn():
33-
await clock.sleep(20)
12+
await clock.sleep(20) # Waits for 20 time units
3413
print("Hello")
3514

3615
asyncgui.start(async_fn())
37-
clock.tick(10)
38-
clock.tick(10) # => Hello
16+
clock.tick(10) # Advances the clock by 10 time units.
17+
clock.tick(10) # Total of 20 time units. The task above will wake up, and prints 'Hello'.
3918
```
4019

41-
These two examples effectively illustrate how this module works but they are not practical.
20+
The example above effectively illustrate how this module works but it's not practical.
4221
In a real-world program, you probably want to call ``clock.tick()`` in a loop or schedule it to be called repeatedly using another scheduling API.
4322
For example, if you are using `PyGame`, you may want to do:
4423

4524
```python
46-
clock = pygame.time.Clock()
47-
vclock = asyncgui_ext.clock.Clock()
25+
pygame_clock = pygame.time.Clock()
26+
clock = asyncgui_ext.clock.Clock()
4827

4928
# main loop
5029
while running:
5130
...
5231

53-
dt = clock.tick(fps)
54-
vclock.tick(dt)
32+
dt = pygame_clock.tick(fps)
33+
clock.tick(dt)
5534
```
5635

5736
And if you are using `Kivy`, you may want to do:
5837

5938
```python
6039
from kivy.clock import Clock
6140

62-
vclock = asyncui_ext.clock.Clock()
63-
Clock.schedule_interval(vclock.tick, 0)
41+
clock = asyncui_ext.clock.Clock()
42+
Clock.schedule_interval(clock.tick, 0)
6443
```
6544

6645
## Installation

sphinx/readme_jp.rst

Lines changed: 15 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,6 @@ ReadMe |ja|
33
===========
44

55
このモジュールは :mod:`asyncgui` を用いるプログラム向けのタイマー機能を提供します。
6-
機能は大別するとコールバック型とasync/await型に分けられ、状況に応じて好きな方を使えます。
7-
まずはコールバック型のAPIを用いた以下のコードを見てください。
8-
9-
.. code-block::
10-
11-
from asyncgui_ext.clock import Clock
12-
13-
clock = Clock()
14-
15-
# 20経過後に関数が呼ばれるようにする。
16-
clock.schedule_once(lambda dt: print("Hello"), 20)
17-
18-
# 時計を10進める。
19-
clock.tick(10)
20-
21-
# 合計で20進むので関数が呼ばれる。
22-
clock.tick(10) # => Hello
23-
24-
:mod:`sched` と同じで時間の単位が決まってない事に気付いたと思います。
25-
APIに渡す時間の単位は統一さえされていれば何でも構いません。
26-
次は同じ事をするasync/await型のコードを見てください。
276

287
.. code-block::
298
@@ -33,38 +12,41 @@ APIに渡す時間の単位は統一さえされていれば何でも構いま
3312
clock = Clock()
3413
3514
async def async_fn():
36-
await clock.sleep(20)
15+
await clock.sleep(20) # 時間が20経過するのを待つ。
3716
print("Hello")
3817
3918
asyncgui.start(async_fn())
40-
clock.tick(10)
41-
clock.tick(10) # => Hello
19+
clock.tick(10) # 時間が10進む。
20+
clock.tick(10) # 合計で20進むのでタスクが再開し 'Hello' が表示される。
21+
22+
この様に ``clock.tick()`` を呼ぶ事で時計内部の時が進み停止中のタスクが再開するわけです。
23+
また :mod:`sched` と同じで時間の単位が決まってない事に気付いたと思います。
24+
APIに渡す時間の単位は統一さえされていれば何でも構いません。
4225

43-
この様に ``clock.tick()`` を呼ぶ事で時計内部の時が進み、関数が呼ばれたり停止中のタスクが再開するわけです。
44-
ただこれらの例はこのモジュールの仕組みを示してはいますが実用的な使い方ではありません。
45-
実際のプログラムでは ``clock.tick()`` をループ内で呼んだり別のタイマーを用いて定期的に呼ぶ事になると思います。
26+
ただ上記の例はこのモジュールの仕組みを示しているだけであり実用的な使い方ではありません。
27+
実際のプログラムでは ``clock.tick()`` をメインループ内で呼んだり別のタイマーを用いて定期的に呼ぶ事になると思います。
4628
例えば ``PyGame`` を使っているなら以下のように、
4729

4830
.. code-block::
4931
50-
clock = pygame.time.Clock()
51-
vclock = asyncgui_ext.clock.Clock()
32+
pygame_clock = pygame.time.Clock()
33+
clock = asyncgui_ext.clock.Clock()
5234
5335
# メインループ
5436
while running:
5537
...
5638
57-
dt = clock.tick(fps)
58-
vclock.tick(dt)
39+
dt = pygame_clock.tick(fps)
40+
clock.tick(dt)
5941
6042
``Kivy`` を使っているなら以下のようになるでしょう。
6143

6244
.. code-block::
6345
6446
from kivy.clock import Clock
6547
66-
vclock = asyncui_ext.clock.Clock()
67-
Clock.schedule_interval(vclock.tick, 0)
48+
clock = asyncui_ext.clock.Clock()
49+
Clock.schedule_interval(clock.tick, 0)
6850
6951
インストール方法
7052
-----------------------

src/asyncgui_ext/clock.py

Lines changed: 8 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from threading import Thread
1212
from concurrent.futures import ThreadPoolExecutor
1313

14-
from asyncgui import AsyncEvent, Cancelled, Task, move_on_when, _sleep_forever, _current_task
14+
from asyncgui import Cancelled, Task, move_on_when, _sleep_forever, _current_task
1515

1616
TimeUnit = TypeVar("TimeUnit")
1717
ClockCallback: TypeAlias = Callable[[TimeUnit], None]
@@ -82,8 +82,6 @@ def tick(self, delta_time):
8282
tba_append(e)
8383
continue
8484
e.callback(cur_time - e._last_tick)
85-
if e._interval is None:
86-
continue
8785
e._deadline += e._interval
8886
e._last_tick = cur_time
8987
tba_append(e)
@@ -92,30 +90,6 @@ def tick(self, delta_time):
9290
self._events = events_tba
9391
self._events_to_be_added = events
9492

95-
def schedule_once(self, func, delay) -> ClockEvent:
96-
'''
97-
Schedules the ``func`` to be called after the ``delay``.
98-
99-
To unschedule:
100-
101-
.. code-block::
102-
103-
event = clock.schedule_once(func, 10)
104-
event.cancel()
105-
106-
You can use this ``event`` object as a context manager, and it will be automatically unscheduled when the
107-
context manager exits.
108-
109-
.. code-block::
110-
111-
with clock.schedule_once(func, 10):
112-
...
113-
'''
114-
cur_time = self._cur_time
115-
event = ClockEvent(cur_time + delay, cur_time, func, None)
116-
self._events_to_be_added.append(event)
117-
return event
118-
11993
def schedule_interval(self, func, interval) -> ClockEvent:
12094
'''
12195
Schedules the ``func`` to be called repeatedly at a specified interval.
@@ -125,6 +99,7 @@ def schedule_interval(self, func, interval) -> ClockEvent:
12599
self._events_to_be_added.append(event)
126100
return event
127101

102+
@types.coroutine
128103
def sleep(self, duration) -> Awaitable:
129104
'''
130105
Waits for a specified period of time.
@@ -133,9 +108,12 @@ def sleep(self, duration) -> Awaitable:
133108
134109
await clock.sleep(10)
135110
'''
136-
ev = AsyncEvent()
137-
self.schedule_once(ev.fire, duration)
138-
return ev.wait()
111+
task = (yield _current_task)[0][0]
112+
event = self.schedule_interval(task._step, duration)
113+
try:
114+
yield _sleep_forever
115+
finally:
116+
event.cancel()
139117

140118
def move_on_after(self, timeout) -> AbstractAsyncContextManager[Task]:
141119
'''

tests/clock/test_schedule_xxx.py

Lines changed: 0 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,3 @@
1-
def test_once(clock):
2-
dt_list = []
3-
func = dt_list.append
4-
5-
clock.schedule_once(func, 100)
6-
assert dt_list == []
7-
clock.schedule_once(func, 50)
8-
assert dt_list == []
9-
clock.tick(20)
10-
assert dt_list == []
11-
clock.schedule_once(func, 40)
12-
assert dt_list == []
13-
clock.tick(50)
14-
assert sorted(dt_list) == [50, 70, ]
15-
clock.tick(50)
16-
assert sorted(dt_list) == [50, 70, 120, ]
17-
18-
19-
def test_once_zero_delay(clock):
20-
dt_list = []
21-
22-
clock.schedule_once(dt_list.append, 0)
23-
assert dt_list == []
24-
clock.tick(0)
25-
assert dt_list == [0, ]
26-
clock.tick(0)
27-
assert dt_list == [0, ]
28-
29-
30-
def test_once_cancel(clock):
31-
dt_list = []
32-
func = dt_list.append
33-
34-
e = clock.schedule_once(func, 100)
35-
clock.schedule_once(func, 50)
36-
e.cancel()
37-
assert dt_list == []
38-
clock.tick(60)
39-
assert dt_list == [60, ]
40-
clock.tick(60)
41-
assert dt_list == [60, ]
42-
43-
44-
def test_once_cancel_from_a_callback(clock):
45-
dt_list = []
46-
47-
e = clock.schedule_once(dt_list.append, 100)
48-
clock.schedule_once(lambda __: e.cancel(), 50)
49-
assert dt_list == []
50-
clock.tick(60)
51-
assert dt_list == []
52-
clock.tick(60)
53-
assert dt_list == []
54-
55-
56-
def test_once_schedule_from_a_callback(clock):
57-
dt_list = []
58-
59-
clock.schedule_once(lambda dt: clock.schedule_once(dt_list.append, 50), 50)
60-
assert dt_list == []
61-
clock.tick(50)
62-
assert dt_list == []
63-
clock.tick(50)
64-
assert dt_list == [50, ]
65-
clock.tick(50)
66-
assert dt_list == [50, ]
67-
68-
691
def test_interval(clock):
702
dt_list = []
713

0 commit comments

Comments
 (0)