sistemata preview della traiettoria simulata in editing
This commit is contained in:
parent
8a4f621f0c
commit
ab22246f54
@ -3,7 +3,7 @@
|
||||
"scan_limit": 60,
|
||||
"max_range": 100,
|
||||
"geometry": "1599x1089+587+179",
|
||||
"last_selected_scenario": "scenario1",
|
||||
"last_selected_scenario": "scenario_dritto",
|
||||
"connection": {
|
||||
"target": {
|
||||
"type": "sfp",
|
||||
|
||||
@ -28,6 +28,7 @@ class SimulationEngine(threading.Thread):
|
||||
communicator: Optional[CommunicatorInterface],
|
||||
simulation_hub: Optional[SimulationStateHub] = None,
|
||||
archive: Optional[str] = None,
|
||||
update_queue: Optional[Queue] = None,
|
||||
):
|
||||
super().__init__(daemon=True, name="SimulationEngineThread")
|
||||
self.logger = get_logger(__name__)
|
||||
@ -35,13 +36,19 @@ class SimulationEngine(threading.Thread):
|
||||
self.communicator = communicator
|
||||
# Backwards-compat: older callers passed an update_queue as the
|
||||
# second positional argument. Detect a Queue and treat it as the
|
||||
# update queue while leaving simulation_hub unset.
|
||||
# update queue while leaving simulation_hub unset. Also accept an
|
||||
# explicit `update_queue` keyword argument for newer callers.
|
||||
self.update_queue: Optional[Queue] = None
|
||||
if isinstance(simulation_hub, Queue):
|
||||
self.update_queue = simulation_hub
|
||||
self.simulation_hub = None
|
||||
else:
|
||||
self.simulation_hub = simulation_hub # Hub for data analysis
|
||||
|
||||
# If caller provided update_queue explicitly, prefer it (overrides
|
||||
# any queue accidentally passed in simulation_hub position).
|
||||
if isinstance(update_queue, Queue):
|
||||
self.update_queue = update_queue
|
||||
self.archive = archive # Archive path if needed
|
||||
self.time_multiplier = 1.0
|
||||
self.update_interval_s = 1.0
|
||||
@ -149,12 +156,44 @@ class SimulationEngine(threading.Thread):
|
||||
|
||||
if self.scenario.is_finished():
|
||||
self.logger.info("Scenario finished. Stopping engine.")
|
||||
# Notify any GUI preview consumers that the simulation finished
|
||||
try:
|
||||
if self.update_queue:
|
||||
try:
|
||||
self.update_queue.put_nowait("SIMULATION_FINISHED")
|
||||
except Exception:
|
||||
# fallback to blocking put to ensure delivery
|
||||
try:
|
||||
self.update_queue.put("SIMULATION_FINISHED")
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
break
|
||||
|
||||
# --- Throttled Communication Step ---
|
||||
if (current_time - self._last_update_time) >= self.update_interval_s:
|
||||
self._last_update_time = current_time
|
||||
|
||||
# If an update_queue is provided (e.g., preview mode), send the
|
||||
# current simulated targets to the queue so the UI can render
|
||||
# them even when no communicator is present.
|
||||
if self.update_queue:
|
||||
try:
|
||||
# Send a shallow list of target objects (UI will copy if needed)
|
||||
targets_snapshot = [t for t in self.scenario.get_all_targets() if t.active]
|
||||
try:
|
||||
self.update_queue.put_nowait(targets_snapshot)
|
||||
except Exception:
|
||||
# fallback to blocking put
|
||||
try:
|
||||
self.update_queue.put(targets_snapshot)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
# Don't let queue failures stop the engine
|
||||
pass
|
||||
|
||||
if self.communicator and self.communicator.is_open:
|
||||
commands_to_send = []
|
||||
|
||||
|
||||
@ -97,7 +97,11 @@ class PPIDisplay(ttk.Frame):
|
||||
top_frame.pack(side=tk.TOP, fill=tk.X, padx=5, pady=5)
|
||||
self.controls_frame = ttk.Frame(top_frame)
|
||||
self.controls_frame.pack(side=tk.LEFT, fill=tk.X, expand=True)
|
||||
ttk.Label(self.controls_frame, text="Range (NM):").pack(side=tk.LEFT)
|
||||
# Use grid inside controls_frame so we can stack the antenna animation
|
||||
# toggle immediately below the range selector as requested.
|
||||
ttk.Label(self.controls_frame, text="Range (NM):").grid(
|
||||
row=0, column=0, sticky="w"
|
||||
)
|
||||
all_steps = [10, 20, 40, 80, 100, 160, 240, 320]
|
||||
valid_steps = sorted([s for s in all_steps if s <= self.max_range])
|
||||
if not valid_steps or self.max_range not in valid_steps:
|
||||
@ -111,7 +115,17 @@ class PPIDisplay(ttk.Frame):
|
||||
state="readonly",
|
||||
width=5,
|
||||
)
|
||||
self.range_selector.pack(side=tk.LEFT, padx=5)
|
||||
self.range_selector.grid(row=0, column=1, padx=5, sticky="w")
|
||||
# Place the antenna animate toggle directly under the range selector
|
||||
# to match the requested layout: range combobox, then antenna flag.
|
||||
cb_antenna_local = ttk.Checkbutton(
|
||||
self.controls_frame,
|
||||
text="Animate Antenna",
|
||||
variable=self.animate_antenna_var,
|
||||
command=self._on_antenna_animate_changed,
|
||||
)
|
||||
cb_antenna_local.grid(row=1, column=0, columnspan=2, sticky="w", pady=(4, 0))
|
||||
|
||||
options_frame = ttk.LabelFrame(top_frame, text="Display Options")
|
||||
options_frame.pack(side=tk.RIGHT, padx=(10, 0))
|
||||
cb_sim_points = ttk.Checkbutton(
|
||||
@ -142,24 +156,24 @@ class PPIDisplay(ttk.Frame):
|
||||
command=self._on_display_options_changed,
|
||||
)
|
||||
cb_real_trail.grid(row=1, column=1, sticky="w", padx=5)
|
||||
# Antenna animate toggle (single Checkbutton)
|
||||
cb_antenna = ttk.Checkbutton(
|
||||
options_frame,
|
||||
text="Animate Antenna",
|
||||
variable=self.animate_antenna_var,
|
||||
command=self._on_antenna_animate_changed,
|
||||
)
|
||||
cb_antenna.grid(row=2, column=0, columnspan=2, sticky="w", padx=5, pady=(6, 0))
|
||||
# Legend: stack simulated and real swatches vertically to reduce width
|
||||
legend_frame = ttk.Frame(top_frame)
|
||||
legend_frame.pack(side=tk.RIGHT, padx=(10, 5))
|
||||
sim_sw = tk.Canvas(legend_frame, width=16, height=12, highlightthickness=0)
|
||||
# Use grid so items are stacked
|
||||
sim_sw = tk.Canvas(
|
||||
legend_frame, width=16, height=12, highlightthickness=0
|
||||
)
|
||||
sim_sw.create_rectangle(0, 0, 16, 12, fill="green", outline="black")
|
||||
sim_sw.pack(side=tk.LEFT, padx=(0, 4))
|
||||
ttk.Label(legend_frame, text="Simulated").pack(side=tk.LEFT, padx=(0, 8))
|
||||
real_sw = tk.Canvas(legend_frame, width=16, height=12, highlightthickness=0)
|
||||
sim_sw.grid(row=0, column=0, padx=(0, 4), pady=(0, 2))
|
||||
ttk.Label(legend_frame, text="Simulated").grid(
|
||||
row=0, column=1, sticky="w", padx=(0, 8), pady=(0, 2)
|
||||
)
|
||||
real_sw = tk.Canvas(
|
||||
legend_frame, width=16, height=12, highlightthickness=0
|
||||
)
|
||||
real_sw.create_rectangle(0, 0, 16, 12, fill="red", outline="black")
|
||||
real_sw.pack(side=tk.LEFT, padx=(2, 4))
|
||||
ttk.Label(legend_frame, text="Real").pack(side=tk.LEFT)
|
||||
real_sw.grid(row=1, column=0, padx=(0, 4))
|
||||
ttk.Label(legend_frame, text="Real").grid(row=1, column=1, sticky="w", padx=(0, 8))
|
||||
|
||||
def _create_plot(self):
|
||||
fig = Figure(figsize=(5, 5), dpi=100, facecolor="#3E3E3E")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user