""" ui/predictor_ui.py ------------------ Prediction generator screen — two tabs inside a ttk.Notebook. Generate tab — pick strategy + game + count → generate tickets → save to DB Saved tab — browse all saved predictions, see match count vs last real draw, delete individual rows or clear all """ import os import tkinter as tk from tkinter import ttk, filedialog, messagebox import logging from db.models import ( get_all_games, get_game_by_name, get_last_draw, insert_prediction, get_predictions, delete_prediction, delete_all_predictions, ) from core.predictor import ( hot_numbers, due_numbers, weighted_random, monte_carlo, positional_pick, quick_pick, ) from core.checker import check_ticket, parse_numbers from core.exporter import export_predictions_excel, export_predictions_csv, ensure_exports_dir from ui.widgets import BallsBar logger = logging.getLogger(__name__) _STRATEGIES = { "Hot Numbers": hot_numbers, "Due Numbers": due_numbers, "Weighted Random": weighted_random, "Monte Carlo": monte_carlo, "Positional": positional_pick, "Quick Pick": quick_pick, } _DESCRIPTIONS = { "Hot Numbers": "Top 5 most frequent numbers from the last 100 draws.", "Due Numbers": "Numbers most overdue based on expected frequency gap.", "Weighted Random": "Random pick weighted by each number's historical frequency.", "Monte Carlo": "10,000 simulated draws — pick the most often-selected numbers.", "Positional": "Most frequent number at each draw position (1–5).", "Quick Pick": "Pure random selection — no historical data required.", } _TICKET_COUNTS = [str(n) for n in range(1, 11)] def _count_matches(pred_numbers: str, last_draw) -> str: """Compare prediction numbers to last draw, return 'X/5' string.""" if last_draw is None: return "—" try: pred_set = {int(n) for n in pred_numbers.split(",")} draw_set = {int(n) for n in last_draw["numbers"].split(",")} matched = len(pred_set & draw_set) return f"{matched}/{len(pred_set)}" except Exception: return "—" class PredictorScreen(ttk.Frame): def __init__(self, parent, **kwargs): super().__init__(parent, **kwargs) self._game_id: int | None = None self._tickets: list[dict] = [] self._strategy_name: str = "Hot Numbers" self._build_ui() # ── UI construction ─────────────────────────────────────────────────────── def _build_ui(self): nb = ttk.Notebook(self) nb.pack(fill="both", expand=True) gen_frame = ttk.Frame(nb) saved_frame = ttk.Frame(nb) check_frame = ttk.Frame(nb) nb.add(gen_frame, text="Generate") nb.add(saved_frame, text="Saved") nb.add(check_frame, text="Check Ticket") self._build_generate_tab(gen_frame) self._build_saved_tab(saved_frame) self._build_check_tab(check_frame) # ── Generate tab ────────────────────────────────────────────────────────── def _build_generate_tab(self, parent): bar = ttk.Frame(parent, padding=(6, 6, 6, 4)) bar.pack(fill="x") ttk.Label(bar, text="Game:").pack(side="left") self._game_var = tk.StringVar() self._game_cb = ttk.Combobox( bar, textvariable=self._game_var, state="readonly", width=15 ) self._game_cb.pack(side="left", padx=(4, 14)) self._game_cb.bind("<>", lambda _: self._on_game_change()) ttk.Label(bar, text="Strategy:").pack(side="left") self._strategy_var = tk.StringVar(value="Hot Numbers") strategy_cb = ttk.Combobox( bar, textvariable=self._strategy_var, values=list(_STRATEGIES.keys()), state="readonly", width=16, ) strategy_cb.pack(side="left", padx=(4, 14)) strategy_cb.bind("<>", lambda _: self._on_strategy_change()) ttk.Label(bar, text="Tickets:").pack(side="left") self._count_var = tk.StringVar(value="1") ttk.Combobox( bar, textvariable=self._count_var, values=_TICKET_COUNTS, state="readonly", width=4, ).pack(side="left", padx=(4, 0)) ttk.Label(bar, text="Exclude:", padding=(12, 0, 0, 0)).pack(side="left") self._exclude_var = tk.StringVar() ttk.Entry(bar, textvariable=self._exclude_var, width=14).pack(side="left", padx=(4, 0)) self._gen_btn = ttk.Button(bar, text="Generate", command=self._generate) self._gen_btn.pack(side="left", padx=(12, 0)) # Results treeview tree_frame = ttk.Frame(parent) tree_frame.pack(fill="both", expand=True, padx=6, pady=(4, 0)) cols = ("#", "numbers", "bonus") self._tree = ttk.Treeview( tree_frame, columns=cols, show="headings", selectmode="browse" ) self._tree.heading("#", text="#") self._tree.heading("numbers", text="Numbers") self._tree.heading("bonus", text="Bonus") self._tree.column("#", width=40, anchor="center", stretch=False) self._tree.column("numbers", width=280, anchor="w") self._tree.column("bonus", width=70, anchor="center", stretch=False) vsb = ttk.Scrollbar(tree_frame, orient="vertical", command=self._tree.yview) self._tree.configure(yscrollcommand=vsb.set) self._tree.pack(side="left", fill="both", expand=True) vsb.pack(side="right", fill="y") self._tree.bind("<>", self._on_gen_row_select) # ── Ball detail strip ───────────────────────────────────────────────── ttk.Separator(parent, orient="horizontal").pack(fill="x", padx=6) self._gen_detail = ttk.Frame(parent, padding=(8, 3)) self._gen_detail.pack(fill="x") # Bottom bar bottom = ttk.Frame(parent, padding=(6, 4)) bottom.pack(fill="x") self._desc_var = tk.StringVar(value=_DESCRIPTIONS["Hot Numbers"]) ttk.Label( bottom, textvariable=self._desc_var, foreground="#555555", anchor="w", ).pack(side="left", fill="x", expand=True) self._status_var = tk.StringVar() ttk.Label(bottom, textvariable=self._status_var, foreground="#27ae60").pack(side="left", padx=(8, 0)) self._save_btn = ttk.Button( bottom, text="Save to DB", command=self._save, state="disabled" ) self._save_btn.pack(side="right") self._copy_btn = ttk.Button( bottom, text="Copy", command=self._copy_tickets, state="disabled" ) self._copy_btn.pack(side="right", padx=(0, 4)) ttk.Button(bottom, text="Clear", command=self._clear ).pack(side="right", padx=(0, 4)) ttk.Button(bottom, text="Export CSV", command=self._export_csv ).pack(side="right", padx=(0, 4)) ttk.Button(bottom, text="Export Excel", command=self._export_excel ).pack(side="right", padx=(0, 4)) # ── Saved tab ───────────────────────────────────────────────────────────── def _build_saved_tab(self, parent): # Filter + action bar bar = ttk.Frame(parent, padding=(6, 6, 6, 4)) bar.pack(fill="x") ttk.Label(bar, text="Game:").pack(side="left") self._saved_game_var = tk.StringVar(value="All Games") self._saved_game_cb = ttk.Combobox( bar, textvariable=self._saved_game_var, state="readonly", width=15 ) self._saved_game_cb.pack(side="left", padx=(4, 14)) self._saved_game_cb.bind("<>", lambda _: self._refresh_saved()) ttk.Button(bar, text="↻ Refresh", command=self._refresh_saved).pack(side="left", padx=(0, 4)) ttk.Button(bar, text="Delete Selected", command=self._delete_selected).pack(side="left", padx=(0, 4)) ttk.Button(bar, text="Clear All", command=self._clear_all_saved).pack(side="left") self._saved_count_var = tk.StringVar(value="0 saved") ttk.Label(bar, textvariable=self._saved_count_var, foreground="#777777").pack(side="right") # Treeview tree_frame = ttk.Frame(parent) tree_frame.pack(fill="both", expand=True, padx=6, pady=(2, 6)) s_cols = ("id", "game", "strategy", "numbers", "bonus", "matches", "saved") self._saved_tree = ttk.Treeview( tree_frame, columns=s_cols, show="headings", selectmode="extended" ) self._saved_tree.heading("id", text="ID") self._saved_tree.heading("game", text="Game") self._saved_tree.heading("strategy", text="Strategy") self._saved_tree.heading("numbers", text="Numbers") self._saved_tree.heading("bonus", text="Bonus") self._saved_tree.heading("matches", text="Matches") self._saved_tree.heading("saved", text="Saved") self._saved_tree.column("id", width=45, anchor="center", stretch=False) self._saved_tree.column("game", width=120, anchor="w", stretch=False) self._saved_tree.column("strategy", width=120, anchor="w", stretch=False) self._saved_tree.column("numbers", width=220, anchor="w") self._saved_tree.column("bonus", width=55, anchor="center", stretch=False) self._saved_tree.column("matches", width=65, anchor="center", stretch=False) self._saved_tree.column("saved", width=130, anchor="center", stretch=False) vsb = ttk.Scrollbar(tree_frame, orient="vertical", command=self._saved_tree.yview) hsb = ttk.Scrollbar(tree_frame, orient="horizontal", command=self._saved_tree.xview) self._saved_tree.configure(yscrollcommand=vsb.set, xscrollcommand=hsb.set) self._saved_tree.grid(row=0, column=0, sticky="nsew") vsb.grid(row=0, column=1, sticky="ns") hsb.grid(row=1, column=0, sticky="ew") tree_frame.rowconfigure(0, weight=1) tree_frame.columnconfigure(0, weight=1) # Colour rows by match count self._saved_tree.tag_configure("match_good", foreground="#1e8449") self._saved_tree.tag_configure("match_zero", foreground="#aaaaaa") # ── Refresh / data ──────────────────────────────────────────────────────── def refresh(self): self._load_games() self._load_check_games() self._refresh_saved() def _load_games(self): games = get_all_games(active_only=True) names = [g["name"] for g in games] self._game_cb["values"] = names if not self._game_var.get() or self._game_var.get() not in names: if names: self._game_var.set(names[0]) game = get_game_by_name(self._game_var.get()) self._game_id = game["id"] if game else None saved_names = ["All Games"] + names self._saved_game_cb["values"] = saved_names if self._saved_game_var.get() not in saved_names: self._saved_game_var.set("All Games") def _refresh_saved(self): name = self._saved_game_var.get() game_id = None if name != "All Games": g = get_game_by_name(name) game_id = g["id"] if g else None preds = get_predictions(game_id=game_id, limit=100_000) # Cache last draw per game to avoid repeated DB hits last_draw_cache: dict[int, object] = {} self._saved_tree.delete(*self._saved_tree.get_children()) for p in preds: gid = p["game_id"] if gid not in last_draw_cache: last_draw_cache[gid] = get_last_draw(gid) last = last_draw_cache[gid] match_str = _count_matches(p["numbers"], last) nums_fmt = " ".join(f"{int(n):02d}" for n in p["numbers"].split(",")) saved_ts = p["created_at"][:16].replace("T", " ") try: matched_n = int(match_str.split("/")[0]) tag = "match_good" if matched_n > 0 else "match_zero" except Exception: tag = "" self._saved_tree.insert("", "end", iid=str(p["id"]), values=( p["id"], p["game_name"], p["strategy"], nums_fmt, p["bonus"] or "—", match_str, saved_ts, ), tags=(tag,), ) count = len(preds) self._saved_count_var.set(f"{count} saved prediction{'s' if count != 1 else ''}") # ── Generate tab callbacks ──────────────────────────────────────────────── def _on_game_change(self): game = get_game_by_name(self._game_var.get()) self._game_id = game["id"] if game else None self._clear() def _on_strategy_change(self): self._strategy_name = self._strategy_var.get() self._desc_var.set(_DESCRIPTIONS.get(self._strategy_name, "")) self._clear() def _generate(self): if self._game_id is None: self._status_var.set("Select a game first.") return strategy_fn = _STRATEGIES[self._strategy_var.get()] count = int(self._count_var.get()) self._clear() self._gen_btn.config(state="disabled", text="Generating…") self.update_idletasks() try: excl = self._parse_exclude() tickets = [strategy_fn(self._game_id, exclude=excl) for _ in range(count)] self._tickets = tickets self._display(tickets) self._save_btn.config(state="normal") self._copy_btn.config(state="normal") self._status_var.set(f"{len(tickets)} ticket{'s' if len(tickets) != 1 else ''} generated.") logger.info("[PREDICT] %d ticket(s) generated via %s", count, self._strategy_var.get()) except Exception as e: self._status_var.set(f"Error: {e}") logger.error("[ERROR] Prediction failed: %s", e, exc_info=True) finally: self._gen_btn.config(state="normal", text="Generate") def _display(self, tickets): self._tree.delete(*self._tree.get_children()) game = get_game_by_name(self._game_var.get()) show_bonus = game is not None and game["bonus_count"] > 0 for i, t in enumerate(tickets, start=1): nums_str = " ".join(f"{n:02d}" for n in t["numbers"]) bonus_str = str(t["bonus"]) if show_bonus and t["bonus"] is not None else "—" self._tree.insert("", "end", values=(i, nums_str, bonus_str)) def _save(self): if not self._tickets or self._game_id is None: return strategy_name = self._strategy_var.get() saved = 0 for t in self._tickets: insert_prediction(self._game_id, strategy_name, t["numbers"], t["bonus"]) saved += 1 self._status_var.set(f"Saved {saved} prediction{'s' if saved != 1 else ''} to DB.") self._save_btn.config(state="disabled") self._refresh_saved() logger.info("[PREDICT] Saved %d prediction(s) to DB", saved) def _parse_exclude(self) -> set: raw = self._exclude_var.get().strip() if not raw: return set() result = set() for tok in raw.replace(",", " ").split(): try: result.add(int(tok)) except ValueError: pass return result def _copy_tickets(self): if not self._tickets: return game = get_game_by_name(self._game_var.get()) show_bonus = game is not None and game["bonus_count"] > 0 lines = [] for i, t in enumerate(self._tickets, 1): nums = " ".join(f"{n:02d}" for n in t["numbers"]) line = f"Ticket {i}: {nums}" if show_bonus and t["bonus"] is not None: line += f" + {t['bonus']:02d}" lines.append(line) self.clipboard_clear() self.clipboard_append("\n".join(lines)) self._status_var.set("Copied to clipboard.") def _on_gen_row_select(self, _event=None): for w in self._gen_detail.winfo_children(): w.destroy() sel = self._tree.selection() if not sel: return vals = self._tree.item(sel[0], "values") # vals: (#, numbers_fmt, bonus) try: nums = [int(x) for x in str(vals[1]).split() if x.isdigit()] b_txt = str(vals[2]) bonus = int(b_txt) if b_txt.isdigit() else None except Exception: return if nums: BallsBar(self._gen_detail, numbers=nums, bonus=bonus).pack(anchor="w") def _clear(self): self._tickets = [] self._tree.delete(*self._tree.get_children()) for w in self._gen_detail.winfo_children(): w.destroy() self._save_btn.config(state="disabled") self._copy_btn.config(state="disabled") self._status_var.set("") def _export_excel(self): ensure_exports_dir() fp = filedialog.asksaveasfilename( title="Export predictions", defaultextension=".xlsx", filetypes=[("Excel workbook", "*.xlsx")], initialfile=f"predictions_{__import__('datetime').datetime.now().strftime('%Y%m%d')}.xlsx", ) if not fp: return try: export_predictions_excel(fp, game_id=self._game_id) self._status_var.set(f"Exported → {os.path.basename(fp)}") except Exception as e: messagebox.showerror("Export failed", str(e)) def _export_csv(self): ensure_exports_dir() fp = filedialog.asksaveasfilename( title="Export predictions", defaultextension=".csv", filetypes=[("CSV file", "*.csv")], initialfile=f"predictions_{__import__('datetime').datetime.now().strftime('%Y%m%d')}.csv", ) if not fp: return try: export_predictions_csv(fp, game_id=self._game_id) self._status_var.set(f"Exported → {os.path.basename(fp)}") except Exception as e: messagebox.showerror("Export failed", str(e)) # ── Saved tab callbacks ─────────────────────────────────────────────────── def _delete_selected(self): selected = self._saved_tree.selection() if not selected: return for iid in selected: pred_id = int(self._saved_tree.item(iid, "values")[0]) delete_prediction(pred_id) self._refresh_saved() def _clear_all_saved(self): name = self._saved_game_var.get() game_id = None if name != "All Games": g = get_game_by_name(name) game_id = g["id"] if g else None scope = f" for {name}" if game_id else "" if not messagebox.askyesno("Clear All", f"Delete all saved predictions{scope}?"): return delete_all_predictions(game_id=game_id) self._refresh_saved() # ── Check Ticket tab ────────────────────────────────────────────────────── def _build_check_tab(self, parent): # Input bar bar = ttk.Frame(parent, padding=(6, 8, 6, 4)) bar.pack(fill="x") ttk.Label(bar, text="Game:").pack(side="left") self._chk_game_var = tk.StringVar() self._chk_game_cb = ttk.Combobox( bar, textvariable=self._chk_game_var, state="readonly", width=15 ) self._chk_game_cb.pack(side="left", padx=(4, 14)) ttk.Label(bar, text="Numbers:").pack(side="left") self._chk_nums_var = tk.StringVar() chk_nums_entry = ttk.Entry(bar, textvariable=self._chk_nums_var, width=22) chk_nums_entry.pack(side="left", padx=(4, 4)) chk_nums_entry.bind("", lambda e: self._run_check()) ttk.Label(bar, text="Bonus:").pack(side="left") self._chk_bonus_var = tk.StringVar() chk_bonus_entry = ttk.Entry(bar, textvariable=self._chk_bonus_var, width=5) chk_bonus_entry.pack(side="left", padx=(4, 14)) chk_bonus_entry.bind("", lambda e: self._run_check()) ttk.Button(bar, text="Check", command=self._run_check).pack(side="left", padx=(0, 4)) ttk.Button(bar, text="Clear", command=self._clear_check).pack(side="left") self._chk_status_var = tk.StringVar() ttk.Label(bar, textvariable=self._chk_status_var, foreground="#c0392b").pack(side="left", padx=(12, 0)) # Hint text below bar hint = ttk.Frame(parent, padding=(6, 0, 6, 4)) hint.pack(fill="x") ttk.Label(hint, text="Enter space- or comma-separated main numbers, and optionally a bonus number.", foreground="#888888", font=("TkDefaultFont", 8)).pack(anchor="w") # Results treeview tree_frame = ttk.Frame(parent) tree_frame.pack(fill="both", expand=True, padx=6, pady=(0, 4)) c_cols = ("date", "draw_numbers", "bonus", "main", "bonus_hit", "tier") self._chk_tree = ttk.Treeview( tree_frame, columns=c_cols, show="headings", selectmode="browse" ) self._chk_tree.heading("date", text="Draw Date") self._chk_tree.heading("draw_numbers", text="Draw Numbers") self._chk_tree.heading("bonus", text="Bonus") self._chk_tree.heading("main", text="Main Hits") self._chk_tree.heading("bonus_hit", text="Bonus Hit") self._chk_tree.heading("tier", text="Prize Tier") self._chk_tree.column("date", width=100, anchor="center", stretch=False) self._chk_tree.column("draw_numbers", width=190, anchor="w") self._chk_tree.column("bonus", width=55, anchor="center", stretch=False) self._chk_tree.column("main", width=70, anchor="center", stretch=False) self._chk_tree.column("bonus_hit", width=70, anchor="center", stretch=False) self._chk_tree.column("tier", width=160, anchor="w", stretch=False) vsb = ttk.Scrollbar(tree_frame, orient="vertical", command=self._chk_tree.yview) hsb = ttk.Scrollbar(tree_frame, orient="horizontal", command=self._chk_tree.xview) self._chk_tree.configure(yscrollcommand=vsb.set, xscrollcommand=hsb.set) self._chk_tree.grid(row=0, column=0, sticky="nsew") vsb.grid(row=0, column=1, sticky="ns") hsb.grid(row=1, column=0, sticky="ew") tree_frame.rowconfigure(0, weight=1) tree_frame.columnconfigure(0, weight=1) # Row colour tags self._chk_tree.tag_configure("jackpot", foreground="#8e44ad", font=("TkDefaultFont", 9, "bold")) self._chk_tree.tag_configure("high", foreground="#1e8449") self._chk_tree.tag_configure("low", foreground="#555555") self._chk_tree.bind("<>", self._on_chk_row_select) # ── Ball detail strip ───────────────────────────────────────────────── ttk.Separator(parent, orient="horizontal").pack(fill="x", padx=6) self._chk_detail = ttk.Frame(parent, padding=(8, 3)) self._chk_detail.pack(fill="x") # Summary label self._chk_summary_var = tk.StringVar() ttk.Label(parent, textvariable=self._chk_summary_var, anchor="e", padding=(6, 2)).pack(fill="x") def _run_check(self): self._chk_status_var.set("") self._chk_tree.delete(*self._chk_tree.get_children()) self._chk_summary_var.set("") # Resolve game game = get_game_by_name(self._chk_game_var.get()) if game is None: self._chk_status_var.set("Select a game first.") return # Parse main numbers try: numbers = parse_numbers(self._chk_nums_var.get()) except ValueError as e: self._chk_status_var.set(str(e)) return if len(numbers) != game["main_count"]: self._chk_status_var.set( f"{game['name']} requires {game['main_count']} main numbers " f"(got {len(numbers)})." ) return # Validate range invalid = [n for n in numbers if not (1 <= n <= game["main_max"])] if invalid: self._chk_status_var.set( f"Numbers out of range 1–{game['main_max']}: {invalid}" ) return # Parse optional bonus bonus = None raw_bonus = self._chk_bonus_var.get().strip() if raw_bonus: if not raw_bonus.isdigit(): self._chk_status_var.set("Bonus must be a number.") return bonus = int(raw_bonus) if game["bonus_max"] and not (1 <= bonus <= game["bonus_max"]): self._chk_status_var.set( f"Bonus out of range 1–{game['bonus_max']}." ) return results = check_ticket(game["id"], numbers, bonus=bonus) for r in results: nums_fmt = " ".join(f"{n:02d}" for n in r["draw_numbers"]) bonus_str = str(r["draw_bonus"]) if r["draw_bonus"] is not None else "—" bonus_hit = "✓" if r["bonus_match"] else "—" tier = r["prize_tier"] if tier == "Jackpot": tag = "jackpot" elif r["main_matches"] >= 3 or r["bonus_match"]: tag = "high" else: tag = "low" self._chk_tree.insert("", "end", values=( r["draw_date"], nums_fmt, bonus_str, r["main_matches"], bonus_hit, tier, ), tags=(tag,)) total = len(results) best = results[0]["prize_tier"] if results else "—" self._chk_summary_var.set( f"{total} draw{'s' if total != 1 else ''} matched • Best: {best}" ) def _on_chk_row_select(self, _event=None): for w in self._chk_detail.winfo_children(): w.destroy() sel = self._chk_tree.selection() if not sel: return vals = self._chk_tree.item(sel[0], "values") # vals: (date, draw_numbers_fmt, bonus, main_hits, bonus_hit, tier) try: draw_nums = [int(x) for x in str(vals[1]).split() if x.isdigit()] db_txt = str(vals[2]) draw_bonus = int(db_txt) if db_txt.isdigit() else None except Exception: return if not draw_nums: return # Ticket numbers from the input entries try: ticket_nums = parse_numbers(self._chk_nums_var.get()) except Exception: ticket_nums = [] raw_b = self._chk_bonus_var.get().strip() ticket_bonus = int(raw_b) if raw_b.isdigit() else None ticket_set = set(ticket_nums) draw_set = set(draw_nums) _GREEN = ("#27ae60", "#ffffff") _GREY = ("#cccccc", "#888888") ticket_hi = {n: (_GREEN if n in draw_set else _GREY) for n in ticket_nums} draw_hi = {n: _GREEN for n in draw_nums if n in ticket_set} if ticket_bonus is not None: ticket_hi[ticket_bonus] = ( _GREEN if draw_bonus is not None and ticket_bonus == draw_bonus else _GREY ) if draw_bonus is not None and ticket_bonus is not None and draw_bonus == ticket_bonus: draw_hi[draw_bonus] = _GREEN grid = ttk.Frame(self._chk_detail) grid.pack(anchor="w") if ticket_nums: ttk.Label(grid, text="Ticket:", foreground="#555555", font=("TkDefaultFont", 8), width=7).grid(row=0, column=0, sticky="w") BallsBar(grid, numbers=ticket_nums, bonus=ticket_bonus, highlights=ticket_hi).grid(row=0, column=1, sticky="w") ttk.Label(grid, text="Draw:", foreground="#555555", font=("TkDefaultFont", 8), width=7).grid(row=1, column=0, sticky="w") BallsBar(grid, numbers=draw_nums, bonus=draw_bonus, highlights=draw_hi).grid(row=1, column=1, sticky="w") def _clear_check(self): self._chk_nums_var.set("") self._chk_bonus_var.set("") self._chk_status_var.set("") self._chk_tree.delete(*self._chk_tree.get_children()) for w in self._chk_detail.winfo_children(): w.destroy() self._chk_summary_var.set("") def _load_check_games(self): games = get_all_games(active_only=True) names = [g["name"] for g in games] self._chk_game_cb["values"] = names if not self._chk_game_var.get() or self._chk_game_var.get() not in names: if names: self._chk_game_var.set(names[0])