From 08aa827cd792c0e6e38a61789f573fe2535ba90b Mon Sep 17 00:00:00 2001 From: VALLONGOL Date: Thu, 2 Oct 2025 15:19:18 +0200 Subject: [PATCH] fix gui and button fuction --- target_simulator/__main__.py | 11 ++-- target_simulator/gui/main_view.py | 9 ++- target_simulator/gui/ppi_display.py | 68 +++++++++++++++-------- target_simulator/gui/target_list_frame.py | 12 +++- 4 files changed, 68 insertions(+), 32 deletions(-) diff --git a/target_simulator/__main__.py b/target_simulator/__main__.py index 76e27ce..fc2448f 100644 --- a/target_simulator/__main__.py +++ b/target_simulator/__main__.py @@ -23,17 +23,20 @@ from config import LOGGING_CONFIG def main(): """Initializes and runs the application.""" + import logging + logging.getLogger('matplotlib').setLevel(logging.WARNING) + app = MainView() - + # Setup the global logging system, connecting it to the GUI's main loop setup_basic_logging(app, LOGGING_CONFIG) - + # Now that the logging system is active, add the handler for the GUI widget add_tkinter_handler(app.log_text_widget, LOGGING_CONFIG) - + logger = get_logger(__name__) logger.info("Application starting up.") - + app.mainloop() diff --git a/target_simulator/gui/main_view.py b/target_simulator/gui/main_view.py index fe33573..fb297ab 100644 --- a/target_simulator/gui/main_view.py +++ b/target_simulator/gui/main_view.py @@ -77,14 +77,19 @@ class MainView(tk.Tk): self.scenario_controls = ScenarioControlsFrame(left_frame, load_scenario_command=self._on_load_scenario, save_as_command=self._on_save_scenario_as, update_command=self._on_update_scenario, delete_command=self._on_delete_scenario) self.scenario_controls.pack(fill=tk.X, expand=False, padx=5, pady=(5, 5)) - self.target_list = TargetListFrame(left_frame) + self.target_list = TargetListFrame(left_frame, targets_changed_callback=self._on_targets_changed) self.target_list.pack(fill=tk.BOTH, expand=True, padx=5) - # I bottoni Add/Remove/Edit sono gestiti dal frame stesso + + # --- Logs Frame --- 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 _on_targets_changed(self, targets): + # Called by TargetListFrame when targets are changed + self.ppi_widget.update_targets(targets) + def _draw_conn_status_indicator(self, connected: bool): self.conn_status_canvas.delete("all") color = "#2ecc40" if connected else "#e74c3c" # green or red diff --git a/target_simulator/gui/ppi_display.py b/target_simulator/gui/ppi_display.py index dd8e289..5e59ef8 100644 --- a/target_simulator/gui/ppi_display.py +++ b/target_simulator/gui/ppi_display.py @@ -108,52 +108,72 @@ class PPIDisplay(ttk.Frame): def update_targets(self, targets: List[Target]): """ - Updates the positions of targets on the PPI display. - - Args: - targets: A list of Target objects to display. + Aggiorna la posizione dei target sulla PPI e mostra l'ID accanto al punto. Tooltip con ID al passaggio del mouse. """ - # If an empty list is passed, it means we just want to redraw existing targets - # (e.g., after a zoom change), not clear them. if targets: self.active_targets = [t for t in targets if t.active] - # Clear previously drawn targets + # Rimuovi artisti precedenti for artist in self.target_artists: artist.remove() self.target_artists.clear() - vector_len = self.range_var.get() / 25 # Length of heading vector relative to current view + # Rimuovi eventuali tooltips precedenti + if hasattr(self, 'tooltips'): + for tip in self.tooltips: + tip.remove() + self.tooltips = [] + + vector_len = self.range_var.get() / 25 + self._target_dots = [] for target in getattr(self, 'active_targets', []): - # Target position in polar r1 = target.range_nm th1 = np.deg2rad(target.azimuth_deg) - - # Plot the target's center dot - dot, = self.ax.plot(th1, r1, 'o', markersize=5, color='red') + # Punto del target + dot, = self.ax.plot(th1, r1, 'o', markersize=5, color='red', picker=5) self.target_artists.append(dot) + self._target_dots.append((dot, target)) - # --- Calculate and plot heading vector --- - # Convert target polar to cartesian + # Vettore heading x1 = r1 * np.sin(th1) y1 = r1 * np.cos(th1) - - # Calculate heading vector components h_rad = np.deg2rad(target.heading_deg) dx = vector_len * np.sin(h_rad) dy = vector_len * np.cos(h_rad) - - # Calculate end point of the vector in cartesian x2, y2 = x1 + dx, y1 + dy - - # Convert end point back to polar r2 = np.sqrt(x2**2 + y2**2) th2 = np.arctan2(x2, y2) - - # Plot the heading line line, = self.ax.plot([th1, th2], [r1, r2], color='red', linewidth=1.2) self.target_artists.append(line) - # Redraw the canvas to show the changes - self.canvas.draw() \ No newline at end of file + # Gestione tooltip + def on_motion(event): + # Mostra hint se il mouse è vicino a un punto target + if event.inaxes != self.ax: + if hasattr(self, '_tooltip_label') and self._tooltip_label: + self._tooltip_label.place_forget() + self._tooltip_label = None + return + found = False + for dot, target in self._target_dots: + cont, _ = dot.contains(event) + if cont: + # Usa la posizione del mouse relativa al widget Tkinter + self._show_tooltip(event.x + 10, event.y + 10, f"ID: {target.target_id}") + found = True + break + if not found and hasattr(self, '_tooltip_label') and self._tooltip_label: + self._tooltip_label.place_forget() + self._tooltip_label = None + + self.canvas.mpl_connect('motion_notify_event', on_motion) + self._tooltip_label = None + self.canvas.draw() + + def _show_tooltip(self, x, y, text): + # Mostra un tooltip Tkinter sopra la canvas, centrato sul punto target + if self._tooltip_label: + self._tooltip_label.place_forget() + self._tooltip_label = tk.Label(self.canvas.get_tk_widget(), text=text, bg='yellow', fg='black', font=('Consolas', 9), relief='solid', borderwidth=1) + self._tooltip_label.place(x=x, y=y) \ No newline at end of file diff --git a/target_simulator/gui/target_list_frame.py b/target_simulator/gui/target_list_frame.py index 257133e..e5d762f 100644 --- a/target_simulator/gui/target_list_frame.py +++ b/target_simulator/gui/target_list_frame.py @@ -15,9 +15,10 @@ class TargetListFrame(ttk.LabelFrame): """Frame for displaying and managing the list of targets.""" - def __init__(self, master): + def __init__(self, master, targets_changed_callback=None): super().__init__(master, text="Target List") self.targets_cache = [] + self.targets_changed_callback = targets_changed_callback # --- Treeview for Targets --- columns = ("id", "range", "az", "vel", "hdg", "alt") self.tree = ttk.Treeview(self, columns=columns, show="headings") @@ -72,6 +73,8 @@ class TargetListFrame(ttk.LabelFrame): if add_window.new_target: self.targets_cache.append(add_window.new_target) self.update_target_list(self.targets_cache) + if self.targets_changed_callback: + self.targets_changed_callback(self.targets_cache) def _on_remove_click(self): selected = self.tree.focus() @@ -93,6 +96,8 @@ class TargetListFrame(ttk.LabelFrame): # Rimuovi il target dalla lista self.targets_cache = [t for t in getattr(self, 'targets_cache', []) if t.target_id != target_id] self.update_target_list(self.targets_cache) + if self.targets_changed_callback: + self.targets_changed_callback(self.targets_cache) # Bind doppio click su una riga (usando identify_row per maggiore affidabilità) self.tree.bind('', self._on_double_click) @@ -127,7 +132,8 @@ class TargetListFrame(ttk.LabelFrame): return # Apri la finestra di modifica from .add_target_window import AddTargetWindow - edit_window = AddTargetWindow(self, existing_ids=[target_id]) + # Pass all other IDs except the one being edited + edit_window = AddTargetWindow(self, existing_ids=[t.target_id for t in self.targets_cache if t.target_id != target_id]) # Precompila i dati (tranne l'id) edit_window.id_var.set(target_id) edit_window.id_spinbox.config(state='disabled') @@ -149,6 +155,8 @@ class TargetListFrame(ttk.LabelFrame): target.altitude_ft = edit_window.new_target.altitude_ft # Aggiorna la tabella self.update_target_list(self.targets_cache) + if self.targets_changed_callback: + self.targets_changed_callback(self.targets_cache) def _on_double_click(self, event): # Usa identify_row per trovare la riga cliccata