169 lines
7.0 KiB
Python
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 flightmonitor.utils.logger import get_logger
|
|
from flightmonitor.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 |