S1005403_RisCC/target_simulator/gui/main_view.py
2025-10-01 14:17:56 +02:00

144 lines
5.8 KiB
Python

# target_simulator/gui/main_view.py
"""
Main view of the application, containing the primary window and widgets.
"""
import tkinter as tk
from tkinter import ttk, scrolledtext
from .ppi_display import PPIDisplay
from .settings_window import SettingsWindow
from .radar_config_window import RadarConfigWindow
from .scenario_controls_frame import ScenarioControlsFrame
from .target_list_frame import TargetListFrame
from .add_target_window import AddTargetWindow
from core.serial_communicator import SerialCommunicator
from core.models import Scenario, Target
from utils.logger import get_logger, shutdown_logging_system
class MainView(tk.Tk):
"""The main application window."""
def __init__(self):
super().__init__()
self.logger = get_logger(__name__)
self.communicator = SerialCommunicator()
self.scenario = Scenario()
self.connection_port: str | None = None
self.scan_limit = 60
self.max_range = 100
self.title("Radar Target Simulator")
self.geometry("1200x900")
self.minsize(900, 700)
self._create_menubar()
self._create_main_layout()
self._create_statusbar()
self.logger.info("MainView initialized successfully.")
def _create_main_layout(self):
"""Creates the main paned layout based on the new design."""
# Main vertical pane: Top (Content) / Bottom (Logs)
v_pane = ttk.PanedWindow(self, orient=tk.VERTICAL)
v_pane.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
# Top horizontal pane: Left (Controls) / Right (PPI)
self.h_pane = ttk.PanedWindow(v_pane, orient=tk.HORIZONTAL)
v_pane.add(self.h_pane, weight=4)
# --- Left Side Frame (for controls) ---
left_frame = ttk.Frame(self.h_pane)
self.h_pane.add(left_frame, weight=1)
# --- Right Side Frame (for PPI) ---
self.ppi_widget = PPIDisplay(self.h_pane, max_range_nm=self.max_range, scan_limit_deg=self.scan_limit)
self.h_pane.add(self.ppi_widget, weight=2)
# --- Populate Left Frame ---
self.scenario_controls = ScenarioControlsFrame(left_frame)
self.scenario_controls.pack(fill=tk.X, expand=False, padx=5, pady=(0, 5))
self.target_list = TargetListFrame(left_frame)
self.target_list.pack(fill=tk.BOTH, expand=True, padx=5)
# Connect button command
self.target_list.add_button.config(command=self._on_add_target)
# --- Logger (Bottom) ---
log_frame_container = ttk.LabelFrame(v_pane, text="Logs")
v_pane.add(log_frame_container, weight=1)
self.log_text_widget = scrolledtext.ScrolledText(
log_frame_container, state=tk.DISABLED, wrap=tk.WORD, font=("Consolas", 9)
)
self.log_text_widget.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
def _create_menubar(self):
# (This method remains unchanged)
menubar = tk.Menu(self)
self.config(menu=menubar)
settings_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="Settings", menu=settings_menu)
settings_menu.add_command(label="Connection...", command=self._open_settings)
settings_menu.add_command(label="Radar Config...", command=self._open_radar_config)
def _create_statusbar(self):
# (This method remains unchanged)
self.status_var = tk.StringVar(value="Status: Disconnected")
status_bar = ttk.Label(self, textvariable=self.status_var, anchor=tk.W, relief=tk.SUNKEN)
status_bar.pack(side=tk.BOTTOM, fill=tk.X)
def _update_all_views(self):
"""Refreshes all GUI components based on the current scenario."""
all_targets = self.scenario.get_all_targets()
self.target_list.update_target_list(all_targets)
self.ppi_widget.update_targets(all_targets)
self.logger.info(f"Views updated. Scenario now has {len(all_targets)} target(s).")
def _on_add_target(self):
"""Handles the 'Add Target' button click."""
self.logger.debug("'Add Target' button clicked. Opening dialog.")
existing_ids = list(self.scenario.targets.keys())
# This will block until the dialog is closed
dialog = AddTargetWindow(self, existing_ids)
new_target = dialog.new_target
if new_target:
self.logger.info(f"New target created with ID {new_target.target_id}.")
self.scenario.add_target(new_target)
self._update_all_views()
else:
self.logger.debug("Add target dialog was cancelled.")
def _open_settings(self):
self.logger.info("Opening settings window.")
SettingsWindow(self, self.communicator)
def _open_radar_config(self):
self.logger.info("Opening radar config window.")
dialog = RadarConfigWindow(self)
if dialog.scan_limit is not None and dialog.max_range is not None:
self.scan_limit = dialog.scan_limit
self.max_range = dialog.max_range
self.logger.info(f"Scan limit set to: ±{self.scan_limit} degrees")
self.logger.info(f"Max range set to: {self.max_range} NM")
self.ppi_widget.destroy()
self.ppi_widget = PPIDisplay(self.h_pane, max_range_nm=self.max_range, scan_limit_deg=self.scan_limit)
self.h_pane.add(self.ppi_widget, weight=2)
self._update_all_views()
def update_connection_settings(self, port: str):
self.connection_port = port
self.logger.info(f"Connection port set to: {self.connection_port}")
self.status_var.set(f"Status: Configured for {port}. Ready to connect.")
def _on_closing(self):
self.logger.info("Application shutting down.")
if self.communicator.is_open:
self.communicator.disconnect()
shutdown_logging_system()
self.destroy()