SXXXXXXX_FlightMonitor/flightmonitor/gui/panels/log_status_panel.py
2025-06-04 10:14:38 +02:00

169 lines
7.0 KiB
Python

# FlightMonitor/gui/panels/log_status_panel.py
"""
Panel for displaying application status (semaphore and message) and logs.
"""
import tkinter as tk
from tkinter import ttk
from tkinter.scrolledtext import ScrolledText
from tkinter import font as tkFont # For checking font availability
from typing import Dict, Any, Optional
from ...utils.logger import get_logger
from ...utils.gui_utils import GUI_STATUS_UNKNOWN, SEMAPHORE_COLOR_STATUS_MAP
module_logger = get_logger(__name__)
# Constants for semaphore appearance
SEMAPHORE_SIZE = 12
SEMAPHORE_PAD = 3
SEMAPHORE_BORDER_WIDTH = 1
SEMAPHORE_TOTAL_SIZE = SEMAPHORE_SIZE + 2 * (SEMAPHORE_PAD + SEMAPHORE_BORDER_WIDTH)
class LogStatusPanel:
"""
Manages the GUI elements for status display (semaphore, message) and logging.
"""
def __init__(self, parent_frame: ttk.Frame, root_tk_instance: tk.Tk):
"""
Initializes the LogStatusPanel.
Args:
parent_frame: The parent ttk.Frame where this panel will be placed.
root_tk_instance: The main Tkinter root window instance.
"""
self.parent_frame = parent_frame
self.root_tk = root_tk_instance # Needed for background color
self._semaphore_oval_id: Optional[int] = None
# --- Status Bar (Semaphore and Message) ---
# Le righe seguenti creano il frame per la barra di stato, il canvas del semaforo
# e l'etichetta del messaggio di stato. Questa è la prima parte della UI gestita da questo panel.
self.status_bar_frame = ttk.Frame(self.parent_frame, padding=(5, 3))
self.status_bar_frame.pack(side=tk.TOP, fill=tk.X, pady=(0, 5))
self.semaphore_canvas = tk.Canvas(
self.status_bar_frame,
width=SEMAPHORE_TOTAL_SIZE,
height=SEMAPHORE_TOTAL_SIZE,
bg=self.root_tk.cget("bg"), # Match root background
highlightthickness=0,
)
self.semaphore_canvas.pack(side=tk.LEFT, padx=(0, 5))
self._create_semaphore_oval() # Chiama il metodo helper per creare il cerchio del semaforo
self.status_label = ttk.Label(
self.status_bar_frame, text="Status: Initializing..."
)
self.status_label.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 2))
# --- Log Area ---
# Le righe seguenti creano il frame per l'area di log e il widget di testo scrollabile.
# Questa è la seconda parte della UI gestita da questo panel.
self.log_frame = ttk.Frame(self.parent_frame, padding=(5, 0, 5, 5))
self.log_frame.pack(side=tk.TOP, fill=tk.BOTH, expand=True, padx=0, pady=0)
# Determina il font per il widget di log. Preferisce "Consolas" se disponibile, altrimenti "Courier New".
log_font_family = (
"Consolas" if "Consolas" in tkFont.families() else "Courier New"
)
self.log_text_widget = ScrolledText(
self.log_frame,
state=tk.DISABLED, # Inizialmente disabilitato per prevenire input diretto dell'utente
height=10, # Altezza di default, la dimensione finale sarà gestita dalla PanedWindow
wrap=tk.WORD, # Va a capo le parole
font=(log_font_family, 9),
relief=tk.SUNKEN, # Effetto visivo "incassato"
borderwidth=1,
)
self.log_text_widget.pack(fill=tk.BOTH, expand=True, padx=0, pady=0)
module_logger.debug("LogStatusPanel initialized.")
def _create_semaphore_oval(self):
"""
Private helper method to create the oval shape for the status semaphore.
Called during initialization of the panel.
"""
# Verifica che il canvas esista e che non sia già stato creato l'ID del semaforo per evitare errori.
if not (
self.semaphore_canvas
and self.semaphore_canvas.winfo_exists()
and hasattr(self, "_semaphore_oval_id")
):
module_logger.warning(
"Semaphore canvas not ready or attribute missing, cannot create oval."
)
return
# Calcola le coordinate per il cerchio del semaforo, includendo padding e bordo.
x0 = SEMAPHORE_PAD + SEMAPHORE_BORDER_WIDTH
y0 = SEMAPHORE_PAD + SEMAPHORE_BORDER_WIDTH
x1 = x0 + SEMAPHORE_SIZE
y1 = y0 + SEMAPHORE_SIZE
# Ottiene il colore iniziale dal dizionario di mappatura degli stati o un colore di default.
initial_color = SEMAPHORE_COLOR_STATUS_MAP.get(
GUI_STATUS_UNKNOWN, "gray70"
)
try:
# Crea l'elemento ovale sul canvas e ne memorizza l'ID.
self._semaphore_oval_id = self.semaphore_canvas.create_oval(
x0,
y0,
x1,
y1,
fill=initial_color,
outline="gray30",
width=SEMAPHORE_BORDER_WIDTH,
)
except tk.TclError as e:
# Logga un errore se la creazione fallisce (es. widget già distrutto).
module_logger.error(f"Error creating semaphore oval: {e}")
def update_status_display(self, status_level: str, message: str):
"""
Updates the semaphore color and status message displayed in the GUI.
Args:
status_level: The new status level (e.g., GUI_STATUS_OK, GUI_STATUS_ERROR).
Used to determine the semaphore color.
message: The new status message string to display next to the semaphore.
"""
# Ottiene il colore corrispondente al livello di stato. Se non trovato, usa un colore di default.
color_to_set = SEMAPHORE_COLOR_STATUS_MAP.get(
status_level, SEMAPHORE_COLOR_STATUS_MAP.get(GUI_STATUS_UNKNOWN, "gray60")
)
# Aggiorna il colore del semaforo sul canvas, se il canvas esiste.
if (
self.semaphore_canvas
and self.semaphore_canvas.winfo_exists()
and self._semaphore_oval_id is not None
):
try:
self.semaphore_canvas.itemconfig(
self._semaphore_oval_id, fill=color_to_set
)
except tk.TclError: # Cattura TclError che può accadere durante la chiusura dell'app
pass
# Aggiorna il testo dell'etichetta di stato, se l'etichetta esiste.
current_status_text = f"Status: {message}"
if self.status_label and self.status_label.winfo_exists():
try:
self.status_label.config(text=current_status_text)
except tk.TclError: # Cattura TclError che può accadere durante la chiusura dell'app
pass
def get_log_widget(self) -> ScrolledText:
"""
Returns the Tkinter ScrolledText widget used for displaying logs.
This method is essential as it allows the external logger setup
(from `FlightMonitor.utils.logger`) to attach itself to this specific widget.
Returns:
The `tkinter.scrolledtext.ScrolledText` widget.
"""
return self.log_text_widget