add info to session pannel during monitoring
This commit is contained in:
parent
9851a5bd0b
commit
d60122e201
@ -296,6 +296,16 @@ class AppController:
|
||||
self.is_live_monitoring_active = True
|
||||
self.live_adapter_thread.start()
|
||||
self.live_data_processor.start_processing_queue()
|
||||
|
||||
if (
|
||||
self.main_window
|
||||
and hasattr(self.main_window.function_notebook_panel, "data_logging_panel")
|
||||
and self.main_window.function_notebook_panel.data_logging_panel
|
||||
):
|
||||
self.main_window.function_notebook_panel.data_logging_panel.session_started(
|
||||
start_timestamp=time.time(),
|
||||
query_rate_sec=app_config.LIVE_POLLING_INTERVAL_SECONDS
|
||||
)
|
||||
|
||||
if self.aircraft_db_manager and self._active_bounding_box:
|
||||
session_params = {
|
||||
@ -321,6 +331,14 @@ class AppController:
|
||||
module_logger.info(
|
||||
f"Controller: Stopping live monitoring (from_error={from_error})."
|
||||
)
|
||||
|
||||
if (
|
||||
self.main_window
|
||||
and hasattr(self.main_window.function_notebook_panel, "data_logging_panel")
|
||||
and self.main_window.function_notebook_panel.data_logging_panel
|
||||
):
|
||||
self.main_window.function_notebook_panel.data_logging_panel.session_stopped()
|
||||
|
||||
self.is_live_monitoring_active = False
|
||||
|
||||
if (
|
||||
@ -380,6 +398,18 @@ class AppController:
|
||||
if panel:
|
||||
panel.add_summary_entry(timestamp, aircraft_count)
|
||||
panel.update_last_query_result(timestamp, aircraft_count)
|
||||
|
||||
# This is now the central point for incrementing the query count for any active session
|
||||
self.increment_session_query_count()
|
||||
|
||||
def increment_session_query_count(self):
|
||||
"""Notifies the DataLoggingPanel to increment its session query counter."""
|
||||
if (
|
||||
self.main_window
|
||||
and hasattr(self.main_window.function_notebook_panel, "data_logging_panel")
|
||||
and self.main_window.function_notebook_panel.data_logging_panel
|
||||
):
|
||||
self.main_window.function_notebook_panel.data_logging_panel.increment_query_count()
|
||||
|
||||
def open_log_directory(self, directory_path: str):
|
||||
if not directory_path:
|
||||
@ -495,6 +525,17 @@ class AppController:
|
||||
)
|
||||
self.historical_adapter_thread.start()
|
||||
self.historical_data_processor.start_processing()
|
||||
|
||||
if (
|
||||
self.main_window
|
||||
and hasattr(self.main_window.function_notebook_panel, "data_logging_panel")
|
||||
and self.main_window.function_notebook_panel.data_logging_panel
|
||||
):
|
||||
self.main_window.function_notebook_panel.data_logging_panel.session_started(
|
||||
start_timestamp=params["start_time"],
|
||||
query_rate_sec=params["sampling_interval_sec"]
|
||||
)
|
||||
|
||||
self.main_window.update_semaphore_and_status(
|
||||
GUI_STATUS_FETCHING, "Historical download started."
|
||||
)
|
||||
@ -523,6 +564,13 @@ class AppController:
|
||||
self.aircraft_db_manager.add_scan_history(
|
||||
self._current_scan_params, self._active_bounding_box, status
|
||||
)
|
||||
|
||||
if (
|
||||
self.main_window
|
||||
and hasattr(self.main_window.function_notebook_panel, "data_logging_panel")
|
||||
and self.main_window.function_notebook_panel.data_logging_panel
|
||||
):
|
||||
self.main_window.function_notebook_panel.data_logging_panel.session_stopped()
|
||||
|
||||
self.is_historical_download_active = False
|
||||
self._current_scan_params = None
|
||||
|
||||
@ -6,7 +6,8 @@ This panel is designed to be shared across different monitoring modes.
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, filedialog, font as tkFont
|
||||
from typing import Optional, Any
|
||||
from datetime import datetime, timezone
|
||||
from datetime import datetime, timezone, timedelta
|
||||
import time
|
||||
|
||||
from flightmonitor.utils.logger import get_logger
|
||||
|
||||
@ -29,17 +30,22 @@ class DataLoggingPanel:
|
||||
self.parent_frame = parent_frame
|
||||
self.controller = controller
|
||||
|
||||
# --- Internal State ---
|
||||
self.session_start_time: Optional[float] = None
|
||||
self.session_query_count: int = 0
|
||||
self.session_query_rate_sec: Optional[int] = None
|
||||
self._session_update_after_id: Optional[str] = None
|
||||
|
||||
# --- Tkinter Variables ---
|
||||
self.enable_logging_var = tk.BooleanVar(value=False)
|
||||
self.log_directory_var = tk.StringVar(value="")
|
||||
self.last_query_result_var = tk.StringVar(value="Last query result: N/A")
|
||||
self.session_info_var = tk.StringVar(value="Session not active.")
|
||||
|
||||
# --- Widget References ---
|
||||
self.log_dir_entry: Optional[ttk.Entry] = None
|
||||
self.browse_button: Optional[ttk.Button] = None
|
||||
self.open_folder_button: Optional[ttk.Button] = (
|
||||
None # MODIFICATO: Aggiunto riferimento
|
||||
)
|
||||
self.open_folder_button: Optional[ttk.Button] = None
|
||||
self.summary_table: Optional[ttk.Treeview] = None
|
||||
|
||||
self._build_ui()
|
||||
@ -72,7 +78,6 @@ class DataLoggingPanel:
|
||||
)
|
||||
self.log_dir_entry.grid(row=0, column=1, sticky=tk.EW)
|
||||
|
||||
# MODIFICATO: Aggiunto il nuovo pulsante "Open Folder"
|
||||
self.browse_button = ttk.Button(
|
||||
dir_frame,
|
||||
text="Browse...",
|
||||
@ -88,16 +93,23 @@ class DataLoggingPanel:
|
||||
state=tk.DISABLED,
|
||||
)
|
||||
self.open_folder_button.grid(row=0, column=3, sticky=tk.E, padx=(2, 0))
|
||||
# --- FINE MODIFICA ---
|
||||
|
||||
last_result_font = tkFont.Font(family="Helvetica", size=9, slant="italic")
|
||||
info_font = tkFont.Font(family="Helvetica", size=9, slant="italic")
|
||||
last_result_label = ttk.Label(
|
||||
container,
|
||||
textvariable=self.last_query_result_var,
|
||||
font=last_result_font,
|
||||
font=info_font,
|
||||
foreground="navy",
|
||||
)
|
||||
last_result_label.pack(anchor=tk.W, pady=(8, 2))
|
||||
last_result_label.pack(anchor=tk.W, pady=(8, 0))
|
||||
|
||||
session_info_label = ttk.Label(
|
||||
container,
|
||||
textvariable=self.session_info_var,
|
||||
font=info_font,
|
||||
foreground="darkgreen",
|
||||
)
|
||||
session_info_label.pack(anchor=tk.W, pady=(0, 2))
|
||||
|
||||
table_frame = ttk.Frame(container)
|
||||
table_frame.pack(fill=tk.BOTH, expand=True, pady=(2, 0))
|
||||
@ -125,12 +137,8 @@ class DataLoggingPanel:
|
||||
self.log_dir_entry.config(state=new_state)
|
||||
if self.browse_button:
|
||||
self.browse_button.config(state=new_state)
|
||||
# MODIFICATO: Abilita/disabilita anche il nuovo pulsante
|
||||
if self.open_folder_button:
|
||||
self.open_folder_button.config(state=new_state)
|
||||
module_logger.info(
|
||||
f"Raw data logging has been {'enabled' if is_enabled else 'disabled'} by user."
|
||||
)
|
||||
|
||||
def _on_browse_directory(self):
|
||||
directory = filedialog.askdirectory(
|
||||
@ -138,18 +146,62 @@ class DataLoggingPanel:
|
||||
)
|
||||
if directory:
|
||||
self.log_directory_var.set(directory)
|
||||
module_logger.info(f"User selected new log directory: {directory}")
|
||||
|
||||
# MODIFICATO: Aggiunto il metodo callback per "Open Folder"
|
||||
def _on_open_folder(self):
|
||||
"""Callback for the 'Open Folder' button."""
|
||||
directory_path = self.get_log_directory()
|
||||
if self.controller and hasattr(self.controller, "open_log_directory"):
|
||||
self.controller.open_log_directory(directory_path)
|
||||
else:
|
||||
module_logger.error(
|
||||
"Controller or 'open_log_directory' method not available."
|
||||
)
|
||||
|
||||
def session_started(self, start_timestamp: float, query_rate_sec: Optional[int]):
|
||||
"""To be called by the controller when a monitoring/download session starts."""
|
||||
self.session_start_time = start_timestamp
|
||||
self.session_query_count = 0
|
||||
self.session_query_rate_sec = query_rate_sec
|
||||
|
||||
if self._session_update_after_id:
|
||||
self.parent_frame.after_cancel(self._session_update_after_id)
|
||||
|
||||
self._update_session_duration()
|
||||
module_logger.info(f"DataLoggingPanel session timer started at {start_timestamp}.")
|
||||
|
||||
def session_stopped(self):
|
||||
"""To be called by the controller when a session stops."""
|
||||
if self._session_update_after_id:
|
||||
self.parent_frame.after_cancel(self._session_update_after_id)
|
||||
self._session_update_after_id = None
|
||||
|
||||
self.session_start_time = None
|
||||
self.session_query_count = 0
|
||||
self.session_query_rate_sec = None
|
||||
self.session_info_var.set("Session not active.")
|
||||
module_logger.info("DataLoggingPanel session timer stopped.")
|
||||
|
||||
def _update_session_duration(self):
|
||||
"""Periodically updates the session duration label."""
|
||||
if self.session_start_time is None:
|
||||
return
|
||||
|
||||
elapsed_seconds = time.time() - self.session_start_time
|
||||
duration_str = str(timedelta(seconds=int(elapsed_seconds)))
|
||||
start_time_str = datetime.fromtimestamp(self.session_start_time, timezone.utc).strftime('%H:%M:%S')
|
||||
|
||||
rate_str = ""
|
||||
if self.session_query_rate_sec is not None and self.session_query_rate_sec > 0:
|
||||
rate_str = f" (1 per {self.session_query_rate_sec:.0f}s)"
|
||||
|
||||
session_text = (
|
||||
f"Session started: {start_time_str} (UTC) | "
|
||||
f"Duration: {duration_str} | "
|
||||
f"Queries: {self.session_query_count}{rate_str}"
|
||||
)
|
||||
self.session_info_var.set(session_text)
|
||||
|
||||
self._session_update_after_id = self.parent_frame.after(1000, self._update_session_duration)
|
||||
|
||||
def increment_query_count(self):
|
||||
"""Increments the session query counter."""
|
||||
self.session_query_count += 1
|
||||
# The label will be updated by the next scheduled _update_session_duration call
|
||||
|
||||
def is_logging_enabled(self) -> bool:
|
||||
return self.enable_logging_var.get()
|
||||
@ -160,17 +212,12 @@ class DataLoggingPanel:
|
||||
def add_summary_entry(self, timestamp: float, count: int):
|
||||
if not self.summary_table or not self.summary_table.winfo_exists():
|
||||
return
|
||||
|
||||
try:
|
||||
dt_object = datetime.fromtimestamp(timestamp, timezone.utc)
|
||||
time_str = dt_object.strftime("%Y-%m-%d %H:%M:%S")
|
||||
self.summary_table.insert("", 0, values=(time_str, count))
|
||||
except (ValueError, TypeError) as e:
|
||||
module_logger.error(f"Failed to add summary entry to table: {e}")
|
||||
except tk.TclError:
|
||||
module_logger.warning(
|
||||
"TclError adding summary to table, widget likely destroyed."
|
||||
)
|
||||
except (ValueError, TypeError, tk.TclError):
|
||||
pass
|
||||
|
||||
def update_last_query_result(self, timestamp: float, count: int):
|
||||
try:
|
||||
@ -178,30 +225,23 @@ class DataLoggingPanel:
|
||||
time_str = dt_object.strftime("%H:%M:%S")
|
||||
result_text = f"Last query result: {count} aircraft at {time_str} UTC"
|
||||
self.last_query_result_var.set(result_text)
|
||||
except (ValueError, TypeError):
|
||||
self.last_query_result_var.set("Last query result: Invalid timestamp")
|
||||
except tk.TclError:
|
||||
module_logger.warning(
|
||||
"TclError updating last query label, widget likely destroyed."
|
||||
)
|
||||
except (ValueError, TypeError, tk.TclError):
|
||||
pass
|
||||
|
||||
def clear_summary_table(self):
|
||||
if not self.summary_table or not self.summary_table.winfo_exists():
|
||||
return
|
||||
|
||||
self.last_query_result_var.set("Last query result: N/A")
|
||||
self.session_stopped()
|
||||
|
||||
try:
|
||||
for item in self.summary_table.get_children():
|
||||
self.summary_table.delete(item)
|
||||
module_logger.info("Data logging summary table cleared.")
|
||||
except tk.TclError:
|
||||
module_logger.warning(
|
||||
"TclError clearing summary table, widget likely destroyed."
|
||||
)
|
||||
pass
|
||||
|
||||
def update_settings(self, enabled: bool, directory: str):
|
||||
self.enable_logging_var.set(enabled)
|
||||
self.log_directory_var.set(directory)
|
||||
self._on_toggle_logging()
|
||||
module_logger.debug(
|
||||
f"DataLoggingPanel settings updated: enabled={enabled}, dir='{directory}'"
|
||||
)
|
||||
self._on_toggle_logging()
|
||||
66
test_overlay.py
Normal file
66
test_overlay.py
Normal file
@ -0,0 +1,66 @@
|
||||
import tkinter as tk
|
||||
from PIL import Image, ImageDraw, ImageTk
|
||||
|
||||
class FlightSimulatorApp:
|
||||
def __init__(self, root, map_path):
|
||||
self.root = root
|
||||
self.root.title("Simulatore Tracce Aeree")
|
||||
|
||||
# Carica mappa da file
|
||||
original_map = Image.open(map_path).convert("RGB")
|
||||
self.width, self.height = original_map.size
|
||||
|
||||
# Tkinter canvas
|
||||
self.canvas = tk.Canvas(root, width=self.width, height=self.height)
|
||||
self.canvas.pack()
|
||||
|
||||
# Mappa di sfondo
|
||||
self.base_map = original_map
|
||||
|
||||
# Lista aerei
|
||||
self.aircrafts = [
|
||||
{"x": 100, "y": 100, "vx": 2, "vy": 1},
|
||||
{"x": 200, "y": 250, "vx": 1, "vy": -1},
|
||||
{"x": 300, "y": 400, "vx": -1, "vy": 2},
|
||||
]
|
||||
self.tracks = [[] for _ in self.aircrafts]
|
||||
|
||||
# Avvio animazione
|
||||
self.update_frame()
|
||||
|
||||
def update_frame(self):
|
||||
# Copia mappa
|
||||
frame = self.base_map.copy()
|
||||
draw = ImageDraw.Draw(frame)
|
||||
|
||||
# Disegna aerei e tracce
|
||||
for i, aircraft in enumerate(self.aircrafts):
|
||||
aircraft["x"] += aircraft["vx"]
|
||||
aircraft["y"] += aircraft["vy"]
|
||||
|
||||
# Salva traccia
|
||||
self.tracks[i].append((aircraft["x"], aircraft["y"]))
|
||||
|
||||
# Traccia gialla
|
||||
if len(self.tracks[i]) > 1:
|
||||
draw.line(self.tracks[i], fill='yellow', width=2)
|
||||
|
||||
# Aereo rosso
|
||||
x, y = aircraft["x"], aircraft["y"]
|
||||
draw.ellipse((x-5, y-5, x+5, y+5), fill='red')
|
||||
|
||||
# Aggiorna immagine Tkinter
|
||||
self.tk_image = ImageTk.PhotoImage(frame)
|
||||
self.canvas.create_image(0, 0, anchor='nw', image=self.tk_image)
|
||||
|
||||
# Frame successivo
|
||||
self.root.after(100, self.update_frame)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Percorso immagine di sfondo (mappa)
|
||||
mappa_path = "mappa.png" # ← Cambia con il nome del tuo file
|
||||
|
||||
root = tk.Tk()
|
||||
app = FlightSimulatorApp(root, mappa_path)
|
||||
root.mainloop()
|
||||
Loading…
Reference in New Issue
Block a user