# target_simulator/gui/settings_window.py """ A Toplevel window for configuring connection settings (Serial or TFTP). """ import tkinter as tk from tkinter import ttk, messagebox from typing import Dict, Any from target_simulator.core.serial_communicator import SerialCommunicator, SERIAL_PARAMS from target_simulator.core.tftp_communicator import TFTPCommunicator from target_simulator.utils.config_manager import ConfigManager class SettingsWindow(tk.Toplevel): """A dialog window for connection settings.""" def __init__( self, master, config_manager: ConfigManager, current_config: Dict[str, Any] ): super().__init__(master) self.master_view = master self.config_manager = config_manager self.title("Connection Settings") self.transient(master) self.grab_set() self.resizable(False, False) # --- Variables --- self.comm_type_var = tk.StringVar(value=current_config.get("type", "serial")) self.serial_config_vars = self._setup_serial_vars( current_config.get("serial", {}) ) self.tftp_config_vars = self._setup_tftp_vars(current_config.get("tftp", {})) # --- UI Creation --- main_frame = ttk.Frame(self, padding="10") main_frame.pack(fill=tk.BOTH, expand=True) self._create_type_selector(main_frame) # --- Configuration Frames --- self.serial_frame = self._create_serial_frame(main_frame) self.tftp_frame = self._create_tftp_frame(main_frame) self._create_buttons(main_frame) # --- Initial State --- self._on_comm_type_change() self.protocol("WM_DELETE_WINDOW", self.destroy) self.wait_window(self) def _setup_serial_vars(self, config: Dict) -> Dict[str, tk.StringVar]: return { "port": tk.StringVar(value=config.get("port", "")), "baudrate": tk.StringVar(value=config.get("baudrate", "115200")), "bytesize": tk.StringVar(value=config.get("bytesize", "8")), "parity": tk.StringVar(value=config.get("parity", "N")), "stopbits": tk.StringVar(value=config.get("stopbits", "1")), } def _setup_tftp_vars(self, config: Dict) -> Dict[str, tk.StringVar]: return { "ip": tk.StringVar(value=config.get("ip", "127.0.0.1")), "port": tk.StringVar(value=config.get("port", "69")), } def _create_type_selector(self, parent): frame = ttk.LabelFrame(parent, text="Communication Type", padding=10) frame.pack(fill=tk.X, pady=(0, 10)) ttk.Radiobutton( frame, text="Serial", variable=self.comm_type_var, value="serial", command=self._on_comm_type_change, ).pack(side=tk.LEFT, padx=10) ttk.Radiobutton( frame, text="TFTP", variable=self.comm_type_var, value="tftp", command=self._on_comm_type_change, ).pack(side=tk.LEFT, padx=10) def _create_serial_frame(self, parent) -> ttk.Frame: frame = ttk.LabelFrame(parent, text="Serial Port Settings", padding=10) # Grid layout for alignment frame.columnconfigure(1, weight=1) # Port ttk.Label(frame, text="Port:").grid(row=0, column=0, sticky=tk.W, pady=2) port_combo = ttk.Combobox( frame, textvariable=self.serial_config_vars["port"], state="readonly" ) port_combo.grid(row=0, column=1, sticky=tk.EW, padx=5, pady=2) self._populate_ports(port_combo) # Baudrate ttk.Label(frame, text="Baud Rate:").grid(row=1, column=0, sticky=tk.W, pady=2) baud_combo = ttk.Combobox( frame, textvariable=self.serial_config_vars["baudrate"], values=SERIAL_PARAMS["baudrates"], state="readonly", ) baud_combo.grid(row=1, column=1, sticky=tk.EW, padx=5, pady=2) # Parity ttk.Label(frame, text="Parity:").grid(row=2, column=0, sticky=tk.W, pady=2) parity_combo = ttk.Combobox( frame, textvariable=self.serial_config_vars["parity"], values=list(SERIAL_PARAMS["parities"]), state="readonly", ) parity_combo.grid(row=2, column=1, sticky=tk.EW, padx=5, pady=2) return frame def _create_tftp_frame(self, parent) -> ttk.Frame: frame = ttk.LabelFrame(parent, text="TFTP Server Settings", padding=10) frame.columnconfigure(1, weight=1) # IP Address ttk.Label(frame, text="Server IP:").grid(row=0, column=0, sticky=tk.W, pady=2) ttk.Entry(frame, textvariable=self.tftp_config_vars["ip"]).grid( row=0, column=1, sticky=tk.EW, padx=5, pady=2 ) # Port ttk.Label(frame, text="Server Port:").grid(row=1, column=0, sticky=tk.W, pady=2) ttk.Entry(frame, textvariable=self.tftp_config_vars["port"]).grid( row=1, column=1, sticky=tk.EW, padx=5, pady=2 ) return frame def _create_buttons(self, parent): frame = ttk.Frame(parent, padding=(0, 10, 0, 0)) frame.pack(fill=tk.X) ttk.Button( frame, text="Test Connection", command=self._on_test_connection ).pack(side=tk.LEFT, padx=(0, 10)) # Right-align Save and Cancel ttk.Button(frame, text="Cancel", command=self.destroy).pack(side=tk.RIGHT) ttk.Button(frame, text="Save", command=self._on_save).pack( side=tk.RIGHT, padx=5 ) def _on_comm_type_change(self): """Show/hide frames based on the selected communication type.""" selected_type = self.comm_type_var.get() if selected_type == "serial": self.tftp_frame.pack_forget() self.serial_frame.pack(fill=tk.X, expand=True) elif selected_type == "tftp": self.serial_frame.pack_forget() self.tftp_frame.pack(fill=tk.X, expand=True) def _populate_ports(self, combobox: ttk.Combobox): """Fetches and displays available serial ports.""" available_ports = SerialCommunicator.list_available_ports() if available_ports: combobox["values"] = available_ports if not self.serial_config_vars["port"].get(): self.serial_config_vars["port"].set(available_ports[0]) else: combobox["values"] = ["No ports found"] self.serial_config_vars["port"].set("No ports found") def _on_test_connection(self): """Tests the connection for the currently selected mode.""" comm_type = self.comm_type_var.get() is_success = False config = {} if comm_type == "serial": config = {key: var.get() for key, var in self.serial_config_vars.items()} is_success = SerialCommunicator.test_connection(config) else: # tftp config = {key: var.get() for key, var in self.tftp_config_vars.items()} is_success = TFTPCommunicator.test_connection(config) if is_success: messagebox.showinfo( "Success", f"Connection test successful for {comm_type.upper()}.", parent=self, ) else: messagebox.showerror( "Failure", f"Failed to connect using {comm_type.upper()} settings.", parent=self, ) def _on_save(self): """Saves the selected settings and closes the window.""" comm_type = self.comm_type_var.get() # Recupera la configurazione corrente current = ( self.master_view.connection_config if hasattr(self.master_view, "connection_config") else {} ) serial_conf = current.get("serial", {}) tftp_conf = current.get("tftp", {}) if comm_type == "serial": serial_conf = { key: var.get() for key, var in self.serial_config_vars.items() } else: tftp_conf = {key: var.get() for key, var in self.tftp_config_vars.items()} full_config = {"type": comm_type, "serial": serial_conf, "tftp": tftp_conf} self.master_view.update_connection_settings(full_config) self.destroy()