05/23 Phase 16
This commit is contained in:
+109
@@ -0,0 +1,109 @@
|
||||
"""
|
||||
ui/widgets.py
|
||||
-------------
|
||||
Shared reusable widgets.
|
||||
|
||||
ball_color(number, is_bonus) -> (bg_hex, fg_hex)
|
||||
BallsBar — tk.Canvas that draws a horizontal row of numbered lottery balls
|
||||
"""
|
||||
|
||||
import tkinter as tk
|
||||
|
||||
# (low, high, bg_hex, fg_hex) — range-based colour scheme
|
||||
_RANGES = [
|
||||
( 1, 9, "#f0f0f0", "#333333"),
|
||||
(10, 19, "#ffd700", "#333333"),
|
||||
(20, 29, "#ff7f50", "#ffffff"),
|
||||
(30, 39, "#4a9edd", "#ffffff"),
|
||||
(40, 49, "#3cb371", "#ffffff"),
|
||||
(50, 59, "#9b59b6", "#ffffff"),
|
||||
(60, 69, "#e74c3c", "#ffffff"),
|
||||
(70, 99, "#555555", "#ffffff"),
|
||||
]
|
||||
_BONUS_BG = "#e74c3c"
|
||||
_BONUS_FG = "#ffffff"
|
||||
_FALLBACK = ("#aaaaaa", "#333333")
|
||||
|
||||
|
||||
def ball_color(number: int, is_bonus: bool = False) -> tuple[str, str]:
|
||||
"""Return (bg_hex, fg_hex) for a lottery ball. Pure function, no Tk needed."""
|
||||
if is_bonus:
|
||||
return _BONUS_BG, _BONUS_FG
|
||||
for lo, hi, bg, fg in _RANGES:
|
||||
if lo <= number <= hi:
|
||||
return bg, fg
|
||||
return _FALLBACK
|
||||
|
||||
|
||||
class BallsBar(tk.Canvas):
|
||||
"""
|
||||
Horizontal row of numbered lottery balls drawn on a tk.Canvas.
|
||||
|
||||
numbers — list of main ball integers
|
||||
bonus — optional single bonus ball integer (drawn after a gap, in red)
|
||||
radius — ball radius in pixels (default 14 → 28 px diameter)
|
||||
highlights — {number: (bg_hex, fg_hex)} overrides for specific balls
|
||||
(e.g. green for matched, grey for unmatched in ticket checker)
|
||||
"""
|
||||
|
||||
_GAP = 3 # px between adjacent balls
|
||||
_SEP = 10 # extra px gap before the bonus ball
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
parent,
|
||||
numbers: list[int],
|
||||
bonus: int | None = None,
|
||||
radius: int = 14,
|
||||
highlights: dict | None = None,
|
||||
**kwargs,
|
||||
):
|
||||
self._radius = radius
|
||||
self._highlights = highlights or {}
|
||||
|
||||
diam = radius * 2
|
||||
n_main = len(numbers)
|
||||
n_bonus = 1 if bonus is not None else 0
|
||||
|
||||
width = (
|
||||
n_main * (diam + self._GAP)
|
||||
+ (self._SEP + n_bonus * (diam + self._GAP) if n_bonus else 0)
|
||||
+ 4
|
||||
)
|
||||
height = diam + 4
|
||||
|
||||
# Inherit parent background so the canvas blends in seamlessly
|
||||
if "bg" not in kwargs and "background" not in kwargs:
|
||||
try:
|
||||
kwargs["background"] = parent.cget("background")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
kwargs.setdefault("highlightthickness", 0)
|
||||
kwargs.setdefault("bd", 0)
|
||||
super().__init__(parent, width=width, height=height, **kwargs)
|
||||
|
||||
self._draw(numbers, bonus, diam)
|
||||
|
||||
def _draw(self, numbers: list[int], bonus: int | None, diam: int):
|
||||
r = self._radius
|
||||
x = 2 + r
|
||||
y = r + 2
|
||||
|
||||
for num in numbers:
|
||||
bg, fg = self._highlights.get(num) or ball_color(num, False)
|
||||
self._ball(x, y, num, bg, fg)
|
||||
x += diam + self._GAP
|
||||
|
||||
if bonus is not None:
|
||||
x += self._SEP
|
||||
bg, fg = self._highlights.get(bonus) or ball_color(bonus, True)
|
||||
self._ball(x, y, bonus, bg, fg)
|
||||
|
||||
def _ball(self, cx: int, cy: int, number: int, bg: str, fg: str):
|
||||
r = self._radius - 1
|
||||
self.create_oval(cx - r, cy - r, cx + r, cy + r,
|
||||
fill=bg, outline="#aaaaaa", width=1)
|
||||
size = 7 if number >= 10 else 8
|
||||
self.create_text(cx, cy, text=str(number),
|
||||
font=("TkDefaultFont", size, "bold"), fill=fg)
|
||||
Reference in New Issue
Block a user