77from __future__ import annotations
88
99import functools
10- import threading
1110import time
1211from tkinter import Canvas , TclError , Tk
1312from typing import TYPE_CHECKING , Any
@@ -31,7 +30,7 @@ def validate_colors(ctx: Any, param: Any, value: str) -> list[str]: # noqa: ARG
3130 app = _app ()
3231 colors = value .split ()
3332 if len (colors ) not in (2 , 3 , 4 , 6 ):
34- raise click .BadParameter (f"Give 2, 3, 4 or 6 colors (not { len (colors )} " )
33+ raise click .BadParameter (f"Give 2, 3, 4 or 6 colors (not { len (colors )} ) " )
3534 for c in colors :
3635 try :
3736 app .winfo_rgb (c )
@@ -53,19 +52,24 @@ def validate_colors(ctx: Any, param: Any, value: str) -> list[str]: # noqa: ARG
5352
5453
5554@click .command
56- @click .option ("--colors" , callback = validate_colors , default = DEFAULT_COLORS )
55+ @click .option (
56+ "--colors" ,
57+ callback = validate_colors ,
58+ default = DEFAULT_COLORS ,
59+ metavar = "COLORS" ,
60+ help = "2, 3, 4, or 6 Tk color values" ,
61+ )
5762@click .option ("--size" , default = 48 )
5863@click .option ("--min-size" , default = None )
5964def main (colors : list [str ], size : int , min_size : int | None ) -> None : # noqa: PLR0915
6065 """Visualize the WWVB signal in realtime"""
6166 if min_size is None :
6267 min_size = size
6368
64- def sleep_deadline (deadline : float ) -> None :
65- """Sleep until a deadline"""
69+ def deadline_ms (deadline : float ) -> int :
70+ """Compute the number of ms until a deadline"""
6671 now = time .time ()
67- if deadline > now :
68- time .sleep (deadline - now )
72+ return int (max (0 , deadline - now ) * 1000 )
6973
7074 def wwvbtick () -> Generator [tuple [float , wwvb .AmplitudeModulation ], None , None ]:
7175 """Yield consecutive values of the WWVB amplitude signal, going from minute to minute"""
@@ -127,18 +131,23 @@ def led_off(i: int) -> None:
127131 """Turn the canvas's virtual LED off"""
128132 canvas .itemconfigure (circle , fill = colors [i ])
129133
130- def thread_func () -> None :
131- """Update the canvas virtual LED"""
134+ def controller_func () -> Generator [ int ] :
135+ """Update the canvas virtual LED, yielding the number of ms until the next change """
132136 for stamp , code in wwvbsmarttick ():
133- sleep_deadline (stamp )
137+ yield deadline_ms (stamp )
134138 led_on (code )
135139 app .update ()
136- sleep_deadline (stamp + 0.2 + 0.3 * int (code ))
140+ yield deadline_ms (stamp + 0.2 + 0.3 * int (code ))
137141 led_off (code )
138142 app .update ()
139143
140- thread = threading .Thread (target = thread_func , daemon = True )
141- thread .start ()
144+ controller = controller_func ().__next__
145+
146+ def after_func () -> None :
147+ """Repeatedly run the controller after the desired interval"""
148+ app .after (controller (), after_func )
149+
150+ app .after_idle (after_func )
142151 app .mainloop ()
143152
144153
0 commit comments