refactoring main_view, estratto simulation_controls
This commit is contained in:
parent
b8a39798bb
commit
9fac7dde1e
@ -283,124 +283,127 @@ class MainView(tk.Tk):
|
|||||||
lambda event: self._on_load_scenario(self.sim_scenario_combobox.get()),
|
lambda event: self._on_load_scenario(self.sim_scenario_combobox.get()),
|
||||||
)
|
)
|
||||||
|
|
||||||
engine_frame = ttk.LabelFrame(simulation_tab, text="Live Simulation Engine")
|
# Extracted simulation controls into SimulationControls component
|
||||||
engine_frame.pack(fill=tk.X, padx=5, pady=10, anchor="n")
|
|
||||||
|
|
||||||
# Use grid within engine_frame for a tidy multi-row layout that
|
|
||||||
# doesn't force the window to expand horizontally and keeps the PPI
|
|
||||||
# area visible. Configure columns so the middle spacer expands.
|
|
||||||
for i in range(10):
|
|
||||||
engine_frame.grid_columnconfigure(i, weight=0)
|
|
||||||
# Give the spacer column (3) and the main left column (0) flexible weight
|
|
||||||
engine_frame.grid_columnconfigure(0, weight=0)
|
|
||||||
engine_frame.grid_columnconfigure(3, weight=1)
|
|
||||||
|
|
||||||
self.start_button = ttk.Button(
|
|
||||||
engine_frame, text="Start Live", command=self._on_start_simulation
|
|
||||||
)
|
|
||||||
self.start_button.grid(row=0, column=0, sticky="w", padx=5, pady=5)
|
|
||||||
|
|
||||||
self.stop_button = ttk.Button(
|
|
||||||
engine_frame,
|
|
||||||
text="Stop Live",
|
|
||||||
command=self._on_stop_simulation,
|
|
||||||
state=tk.DISABLED,
|
|
||||||
)
|
|
||||||
self.stop_button.grid(row=0, column=1, sticky="w", padx=5, pady=5)
|
|
||||||
|
|
||||||
# The application exposes a dedicated "Analysis" tab with the full
|
|
||||||
# analysis UI. The redundant "Show Analysis" button has been removed
|
|
||||||
# from the Simulation controls (use the Analysis tab instead).
|
|
||||||
|
|
||||||
# spacer to push the following controls to the right
|
|
||||||
spacer = ttk.Frame(engine_frame)
|
|
||||||
spacer.grid(row=0, column=3, sticky="ew")
|
|
||||||
|
|
||||||
ttk.Label(engine_frame, text="Speed:").grid(
|
|
||||||
row=0, column=4, sticky="e", padx=(10, 2), pady=5
|
|
||||||
)
|
|
||||||
self.time_multiplier_var = tk.StringVar(value="1x")
|
|
||||||
self.multiplier_combo = ttk.Combobox(
|
|
||||||
engine_frame,
|
|
||||||
textvariable=self.time_multiplier_var,
|
|
||||||
values=["1x", "2x", "4x", "10x", "20x"],
|
|
||||||
state="readonly",
|
|
||||||
width=4,
|
|
||||||
)
|
|
||||||
self.multiplier_combo.grid(row=0, column=5, sticky="w", padx=(0, 5), pady=5)
|
|
||||||
self.multiplier_combo.bind(
|
|
||||||
"<<ComboboxSelected>>", self._on_time_multiplier_changed
|
|
||||||
)
|
|
||||||
|
|
||||||
ttk.Label(engine_frame, text="Update Time (s):").grid(
|
|
||||||
row=0, column=6, sticky="e", padx=(10, 2), pady=5
|
|
||||||
)
|
|
||||||
self.update_time_entry = ttk.Entry(
|
|
||||||
engine_frame, textvariable=self.update_time, width=5
|
|
||||||
)
|
|
||||||
self.update_time_entry.grid(row=0, column=7, sticky="w", padx=(0, 5), pady=5)
|
|
||||||
|
|
||||||
self.reset_button = ttk.Button(
|
|
||||||
engine_frame, text="Reset State", command=self._on_reset_simulation
|
|
||||||
)
|
|
||||||
self.reset_button.grid(row=0, column=8, sticky="e", padx=5, pady=5)
|
|
||||||
|
|
||||||
self.reset_radar_button = ttk.Button(
|
|
||||||
engine_frame, text="Reset Radar", command=self._reset_radar_state
|
|
||||||
)
|
|
||||||
self.reset_radar_button.grid(row=0, column=9, sticky="e", padx=5, pady=5)
|
|
||||||
|
|
||||||
# --- Simulation progress bar / slider ---
|
|
||||||
# Place the progress frame on its own row below the control buttons
|
|
||||||
progress_frame = ttk.Frame(engine_frame)
|
|
||||||
# Place the progress frame on a dedicated grid row below the controls
|
|
||||||
progress_frame.grid(
|
|
||||||
row=1, column=0, columnspan=10, sticky="ew", padx=5, pady=(6, 2)
|
|
||||||
)
|
|
||||||
self.sim_slider = ttk.Scale(
|
|
||||||
progress_frame,
|
|
||||||
orient=tk.HORIZONTAL,
|
|
||||||
variable=self.sim_slider_var,
|
|
||||||
from_=0.0,
|
|
||||||
to=1.0,
|
|
||||||
command=lambda v: None,
|
|
||||||
# let grid manage length via sticky and column weights
|
|
||||||
)
|
|
||||||
# configure progress_frame grid so slider expands and labels stay compact
|
|
||||||
progress_frame.grid_columnconfigure(0, weight=1)
|
|
||||||
progress_frame.grid_columnconfigure(1, weight=0)
|
|
||||||
self.sim_slider.grid(row=0, column=0, sticky="ew", padx=(4, 8))
|
|
||||||
# Bind press/release to support seeking
|
|
||||||
try:
|
try:
|
||||||
self.sim_slider.bind(
|
from target_simulator.gui.simulation_controls import SimulationControls
|
||||||
"<ButtonPress-1>", lambda e: setattr(self, "_slider_is_dragging", True)
|
|
||||||
)
|
self.simulation_controls = SimulationControls(simulation_tab, self)
|
||||||
self.sim_slider.bind(
|
self.simulation_controls.pack(fill=tk.X, padx=5, pady=10, anchor="n")
|
||||||
"<ButtonRelease-1>",
|
|
||||||
lambda e: (
|
# Preserve attribute names used throughout MainView for backward compatibility
|
||||||
setattr(self, "_slider_is_dragging", False),
|
self.start_button = self.simulation_controls.start_button
|
||||||
self._on_seek(),
|
self.stop_button = self.simulation_controls.stop_button
|
||||||
),
|
self.multiplier_combo = self.simulation_controls.multiplier_combo
|
||||||
)
|
self.time_multiplier_var = self.simulation_controls.time_multiplier_var
|
||||||
|
self.update_time_entry = self.simulation_controls.update_time_entry
|
||||||
|
self.update_time = self.simulation_controls.update_time
|
||||||
|
self.reset_button = self.simulation_controls.reset_button
|
||||||
|
self.reset_radar_button = self.simulation_controls.reset_radar_button
|
||||||
|
self.sim_slider = self.simulation_controls.sim_slider
|
||||||
|
self.sim_elapsed_label = self.simulation_controls.sim_elapsed_label
|
||||||
|
self.sim_total_label = self.simulation_controls.sim_total_label
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
# If the extracted component fails, fall back to original inline layout
|
||||||
|
engine_frame = ttk.LabelFrame(simulation_tab, text="Live Simulation Engine")
|
||||||
|
engine_frame.pack(fill=tk.X, padx=5, pady=10, anchor="n")
|
||||||
|
|
||||||
# Time labels showing elapsed and total separately at the end of the bar
|
# Use grid within engine_frame for a tidy multi-row layout that
|
||||||
labels_frame = ttk.Frame(progress_frame)
|
# doesn't force the window to expand horizontally and keeps the PPI
|
||||||
labels_frame.grid(row=0, column=1, sticky="e", padx=(4, 4))
|
# area visible. Configure columns so the middle spacer expands.
|
||||||
|
for i in range(10):
|
||||||
|
engine_frame.grid_columnconfigure(i, weight=0)
|
||||||
|
# Give the spacer column (3) and the main left column (0) flexible weight
|
||||||
|
engine_frame.grid_columnconfigure(0, weight=0)
|
||||||
|
engine_frame.grid_columnconfigure(3, weight=1)
|
||||||
|
|
||||||
self.sim_elapsed_label = ttk.Label(
|
self.start_button = ttk.Button(
|
||||||
labels_frame, text="0.0s", width=8, anchor=tk.E
|
engine_frame, text="Start Live", command=self._on_start_simulation
|
||||||
)
|
)
|
||||||
self.sim_elapsed_label.grid(row=0, column=0)
|
self.start_button.grid(row=0, column=0, sticky="w", padx=5, pady=5)
|
||||||
|
|
||||||
slash_label = ttk.Label(labels_frame, text="/")
|
self.stop_button = ttk.Button(
|
||||||
slash_label.grid(row=0, column=1, padx=(2, 2))
|
engine_frame,
|
||||||
|
text="Stop Live",
|
||||||
|
command=self._on_stop_simulation,
|
||||||
|
state=tk.DISABLED,
|
||||||
|
)
|
||||||
|
self.stop_button.grid(row=0, column=1, sticky="w", padx=5, pady=5)
|
||||||
|
|
||||||
self.sim_total_label = ttk.Label(
|
spacer = ttk.Frame(engine_frame)
|
||||||
labels_frame, text="0.0s", width=8, anchor=tk.W
|
spacer.grid(row=0, column=3, sticky="ew")
|
||||||
)
|
|
||||||
self.sim_total_label.grid(row=0, column=2)
|
ttk.Label(engine_frame, text="Speed:").grid(
|
||||||
|
row=0, column=4, sticky="e", padx=(10, 2), pady=5
|
||||||
|
)
|
||||||
|
self.time_multiplier_var = tk.StringVar(value="1x")
|
||||||
|
self.multiplier_combo = ttk.Combobox(
|
||||||
|
engine_frame,
|
||||||
|
textvariable=self.time_multiplier_var,
|
||||||
|
values=["1x", "2x", "4x", "10x", "20x"],
|
||||||
|
state="readonly",
|
||||||
|
width=4,
|
||||||
|
)
|
||||||
|
self.multiplier_combo.grid(row=0, column=5, sticky="w", padx=(0, 5), pady=5)
|
||||||
|
self.multiplier_combo.bind(
|
||||||
|
"<<ComboboxSelected>>", self._on_time_multiplier_changed
|
||||||
|
)
|
||||||
|
|
||||||
|
ttk.Label(engine_frame, text="Update Time (s):").grid(
|
||||||
|
row=0, column=6, sticky="e", padx=(10, 2), pady=5
|
||||||
|
)
|
||||||
|
self.update_time_entry = ttk.Entry(
|
||||||
|
engine_frame, textvariable=self.update_time, width=5
|
||||||
|
)
|
||||||
|
self.update_time_entry.grid(row=0, column=7, sticky="w", padx=(0, 5), pady=5)
|
||||||
|
|
||||||
|
self.reset_button = ttk.Button(
|
||||||
|
engine_frame, text="Reset State", command=self._on_reset_simulation
|
||||||
|
)
|
||||||
|
self.reset_button.grid(row=0, column=8, sticky="e", padx=5, pady=5)
|
||||||
|
|
||||||
|
self.reset_radar_button = ttk.Button(
|
||||||
|
engine_frame, text="Reset Radar", command=self._reset_radar_state
|
||||||
|
)
|
||||||
|
self.reset_radar_button.grid(row=0, column=9, sticky="e", padx=5, pady=5)
|
||||||
|
|
||||||
|
progress_frame = ttk.Frame(engine_frame)
|
||||||
|
progress_frame.grid(row=1, column=0, columnspan=10, sticky="ew", padx=5, pady=(6, 2))
|
||||||
|
self.sim_slider = ttk.Scale(
|
||||||
|
progress_frame,
|
||||||
|
orient=tk.HORIZONTAL,
|
||||||
|
variable=self.sim_slider_var,
|
||||||
|
from_=0.0,
|
||||||
|
to=1.0,
|
||||||
|
command=lambda v: None,
|
||||||
|
)
|
||||||
|
progress_frame.grid_columnconfigure(0, weight=1)
|
||||||
|
progress_frame.grid_columnconfigure(1, weight=0)
|
||||||
|
self.sim_slider.grid(row=0, column=0, sticky="ew", padx=(4, 8))
|
||||||
|
try:
|
||||||
|
self.sim_slider.bind(
|
||||||
|
"<ButtonPress-1>", lambda e: setattr(self, "_slider_is_dragging", True)
|
||||||
|
)
|
||||||
|
self.sim_slider.bind(
|
||||||
|
"<ButtonRelease-1>",
|
||||||
|
lambda e: (
|
||||||
|
setattr(self, "_slider_is_dragging", False),
|
||||||
|
self._on_seek(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
labels_frame = ttk.Frame(progress_frame)
|
||||||
|
labels_frame.grid(row=0, column=1, sticky="e", padx=(4, 4))
|
||||||
|
|
||||||
|
self.sim_elapsed_label = ttk.Label(labels_frame, text="0.0s", width=8, anchor=tk.E)
|
||||||
|
self.sim_elapsed_label.grid(row=0, column=0)
|
||||||
|
|
||||||
|
slash_label = ttk.Label(labels_frame, text="/")
|
||||||
|
slash_label.grid(row=0, column=1, padx=(2, 2))
|
||||||
|
|
||||||
|
self.sim_total_label = ttk.Label(labels_frame, text="0.0s", width=8, anchor=tk.W)
|
||||||
|
self.sim_total_label.grid(row=0, column=2)
|
||||||
|
|
||||||
# --- TAB 3: LRU SIMULATION ---
|
# --- TAB 3: LRU SIMULATION ---
|
||||||
lru_tab = ttk.Frame(left_notebook)
|
lru_tab = ttk.Frame(left_notebook)
|
||||||
|
|||||||
118
target_simulator/gui/simulation_controls.py
Normal file
118
target_simulator/gui/simulation_controls.py
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
import tkinter as tk
|
||||||
|
from tkinter import ttk
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
|
class SimulationControls(ttk.LabelFrame):
|
||||||
|
"""Encapsulates the Live Simulation Engine controls.
|
||||||
|
|
||||||
|
This keeps the UI widgets (Start/Stop, Speed, Update Time, Reset, slider)
|
||||||
|
in a focused component. It uses the provided main_view reference for
|
||||||
|
callbacks and shared Tk variables to preserve existing behavior while
|
||||||
|
keeping the layout code out of `main_view.py`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, parent, main_view):
|
||||||
|
super().__init__(parent, text="Live Simulation Engine")
|
||||||
|
|
||||||
|
self.main_view = main_view
|
||||||
|
|
||||||
|
# Use main_view's variables so external code referencing them keeps
|
||||||
|
# working unchanged.
|
||||||
|
self.time_multiplier_var = getattr(main_view, "time_multiplier_var", tk.StringVar(value="1x"))
|
||||||
|
self.update_time = getattr(main_view, "update_time", tk.DoubleVar(value=1.0))
|
||||||
|
self.sim_slider_var = getattr(main_view, "sim_slider_var", tk.DoubleVar(value=0.0))
|
||||||
|
|
||||||
|
# Layout grid setup (mirror MainView original layout)
|
||||||
|
for i in range(10):
|
||||||
|
self.grid_columnconfigure(i, weight=0)
|
||||||
|
self.grid_columnconfigure(0, weight=0)
|
||||||
|
self.grid_columnconfigure(3, weight=1)
|
||||||
|
|
||||||
|
# Buttons
|
||||||
|
self.start_button = ttk.Button(self, text="Start Live", command=self._start)
|
||||||
|
self.start_button.grid(row=0, column=0, sticky="w", padx=5, pady=5)
|
||||||
|
|
||||||
|
self.stop_button = ttk.Button(self, text="Stop Live", command=self._stop, state=tk.DISABLED)
|
||||||
|
self.stop_button.grid(row=0, column=1, sticky="w", padx=5, pady=5)
|
||||||
|
|
||||||
|
# spacer
|
||||||
|
spacer = ttk.Frame(self)
|
||||||
|
spacer.grid(row=0, column=3, sticky="ew")
|
||||||
|
|
||||||
|
ttk.Label(self, text="Speed:").grid(row=0, column=4, sticky="e", padx=(10, 2), pady=5)
|
||||||
|
self.multiplier_combo = ttk.Combobox(
|
||||||
|
self,
|
||||||
|
textvariable=self.time_multiplier_var,
|
||||||
|
values=["1x", "2x", "4x", "10x", "20x"],
|
||||||
|
state="readonly",
|
||||||
|
width=4,
|
||||||
|
)
|
||||||
|
self.multiplier_combo.grid(row=0, column=5, sticky="w", padx=(0, 5), pady=5)
|
||||||
|
self.multiplier_combo.bind("<<ComboboxSelected>>", getattr(main_view, "_on_time_multiplier_changed", lambda e=None: None))
|
||||||
|
|
||||||
|
ttk.Label(self, text="Update Time (s):").grid(row=0, column=6, sticky="e", padx=(10, 2), pady=5)
|
||||||
|
self.update_time_entry = ttk.Entry(self, textvariable=self.update_time, width=5)
|
||||||
|
self.update_time_entry.grid(row=0, column=7, sticky="w", padx=(0, 5), pady=5)
|
||||||
|
|
||||||
|
self.reset_button = ttk.Button(self, text="Reset State", command=getattr(main_view, "_on_reset_simulation", lambda: None))
|
||||||
|
self.reset_button.grid(row=0, column=8, sticky="e", padx=5, pady=5)
|
||||||
|
|
||||||
|
self.reset_radar_button = ttk.Button(self, text="Reset Radar", command=getattr(main_view, "_reset_radar_state", lambda: None))
|
||||||
|
self.reset_radar_button.grid(row=0, column=9, sticky="e", padx=5, pady=5)
|
||||||
|
|
||||||
|
# Progress slider row
|
||||||
|
progress_frame = ttk.Frame(self)
|
||||||
|
progress_frame.grid(row=1, column=0, columnspan=10, sticky="ew", padx=5, pady=(6, 2))
|
||||||
|
progress_frame.grid_columnconfigure(0, weight=1)
|
||||||
|
progress_frame.grid_columnconfigure(1, weight=0)
|
||||||
|
|
||||||
|
self.sim_slider = ttk.Scale(
|
||||||
|
progress_frame,
|
||||||
|
orient=tk.HORIZONTAL,
|
||||||
|
variable=self.sim_slider_var,
|
||||||
|
from_=0.0,
|
||||||
|
to=1.0,
|
||||||
|
command=lambda v: None,
|
||||||
|
)
|
||||||
|
self.sim_slider.grid(row=0, column=0, sticky="ew", padx=(4, 8))
|
||||||
|
|
||||||
|
# Bind press/release to support seeking (use main_view handler)
|
||||||
|
try:
|
||||||
|
self.sim_slider.bind("<ButtonPress-1>", lambda e: setattr(main_view, "_slider_is_dragging", True))
|
||||||
|
self.sim_slider.bind(
|
||||||
|
"<ButtonRelease-1>",
|
||||||
|
lambda e: (
|
||||||
|
setattr(main_view, "_slider_is_dragging", False),
|
||||||
|
getattr(main_view, "_on_seek", lambda: None)(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
labels_frame = ttk.Frame(progress_frame)
|
||||||
|
labels_frame.grid(row=0, column=1, sticky="e", padx=(4, 4))
|
||||||
|
|
||||||
|
self.sim_elapsed_label = ttk.Label(labels_frame, text="0.0s", width=8, anchor=tk.E)
|
||||||
|
self.sim_elapsed_label.grid(row=0, column=0)
|
||||||
|
|
||||||
|
slash_label = ttk.Label(labels_frame, text="/")
|
||||||
|
slash_label.grid(row=0, column=1, padx=(2, 2))
|
||||||
|
|
||||||
|
self.sim_total_label = ttk.Label(labels_frame, text="0.0s", width=8, anchor=tk.W)
|
||||||
|
self.sim_total_label.grid(row=0, column=2)
|
||||||
|
|
||||||
|
# Button handlers that delegate to main_view methods
|
||||||
|
def _start(self):
|
||||||
|
try:
|
||||||
|
if hasattr(self.main_view, "_on_start_simulation"):
|
||||||
|
self.main_view._on_start_simulation()
|
||||||
|
except Exception:
|
||||||
|
raise
|
||||||
|
|
||||||
|
def _stop(self):
|
||||||
|
try:
|
||||||
|
if hasattr(self.main_view, "_on_stop_simulation"):
|
||||||
|
self.main_view._on_stop_simulation()
|
||||||
|
except Exception:
|
||||||
|
raise
|
||||||
Loading…
Reference in New Issue
Block a user