195 lines
9.4 KiB
Python
195 lines
9.4 KiB
Python
# FlightMonitor/gui/panels/function_notebook_panel.py
|
|
"""
|
|
Panel managing the area profiles, BBox input, and the function notebooks.
|
|
"""
|
|
import tkinter as tk
|
|
from tkinter import ttk, messagebox
|
|
from typing import Dict, Any, Optional
|
|
|
|
from ...utils.logger import get_logger
|
|
from ...data import config as app_config
|
|
from ...map.map_utils import _is_valid_bbox_dict
|
|
from ...data.area_profile_manager import DEFAULT_PROFILE_NAME
|
|
|
|
module_logger = get_logger(__name__)
|
|
|
|
class FunctionNotebookPanel:
|
|
"""
|
|
Manages the combined Area Profiles, BBox, and Function Notebooks panel.
|
|
"""
|
|
def __init__(self, parent_frame: ttk.Frame, controller: Any):
|
|
self.parent_frame = parent_frame
|
|
self.controller = controller
|
|
|
|
# --- Tkinter Variables ---
|
|
self.lat_min_var = tk.StringVar()
|
|
self.lon_min_var = tk.StringVar()
|
|
self.lat_max_var = tk.StringVar()
|
|
self.lon_max_var = tk.StringVar()
|
|
self.selected_profile_var = tk.StringVar()
|
|
|
|
self._build_ui()
|
|
self.update_profile_list()
|
|
self.set_selected_profile(DEFAULT_PROFILE_NAME)
|
|
|
|
module_logger.debug("FunctionNotebookPanel (unified) initialized.")
|
|
|
|
def _build_ui(self):
|
|
# --- Main Container for Area Management ---
|
|
area_frame = ttk.LabelFrame(self.parent_frame, text="Area Profiles & BBox", padding=10)
|
|
area_frame.pack(side=tk.TOP, fill=tk.X, padx=2, pady=(0, 5))
|
|
|
|
# Configure grid for the area frame
|
|
area_frame.columnconfigure(1, weight=1)
|
|
|
|
# --- Row 0: Profile Selection and Buttons ---
|
|
profile_controls_frame = ttk.Frame(area_frame)
|
|
profile_controls_frame.grid(row=0, column=0, columnspan=4, sticky="ew", pady=(0, 10))
|
|
profile_controls_frame.columnconfigure(0, weight=1) # Let combobox expand
|
|
|
|
self.profile_combobox = ttk.Combobox(
|
|
profile_controls_frame, textvariable=self.selected_profile_var, state="readonly"
|
|
)
|
|
self.profile_combobox.grid(row=0, column=0, sticky="ew", padx=(0, 5))
|
|
self.profile_combobox.bind("<<ComboboxSelected>>", self._on_profile_selected)
|
|
|
|
new_button = ttk.Button(profile_controls_frame, text="New", command=self._on_new_profile, width=5)
|
|
new_button.grid(row=0, column=1, padx=(0, 2))
|
|
|
|
save_button = ttk.Button(profile_controls_frame, text="Save", command=self._on_save_profile, width=5)
|
|
save_button.grid(row=0, column=2, padx=(0, 2))
|
|
|
|
delete_button = ttk.Button(profile_controls_frame, text="Delete", command=self._on_delete_profile, width=7)
|
|
delete_button.grid(row=0, column=3)
|
|
|
|
# --- Row 1 & 2: Bounding Box Entries ---
|
|
ttk.Label(area_frame, text="Lat Min:").grid(row=1, column=0, padx=(0, 2), pady=2, sticky=tk.W)
|
|
self.lat_min_entry = ttk.Entry(area_frame, textvariable=self.lat_min_var)
|
|
self.lat_min_entry.grid(row=1, column=1, padx=(0, 5), pady=2, sticky=tk.EW)
|
|
|
|
ttk.Label(area_frame, text="Lon Min:").grid(row=1, column=2, padx=(5, 2), pady=2, sticky=tk.W)
|
|
self.lon_min_entry = ttk.Entry(area_frame, textvariable=self.lon_min_var)
|
|
self.lon_min_entry.grid(row=1, column=3, padx=(0, 0), pady=2, sticky=tk.EW)
|
|
|
|
ttk.Label(area_frame, text="Lat Max:").grid(row=2, column=0, padx=(0, 2), pady=2, sticky=tk.W)
|
|
self.lat_max_entry = ttk.Entry(area_frame, textvariable=self.lat_max_var)
|
|
self.lat_max_entry.grid(row=2, column=1, padx=(0, 5), pady=2, sticky=tk.EW)
|
|
|
|
ttk.Label(area_frame, text="Lon Max:").grid(row=2, column=2, padx=(5, 2), pady=2, sticky=tk.W)
|
|
self.lon_max_entry = ttk.Entry(area_frame, textvariable=self.lon_max_var)
|
|
self.lon_max_entry.grid(row=2, column=3, padx=(0, 0), pady=2, sticky=tk.EW)
|
|
|
|
# --- Function Notebook ---
|
|
self.function_notebook = ttk.Notebook(self.parent_frame)
|
|
self.function_notebook.pack(side=tk.TOP, fill=tk.BOTH, expand=True, padx=2, pady=2)
|
|
|
|
# --- Live Tab ---
|
|
live_tab_frame = ttk.Frame(self.function_notebook, padding=10)
|
|
self.function_notebook.add(live_tab_frame, text="Live Monitor")
|
|
self.start_live_button = ttk.Button(live_tab_frame, text="Start Live Monitoring", command=self._on_start_live_monitoring)
|
|
self.start_live_button.pack(side=tk.LEFT, padx=5, pady=5)
|
|
self.stop_live_button = ttk.Button(live_tab_frame, text="Stop Live Monitoring", command=self._on_stop_live_monitoring, state=tk.DISABLED)
|
|
self.stop_live_button.pack(side=tk.LEFT, padx=5, pady=5)
|
|
|
|
# --- Historical Download Tab ---
|
|
download_tab_frame = ttk.Frame(self.function_notebook, padding=10)
|
|
self.function_notebook.add(download_tab_frame, text="Historical Download")
|
|
ttk.Label(download_tab_frame, text="Historical download controls...").pack()
|
|
|
|
# --- Playback Tab ---
|
|
playback_tab_frame = ttk.Frame(self.function_notebook, padding=10)
|
|
self.function_notebook.add(playback_tab_frame, text="Playback")
|
|
ttk.Label(playback_tab_frame, text="Playback controls...").pack()
|
|
|
|
self.function_notebook.bind("<<NotebookTabChanged>>", self._on_tab_change)
|
|
|
|
# --- Event Handlers ---
|
|
def _on_start_live_monitoring(self):
|
|
if self.controller: self.controller.start_live_monitoring()
|
|
|
|
def _on_stop_live_monitoring(self):
|
|
if self.controller: self.controller.stop_live_monitoring()
|
|
|
|
def _on_profile_selected(self, event=None):
|
|
selected_name = self.selected_profile_var.get()
|
|
if selected_name and self.controller:
|
|
self.controller.load_area_profile(selected_name)
|
|
|
|
def _on_new_profile(self):
|
|
self.selected_profile_var.set("")
|
|
self.update_bbox_gui_fields({})
|
|
|
|
def _on_save_profile(self):
|
|
if self.controller: self.controller.save_current_area_as_profile()
|
|
|
|
def _on_delete_profile(self):
|
|
profile_to_delete = self.selected_profile_var.get()
|
|
if not profile_to_delete or profile_to_delete == DEFAULT_PROFILE_NAME:
|
|
messagebox.showerror("Delete Error", "Cannot delete the default profile or no profile selected.", parent=self.parent_frame)
|
|
return
|
|
if messagebox.askyesno("Confirm Delete", f"Delete profile '{profile_to_delete}'?", parent=self.parent_frame):
|
|
if self.controller: self.controller.delete_area_profile(profile_to_delete)
|
|
|
|
def _on_tab_change(self, event=None):
|
|
if not self.function_notebook: return
|
|
tab_text = self.function_notebook.tab(self.function_notebook.index("current"), "text")
|
|
if self.controller and hasattr(self.controller, "on_function_tab_changed"):
|
|
self.controller.on_function_tab_changed(tab_text)
|
|
|
|
# --- Public Methods ---
|
|
def update_profile_list(self):
|
|
if self.controller:
|
|
profile_names = self.controller.get_profile_names()
|
|
self.profile_combobox["values"] = profile_names
|
|
self.set_selected_profile(self.selected_profile_var.get() or DEFAULT_PROFILE_NAME)
|
|
|
|
def set_selected_profile(self, profile_name: str):
|
|
if profile_name in self.profile_combobox["values"]:
|
|
self.selected_profile_var.set(profile_name)
|
|
elif self.profile_combobox["values"]:
|
|
self.selected_profile_var.set(self.profile_combobox["values"][0])
|
|
|
|
def get_bounding_box_input(self):
|
|
# ... (metodo rimane invariato)
|
|
try:
|
|
bbox_candidate = {
|
|
"lat_min": float(self.lat_min_var.get()), "lon_min": float(self.lon_min_var.get()),
|
|
"lat_max": float(self.lat_max_var.get()), "lon_max": float(self.lon_max_var.get()),
|
|
}
|
|
return bbox_candidate if _is_valid_bbox_dict(bbox_candidate) else None
|
|
except (ValueError, TypeError):
|
|
return None
|
|
|
|
def update_bbox_gui_fields(self, bbox_dict: Dict[str, float]):
|
|
# ... (metodo rimane invariato)
|
|
if bbox_dict and _is_valid_bbox_dict(bbox_dict):
|
|
decimals = getattr(app_config, "COORDINATE_DECIMAL_PLACES", 5)
|
|
self.lat_min_var.set(f"{bbox_dict['lat_min']:.{decimals}f}")
|
|
self.lon_min_var.set(f"{bbox_dict['lon_min']:.{decimals}f}")
|
|
self.lat_max_var.set(f"{bbox_dict['lat_max']:.{decimals}f}")
|
|
self.lon_max_var.set(f"{bbox_dict['lon_max']:.{decimals}f}")
|
|
else:
|
|
self.lat_min_var.set("")
|
|
self.lon_min_var.set("")
|
|
self.lat_max_var.set("")
|
|
self.lon_max_var.set("")
|
|
|
|
def set_monitoring_button_states(self, is_monitoring_active: bool):
|
|
# ... (metodo rimane invariato)
|
|
if self.start_live_button and self.start_live_button.winfo_exists():
|
|
self.start_live_button.config(state=tk.DISABLED if is_monitoring_active else tk.NORMAL)
|
|
if self.stop_live_button and self.stop_live_button.winfo_exists():
|
|
self.stop_live_button.config(state=tk.NORMAL if is_monitoring_active else tk.DISABLED)
|
|
|
|
def set_controls_state(self, enabled: bool):
|
|
state = tk.NORMAL if enabled else tk.DISABLED
|
|
readonly_state = "readonly" if enabled else tk.DISABLED
|
|
|
|
self.profile_combobox.config(state=readonly_state)
|
|
for entry in [self.lat_min_entry, self.lon_min_entry, self.lat_max_entry, self.lon_max_entry]:
|
|
if entry.winfo_exists(): entry.config(state=state)
|
|
|
|
# Abilita/Disabilita i bottoni dei profili
|
|
for child in self.profile_combobox.master.winfo_children():
|
|
if isinstance(child, ttk.Button):
|
|
child.config(state=state) |