|
| 1 | +"""Dialog for selecting which complex problem to solve.""" |
| 2 | + |
| 3 | +from __future__ import annotations |
| 4 | + |
| 5 | +import tkinter as tk |
| 6 | +from tkinter import ttk |
| 7 | + |
| 8 | +from complex_problems.problem_registry import PROBLEM_REGISTRY |
| 9 | +from config import get_env_from_schema |
| 10 | +from frontend.ui_dialogs import ToolTip, setup_arrow_enter_navigation |
| 11 | +from frontend.window_utils import bind_wraplength, fit_and_center, make_modal |
| 12 | +from utils import get_logger |
| 13 | + |
| 14 | +logger = get_logger(__name__) |
| 15 | + |
| 16 | + |
| 17 | +class ComplexProblemsDialog: |
| 18 | + """Window for selecting a complex problem to solve. |
| 19 | +
|
| 20 | + Args: |
| 21 | + parent: Parent window. |
| 22 | + """ |
| 23 | + |
| 24 | + def __init__(self, parent: tk.Tk | tk.Toplevel) -> None: |
| 25 | + self.parent = parent |
| 26 | + self.win = tk.Toplevel(parent) |
| 27 | + self.win.title("Complex Problems") |
| 28 | + |
| 29 | + bg: str = get_env_from_schema("UI_BACKGROUND") |
| 30 | + self.win.configure(bg=bg) |
| 31 | + |
| 32 | + self._build_ui() |
| 33 | + |
| 34 | + fit_and_center(self.win, min_width=640, min_height=680, resizable=True) |
| 35 | + make_modal(self.win, parent) |
| 36 | + logger.info("Complex problems dialog opened") |
| 37 | + |
| 38 | + def _build_ui(self) -> None: |
| 39 | + """Construct the dialog layout.""" |
| 40 | + pad: int = get_env_from_schema("UI_PADDING") |
| 41 | + |
| 42 | + main_frame = ttk.Frame(self.win, padding=pad * 2) |
| 43 | + main_frame.pack(fill=tk.BOTH, expand=True) |
| 44 | + |
| 45 | + ttk.Label( |
| 46 | + main_frame, |
| 47 | + text="Complex Problems", |
| 48 | + style="Title.TLabel", |
| 49 | + ).pack(pady=(0, pad)) |
| 50 | + |
| 51 | + desc = ttk.Label( |
| 52 | + main_frame, |
| 53 | + text=( |
| 54 | + "Select a special problem to solve. Each problem has\n" |
| 55 | + "custom parameters, statistics, and visualizations." |
| 56 | + ), |
| 57 | + style="Small.TLabel", |
| 58 | + justify=tk.CENTER, |
| 59 | + ) |
| 60 | + desc.pack(pady=(0, pad * 2)) |
| 61 | + bind_wraplength(main_frame, desc, pad=4 * pad, min_wrap=200) |
| 62 | + |
| 63 | + # Buttons for each problem |
| 64 | + btn_frame = ttk.Frame(main_frame) |
| 65 | + btn_frame.pack(fill=tk.BOTH, expand=True) |
| 66 | + |
| 67 | + for i, (prob_id, descriptor) in enumerate(PROBLEM_REGISTRY.items()): |
| 68 | + btn = ttk.Button( |
| 69 | + btn_frame, |
| 70 | + text=descriptor.name, |
| 71 | + command=self._make_open_callback(descriptor), |
| 72 | + ) |
| 73 | + btn.pack(fill=tk.X, padx=pad, pady=pad // 2) |
| 74 | + ToolTip(btn, descriptor.description) |
| 75 | + |
| 76 | + btn_close = ttk.Button( |
| 77 | + main_frame, |
| 78 | + text="Close", |
| 79 | + style="Cancel.TButton", |
| 80 | + command=self.win.destroy, |
| 81 | + ) |
| 82 | + btn_close.pack(pady=(pad * 2, 0)) |
| 83 | + |
| 84 | + setup_arrow_enter_navigation([[btn_close]]) |
| 85 | + btn_close.focus_set() |
| 86 | + |
| 87 | + def _make_open_callback(self, descriptor): |
| 88 | + """Create a callback that opens the problem dialog and closes this one.""" |
| 89 | + |
| 90 | + def _on_click() -> None: |
| 91 | + self.win.destroy() |
| 92 | + descriptor.open_dialog(self.parent) |
| 93 | + |
| 94 | + return _on_click |
0 commit comments