From b3db92e101f481c6c41fc1698e9764f2fd686b5c Mon Sep 17 00:00:00 2001 From: VALLONGOL Date: Tue, 4 Nov 2025 08:36:22 +0100 Subject: [PATCH] modificato gestione messaggi in simulazione su status bar --- target_simulator/gui/main_view.py | 129 ++++++++++++++++++++++++++---- 1 file changed, 114 insertions(+), 15 deletions(-) diff --git a/target_simulator/gui/main_view.py b/target_simulator/gui/main_view.py index 73c1d72..cacd2f9 100644 --- a/target_simulator/gui/main_view.py +++ b/target_simulator/gui/main_view.py @@ -619,30 +619,41 @@ class MainView(tk.Tk): # Id used by show_status_message scheduling self._status_after_id = None - def show_status_message(self, text: str, timeout_ms: int = 3000): - """Show a transient status message in the main status bar. + def show_status_message(self, text: str, timeout_ms: int | None = 3000): + """Show a status message in the main status bar. - If another message is scheduled to clear, cancel it and schedule the - new message to be cleared after timeout_ms. + If timeout_ms is None the message is persistent until explicitly + cleared or replaced. Otherwise the message will be cleared back to + "Ready" after timeout_ms milliseconds. """ try: # Cancel previous scheduled clear if any try: if self._status_after_id is not None: self.after_cancel(self._status_after_id) + self._status_after_id = None except Exception: pass + # Set message self.status_var.set(text) - # Schedule clear back to Ready - def _clear(): - try: - self.status_var.set("Ready") - except Exception: - pass + # Schedule clear back to Ready if timeout provided + if timeout_ms is not None: + def _clear(): + try: + self.status_var.set("Ready") + except Exception: + pass - self._status_after_id = self.after(timeout_ms, _clear) + try: + self._status_after_id = self.after(timeout_ms, _clear) + except Exception: + # If scheduling fails, log and leave message as-is + try: + self.logger.exception("Failed to schedule status clear") + except Exception: + pass except Exception: # As a fallback, log the status try: @@ -650,6 +661,28 @@ class MainView(tk.Tk): except Exception: pass + def clear_status_message(self): + """Clear status to the default 'Ready' and cancel any pending clears.""" + try: + try: + if self._status_after_id is not None: + self.after_cancel(self._status_after_id) + except Exception: + pass + try: + self._status_after_id = None + except Exception: + pass + try: + self.status_var.set("Ready") + except Exception: + pass + except Exception: + try: + self.logger.exception("Failed to clear status message") + except Exception: + pass + def _draw_status_indicator(self, canvas, color): canvas.delete("all") canvas.create_oval(2, 2, 14, 14, fill=color, outline="black") @@ -934,9 +967,14 @@ class MainView(tk.Tk): except Exception: pass return - self._start_in_progress_main = True try: + # Show a persistent 'starting' message while the controller/engine prepares + try: + self.show_status_message("Starting simulation...", timeout_ms=None) + except Exception: + pass + # Delegate to SimulationController if available try: if hasattr(self, "simulation_controller") and self.simulation_controller: @@ -950,6 +988,11 @@ class MainView(tk.Tk): # If controller is not present or failed, attempt no-op fallback try: messagebox.showerror("Start Error", "Unable to start simulation (controller unavailable).") + # If start failed, clear the starting message so the status bar isn't stuck + try: + self.clear_status_message() + except Exception: + pass except Exception: pass finally: @@ -978,25 +1021,65 @@ class MainView(tk.Tk): def _on_simulation_finished(self): try: if hasattr(self, "simulation_controller") and self.simulation_controller: - return self.simulation_controller.on_simulation_finished(self) + try: + result = self.simulation_controller.on_simulation_finished(self) + except Exception: + result = None + else: + result = None except Exception: try: self.logger.exception("SimulationController on_finished failed; falling back to inline finished handler.") except Exception: pass + result = None + + # Ensure UI reflects finished state regardless of controller handling try: - self.logger.error("Unable to handle simulation finished (controller unavailable).") + # Mark simulation as not running + try: + self.is_simulation_running.set(False) + except Exception: + pass + + # Show a short transient message informing the user + try: + self.show_status_message("Simulation finished", timeout_ms=5000) + except Exception: + pass except Exception: - pass + try: + self.logger.error("Unable to handle simulation finished (controller unavailable).") + except Exception: + pass + + return result def _on_reset_simulation(self): self.logger.info("Resetting scenario to initial state.") + # Show a brief 'starting' message during the reset phase so users + # see that a reset/action is in progress (matches requested UX). + try: + self.show_status_message("Starting simulation...", timeout_ms=1500) + except Exception: + pass + if self.is_simulation_running.get(): self._on_stop_simulation() self.scenario.reset_simulation() self._update_all_views() + # After reset complete, clear or set Ready so the status bar does not + # remain stuck on 'Starting simulation...' + try: + self.show_status_message("Ready", timeout_ms=1500) + except Exception: + try: + self.clear_status_message() + except Exception: + pass + def _process_gui_queue(self): """ Processes a batch of updates from the GUI queue to keep the UI responsive @@ -1668,6 +1751,22 @@ class MainView(tk.Tk): self.simulation_engine is not None and self.simulation_engine.is_running() ) + # Transition: started -> set running status + if (not sim_was_running) and sim_is_running_now: + try: + # Mark internal flag and show persistent running message + try: + self.is_simulation_running.set(True) + except Exception: + pass + try: + self.show_status_message("Simulation running", timeout_ms=None) + except Exception: + pass + except Exception: + pass + + # Transition: stopped if sim_was_running and not sim_is_running_now: self._on_simulation_finished()