Merge pull request #133 from jepler/dethread-wwvbtk

No need for threading in wwvbtk
This commit is contained in:
Jeff Epler 2025-04-17 13:02:07 -05:00 committed by GitHub
commit 2d574f6d53
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 23 additions and 14 deletions

View file

@ -19,7 +19,7 @@ dependencies = {file = "requirements.txt"}
write_to = "src/wwvb/__version__.py"
[tool.ruff.lint]
select = ["E", "F", "D", "I", "N", "UP", "YTT", "BLE", "B", "FBT", "A", "COM", "C4", "DTZ", "FA", "ISC", "ICN", "PIE", "PYI", "Q", "RET", "SIM", "TID", "TCH", "ARG", "PTH", "C", "R", "W", "FLY", "RUF", "PL"]
ignore = ["D203", "D213", "D400", "D415", "ISC001", "E741", "C901", "PLR0911", "PLR2004", "PLR0913"]
ignore = ["D203", "D213", "D400", "D415", "ISC001", "E741", "C901", "PLR0911", "PLR2004", "PLR0913", "COM812"]
[tool.ruff]
line-length = 120
[project]

View file

@ -7,7 +7,6 @@
from __future__ import annotations
import functools
import threading
import time
from tkinter import Canvas, TclError, Tk
from typing import TYPE_CHECKING, Any
@ -31,7 +30,7 @@ def validate_colors(ctx: Any, param: Any, value: str) -> list[str]: # noqa: ARG
app = _app()
colors = value.split()
if len(colors) not in (2, 3, 4, 6):
raise click.BadParameter(f"Give 2, 3, 4 or 6 colors (not {len(colors)}")
raise click.BadParameter(f"Give 2, 3, 4 or 6 colors (not {len(colors)})")
for c in colors:
try:
app.winfo_rgb(c)
@ -53,7 +52,13 @@ DEFAULT_COLORS = "#3c3c3c #3c3c3c #3c3c3c #cc3c3c #88883c #3ccc3c"
@click.command
@click.option("--colors", callback=validate_colors, default=DEFAULT_COLORS)
@click.option(
"--colors",
callback=validate_colors,
default=DEFAULT_COLORS,
metavar="COLORS",
help="2, 3, 4, or 6 Tk color values",
)
@click.option("--size", default=48)
@click.option("--min-size", default=None)
def main(colors: list[str], size: int, min_size: int | None) -> None: # noqa: PLR0915
@ -61,11 +66,10 @@ def main(colors: list[str], size: int, min_size: int | None) -> None: # noqa: P
if min_size is None:
min_size = size
def sleep_deadline(deadline: float) -> None:
"""Sleep until a deadline"""
def deadline_ms(deadline: float) -> int:
"""Compute the number of ms until a deadline"""
now = time.time()
if deadline > now:
time.sleep(deadline - now)
return int(max(0, deadline - now) * 1000)
def wwvbtick() -> Generator[tuple[float, wwvb.AmplitudeModulation], None, None]:
"""Yield consecutive values of the WWVB amplitude signal, going from minute to minute"""
@ -127,18 +131,23 @@ def main(colors: list[str], size: int, min_size: int | None) -> None: # noqa: P
"""Turn the canvas's virtual LED off"""
canvas.itemconfigure(circle, fill=colors[i])
def thread_func() -> None:
"""Update the canvas virtual LED"""
def controller_func() -> Generator[int]:
"""Update the canvas virtual LED, yielding the number of ms until the next change"""
for stamp, code in wwvbsmarttick():
sleep_deadline(stamp)
yield deadline_ms(stamp)
led_on(code)
app.update()
sleep_deadline(stamp + 0.2 + 0.3 * int(code))
yield deadline_ms(stamp + 0.2 + 0.3 * int(code))
led_off(code)
app.update()
thread = threading.Thread(target=thread_func, daemon=True)
thread.start()
controller = controller_func().__next__
def after_func() -> None:
"""Repeatedly run the controller after the desired interval"""
app.after(controller(), after_func)
app.after_idle(after_func)
app.mainloop()