05/23 Phase 14
This commit is contained in:
+112
-1
@@ -7,13 +7,14 @@ on_fetch: callable injected by main.py to trigger the shared fetch thread.
|
||||
"""
|
||||
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
from tkinter import ttk, messagebox
|
||||
import logging
|
||||
|
||||
from db.database import get_db_stats
|
||||
from db.models import (
|
||||
get_all_games, get_draw_count, set_game_active,
|
||||
get_last_fetch_per_source, get_predictions,
|
||||
add_game, delete_game, _BUILTIN_GAMES,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -131,6 +132,17 @@ class SettingsScreen(ttk.Frame):
|
||||
foreground="#777777",
|
||||
).pack(side="left", padx=(16, 0))
|
||||
|
||||
if game["name"] not in _BUILTIN_GAMES:
|
||||
ttk.Button(
|
||||
row, text="Delete",
|
||||
command=lambda gid=game["id"], gname=game["name"]: self._delete_game(gid, gname),
|
||||
).pack(side="left", padx=(12, 0))
|
||||
|
||||
ttk.Button(
|
||||
self._games_body, text="+ Add Custom Game",
|
||||
command=self._open_add_game_dialog,
|
||||
).pack(anchor="w", pady=(8, 0))
|
||||
|
||||
def _refresh_sources(self):
|
||||
for w in self._sources_body.winfo_children():
|
||||
w.destroy()
|
||||
@@ -190,3 +202,102 @@ class SettingsScreen(ttk.Frame):
|
||||
self._on_fetch()
|
||||
else:
|
||||
self._fetch_msg_var.set("Fetch not available.")
|
||||
|
||||
def _open_add_game_dialog(self):
|
||||
_AddGameDialog(self, on_save=self._refresh_games)
|
||||
|
||||
def _delete_game(self, game_id: int, game_name: str):
|
||||
count = get_draw_count(game_id)
|
||||
if count > 0:
|
||||
messagebox.showwarning(
|
||||
"Cannot Delete",
|
||||
f"'{game_name}' has {count:,} draw records.\n"
|
||||
"Delete all draws for this game before removing it.",
|
||||
)
|
||||
return
|
||||
if not messagebox.askyesno("Delete Game",
|
||||
f"Permanently delete '{game_name}'?"):
|
||||
return
|
||||
if delete_game(game_id):
|
||||
self._refresh_games()
|
||||
else:
|
||||
messagebox.showerror("Error", f"Could not delete '{game_name}'.")
|
||||
|
||||
|
||||
class _AddGameDialog(tk.Toplevel):
|
||||
"""Modal dialog for adding a custom lottery game."""
|
||||
|
||||
def __init__(self, parent, on_save):
|
||||
super().__init__(parent)
|
||||
self.title("Add Custom Game")
|
||||
self.resizable(False, False)
|
||||
self.grab_set() # modal
|
||||
self._on_save = on_save
|
||||
self._build()
|
||||
self.transient(parent)
|
||||
self.wait_visibility()
|
||||
self.focus_set()
|
||||
|
||||
def _build(self):
|
||||
pad = {"padx": 10, "pady": 4}
|
||||
|
||||
# Name
|
||||
r = ttk.Frame(self, padding=(14, 14, 14, 4))
|
||||
r.pack(fill="x")
|
||||
ttk.Label(r, text="Game name:", width=18, anchor="w").pack(side="left")
|
||||
self._name_var = tk.StringVar()
|
||||
ttk.Entry(r, textvariable=self._name_var, width=22).pack(side="left")
|
||||
|
||||
# Main balls
|
||||
r2 = ttk.Frame(self, padding=(14, 4, 14, 4))
|
||||
r2.pack(fill="x")
|
||||
ttk.Label(r2, text="Main balls:", width=18, anchor="w").pack(side="left")
|
||||
self._main_count = tk.IntVar(value=5)
|
||||
ttk.Spinbox(r2, from_=1, to=10, textvariable=self._main_count,
|
||||
width=5).pack(side="left")
|
||||
ttk.Label(r2, text=" out of 1–", foreground="#555").pack(side="left")
|
||||
self._main_max = tk.IntVar(value=69)
|
||||
ttk.Spinbox(r2, from_=1, to=99, textvariable=self._main_max,
|
||||
width=5).pack(side="left")
|
||||
|
||||
# Bonus balls
|
||||
r3 = ttk.Frame(self, padding=(14, 4, 14, 4))
|
||||
r3.pack(fill="x")
|
||||
ttk.Label(r3, text="Bonus balls:", width=18, anchor="w").pack(side="left")
|
||||
self._bonus_count = tk.IntVar(value=1)
|
||||
ttk.Spinbox(r3, from_=0, to=5, textvariable=self._bonus_count,
|
||||
width=5).pack(side="left")
|
||||
ttk.Label(r3, text=" out of 1–", foreground="#555").pack(side="left")
|
||||
self._bonus_max = tk.IntVar(value=26)
|
||||
ttk.Spinbox(r3, from_=0, to=99, textvariable=self._bonus_max,
|
||||
width=5).pack(side="left")
|
||||
|
||||
# Error label
|
||||
self._err_var = tk.StringVar()
|
||||
ttk.Label(self, textvariable=self._err_var,
|
||||
foreground="#c0392b",
|
||||
padding=(14, 2)).pack(fill="x")
|
||||
|
||||
# Buttons
|
||||
btn_row = ttk.Frame(self, padding=(14, 4, 14, 14))
|
||||
btn_row.pack(fill="x")
|
||||
ttk.Button(btn_row, text="Add Game", command=self._save).pack(side="right")
|
||||
ttk.Button(btn_row, text="Cancel", command=self.destroy).pack(side="right", padx=(0, 6))
|
||||
|
||||
def _save(self):
|
||||
name = self._name_var.get().strip()
|
||||
if not name:
|
||||
self._err_var.set("Game name is required.")
|
||||
return
|
||||
try:
|
||||
add_game(
|
||||
name=name,
|
||||
main_count=int(self._main_count.get()),
|
||||
main_max=int(self._main_max.get()),
|
||||
bonus_count=int(self._bonus_count.get()),
|
||||
bonus_max=int(self._bonus_max.get()),
|
||||
)
|
||||
self._on_save()
|
||||
self.destroy()
|
||||
except ValueError as e:
|
||||
self._err_var.set(str(e))
|
||||
|
||||
Reference in New Issue
Block a user