add new icon, refactor live tab
This commit is contained in:
parent
6fa4928b0c
commit
5ff1686112
BIN
flightmonitor/assets/icons/play_icon.png
Normal file
BIN
flightmonitor/assets/icons/play_icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
BIN
flightmonitor/assets/icons/stop_icon.png
Normal file
BIN
flightmonitor/assets/icons/stop_icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.0 KiB |
@ -18,7 +18,7 @@ from .panels.map_tools_panel import MapToolsPanel
|
||||
from .panels.map_info_panel import MapInfoPanel
|
||||
from .panels.selected_flight_details_panel import SelectedFlightDetailsPanel
|
||||
from .panels.views_notebook_panel import ViewsNotebookPanel
|
||||
from .panels.function_notebook_panel import FunctionNotebookPanel
|
||||
from .panels.function_notebook_panel import FunctionNotebookPanel, DEFAULT_PROFILE_NAME
|
||||
|
||||
try:
|
||||
from ..map.map_canvas_manager import MapCanvasManager
|
||||
@ -211,6 +211,10 @@ class MainWindow:
|
||||
else:
|
||||
self._update_map_placeholder("Map functionality disabled (Import Error).")
|
||||
|
||||
# MODIFICA: Invochiamo il caricamento del profilo di default da qui,
|
||||
# dopo che la mappa è stata programmata per l'inizializzazione.
|
||||
self.root.after(250, lambda: self.controller.load_area_profile(DEFAULT_PROFILE_NAME))
|
||||
|
||||
self.function_notebook_panel._on_tab_change()
|
||||
module_logger.info("MainWindow fully initialized.")
|
||||
|
||||
|
||||
@ -3,8 +3,15 @@
|
||||
Panel managing the area profiles, BBox input, and the function notebooks.
|
||||
"""
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, messagebox
|
||||
from tkinter import ttk, messagebox, font as tkFont
|
||||
from typing import Dict, Any, Optional
|
||||
import os
|
||||
|
||||
try:
|
||||
from PIL import Image, ImageTk
|
||||
PIL_AVAILABLE = True
|
||||
except ImportError:
|
||||
PIL_AVAILABLE = False
|
||||
|
||||
from ...utils.logger import get_logger
|
||||
from ...data import config as app_config
|
||||
@ -13,6 +20,19 @@ from ...data.area_profile_manager import DEFAULT_PROFILE_NAME
|
||||
|
||||
module_logger = get_logger(__name__)
|
||||
|
||||
ICON_PATH = os.path.join(os.path.dirname(__file__), '..', '..', 'assets', 'icons')
|
||||
PLAY_ICON_PATH = os.path.join(ICON_PATH, 'play_icon.png')
|
||||
STOP_ICON_PATH = os.path.join(ICON_PATH, 'stop_icon.png')
|
||||
|
||||
# Definiamo i colori per i bottoni
|
||||
COLOR_START_BG = "#28a745" # Verde
|
||||
COLOR_START_ACTIVE = "#218838"
|
||||
COLOR_STOP_BG = "#dc3545" # Rosso
|
||||
COLOR_STOP_ACTIVE = "#c82333"
|
||||
COLOR_DISABLED_BG = "#e0e0e0" # Grigio chiaro
|
||||
COLOR_DISABLED_FG = "#a0a0a0" # Grigio scuro per il testo
|
||||
COLOR_TEXT = "black"
|
||||
|
||||
class FunctionNotebookPanel:
|
||||
"""
|
||||
Manages the combined Area Profiles, BBox, and Function Notebooks panel.
|
||||
@ -21,7 +41,10 @@ class FunctionNotebookPanel:
|
||||
self.parent_frame = parent_frame
|
||||
self.controller = controller
|
||||
|
||||
# --- Tkinter Variables ---
|
||||
self.play_icon: Optional[tk.PhotoImage] = None
|
||||
self.stop_icon: Optional[tk.PhotoImage] = None
|
||||
|
||||
# ... (il resto del costruttore rimane uguale) ...
|
||||
self.lat_min_var = tk.StringVar()
|
||||
self.lon_min_var = tk.StringVar()
|
||||
self.lat_max_var = tk.StringVar()
|
||||
@ -30,51 +53,62 @@ class FunctionNotebookPanel:
|
||||
|
||||
self._build_ui()
|
||||
self.update_profile_list()
|
||||
|
||||
# Seleziona il valore nella combobox, ma non carica ancora i dati
|
||||
self.set_selected_profile(DEFAULT_PROFILE_NAME)
|
||||
|
||||
# RIMOSSA: La chiamata a _on_profile_selected() viene ora gestita da MainWindow
|
||||
|
||||
module_logger.debug("FunctionNotebookPanel (unified) initialized.")
|
||||
|
||||
def _load_icons(self):
|
||||
if not PIL_AVAILABLE: return
|
||||
try:
|
||||
icon_size = (32, 32) # Icone più grandi
|
||||
play_img = Image.open(PLAY_ICON_PATH).resize(icon_size, Image.Resampling.LANCZOS)
|
||||
self.play_icon = ImageTk.PhotoImage(play_img)
|
||||
|
||||
stop_img = Image.open(STOP_ICON_PATH).resize(icon_size, Image.Resampling.LANCZOS)
|
||||
self.stop_icon = ImageTk.PhotoImage(stop_img)
|
||||
except Exception as e:
|
||||
module_logger.warning(f"Could not load icons: {e}. Buttons will be text-only.")
|
||||
|
||||
def _build_ui(self):
|
||||
self._load_icons()
|
||||
button_font = tkFont.Font(family="Helvetica", size=10, weight="bold")
|
||||
|
||||
# --- 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)
|
||||
# MODIFICA: Assegna lo stesso peso anche alla quarta colonna
|
||||
area_frame.columnconfigure(3, 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"
|
||||
)
|
||||
profile_controls_frame.columnconfigure(0, weight=1)
|
||||
|
||||
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)
|
||||
@ -84,44 +118,81 @@ class FunctionNotebookPanel:
|
||||
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)
|
||||
live_tab_frame = ttk.Frame(self.function_notebook, padding=(0, 10, 0, 0)) # Aggiunto padding
|
||||
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 ---
|
||||
# Usiamo pack per un controllo migliore sull'allineamento verticale
|
||||
button_container = ttk.Frame(live_tab_frame)
|
||||
button_container.pack(side=tk.TOP, fill=tk.X, expand=False, anchor='n')
|
||||
|
||||
# Frame interni per centrare i bottoni orizzontalmente
|
||||
left_spacer = ttk.Frame(button_container)
|
||||
left_spacer.pack(side=tk.LEFT, expand=True)
|
||||
|
||||
self.start_live_button = tk.Button(
|
||||
button_container,
|
||||
text="Start Live",
|
||||
image=self.play_icon,
|
||||
compound=tk.TOP,
|
||||
command=self._on_start_live_monitoring,
|
||||
font=button_font,
|
||||
bg=COLOR_START_BG,
|
||||
fg=COLOR_TEXT,
|
||||
activebackground=COLOR_START_ACTIVE,
|
||||
activeforeground=COLOR_TEXT,
|
||||
relief=tk.RAISED,
|
||||
borderwidth=2,
|
||||
padx=10,
|
||||
pady=5
|
||||
)
|
||||
self.start_live_button.pack(side=tk.LEFT, padx=15, pady=5)
|
||||
|
||||
self.stop_live_button = tk.Button(
|
||||
button_container,
|
||||
text="Stop Live",
|
||||
image=self.stop_icon,
|
||||
compound=tk.TOP,
|
||||
command=self._on_stop_live_monitoring,
|
||||
font=button_font,
|
||||
bg=COLOR_DISABLED_BG,
|
||||
fg=COLOR_DISABLED_FG,
|
||||
activebackground=COLOR_DISABLED_BG,
|
||||
activeforeground=COLOR_DISABLED_FG,
|
||||
state=tk.DISABLED,
|
||||
relief=tk.RAISED,
|
||||
borderwidth=2,
|
||||
padx=10,
|
||||
pady=5
|
||||
)
|
||||
self.stop_live_button.pack(side=tk.LEFT, padx=15, pady=5)
|
||||
|
||||
right_spacer = ttk.Frame(button_container)
|
||||
right_spacer.pack(side=tk.LEFT, expand=True)
|
||||
|
||||
# --- Altri Tab (invariati) ---
|
||||
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 ---
|
||||
# --- Event Handlers (invariati) ---
|
||||
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)
|
||||
|
||||
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:
|
||||
@ -129,14 +200,40 @@ class FunctionNotebookPanel:
|
||||
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)
|
||||
if self.function_notebook:
|
||||
tab_text = self.function_notebook.tab(self.function_notebook.index("current"), "text")
|
||||
if self.controller: self.controller.on_function_tab_changed(tab_text)
|
||||
|
||||
# --- Public Methods ---
|
||||
# --- Public Methods (modificata set_monitoring_button_states) ---
|
||||
def set_monitoring_button_states(self, is_monitoring_active: bool):
|
||||
"""Sets the state and style of the Start/Stop buttons."""
|
||||
if is_monitoring_active:
|
||||
# Monitoring ATTIVO
|
||||
if self.start_live_button:
|
||||
self.start_live_button.config(
|
||||
state=tk.DISABLED, bg=COLOR_DISABLED_BG, fg=COLOR_DISABLED_FG,
|
||||
activebackground=COLOR_DISABLED_BG, activeforeground=COLOR_DISABLED_FG
|
||||
)
|
||||
if self.stop_live_button:
|
||||
self.stop_live_button.config(
|
||||
state=tk.NORMAL, bg=COLOR_STOP_BG, fg=COLOR_TEXT,
|
||||
activebackground=COLOR_STOP_ACTIVE, activeforeground=COLOR_TEXT
|
||||
)
|
||||
else:
|
||||
# Monitoring FERMO
|
||||
if self.start_live_button:
|
||||
self.start_live_button.config(
|
||||
state=tk.NORMAL, bg=COLOR_START_BG, fg=COLOR_TEXT,
|
||||
activebackground=COLOR_START_ACTIVE, activeforeground=COLOR_TEXT
|
||||
)
|
||||
if self.stop_live_button:
|
||||
self.stop_live_button.config(
|
||||
state=tk.DISABLED, bg=COLOR_DISABLED_BG, fg=COLOR_DISABLED_FG,
|
||||
activebackground=COLOR_DISABLED_BG, activeforeground=COLOR_DISABLED_FG
|
||||
)
|
||||
|
||||
# --- Altri metodi pubblici (invariati) ---
|
||||
def update_profile_list(self):
|
||||
if self.controller:
|
||||
profile_names = self.controller.get_profile_names()
|
||||
@ -144,24 +241,21 @@ class FunctionNotebookPanel:
|
||||
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"]:
|
||||
if self.profile_combobox["values"] and 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
|
||||
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}")
|
||||
@ -169,27 +263,14 @@ class FunctionNotebookPanel:
|
||||
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("")
|
||||
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)
|
||||
Loading…
Reference in New Issue
Block a user