🐛 fix(gui): fix config save and UI freeze on startup

Config save fix:
- Add _sync_config_from_ui() to sync UI values to Config object
- Store _ip_string_var reference for IP field sync
- Call _sync_config_from_ui() before every save operation
- Fix _on_close to sync before saving

UI freeze fix:
- Defer _check_vitis() and _refresh_ports_initial() to _deferred_init()
- Use self.after(100, ...) to defer until window is displayed
- Window now appears immediately, blocking ops run in background
This commit is contained in:
Jeremy Shen
2026-06-10 10:58:29 +08:00
parent 7ac04a6bea
commit 9de0153854
+35 -3
View File
@@ -80,15 +80,24 @@ class MainWindow(ctk.CTk):
self._zynq_jtag_present: bool = False
self._zynq_jtag_info = None
# StringVar references for config sync
self._ip_string_var: ctk.StringVar | None = None
self._load_config()
self._build_ui()
self._check_vitis()
self._refresh_ports_initial()
self._update_step_dependencies()
# Handle window close
self.protocol("WM_DELETE_WINDOW", self._on_close)
# Defer blocking operations until after the window is displayed
self.after(100, self._deferred_init)
def _deferred_init(self) -> None:
"""Run blocking operations after the window is shown."""
self._check_vitis()
self._refresh_ports_initial()
# ── Config ─────────────────────────────────────────────────
def _find_config(self) -> Path | None:
@@ -126,8 +135,29 @@ class MainWindow(ctk.CTk):
else:
self._config = Config.from_default()
def _sync_config_from_ui(self) -> None:
"""Read current UI values and update the Config object.
This must be called before _save_config() to ensure
the Config object has the latest UI values.
"""
if not self._config:
return
# Sync IP address
if self._ip_string_var:
self._config.zynq_ip = self._ip_string_var.get()
# Sync serial port
self._config.serial_port = self._port_var.get()
# Sync file paths from FileSelectors
files = self._get_selected_files()
self._config.bootloader_bit_path = files["bootloader_bit_path"]
self._config.bootloader_elf_path = files["bootloader_elf_path"]
self._config.bootloader_bin_path = files["bootloader_bin_path"]
self._config.firmware_bin_path = files["firmware_bin_path"]
def _save_config(self) -> None:
"""Save current configuration to file."""
self._sync_config_from_ui() # Sync UI values first
if self._config and self._config.config_path:
self._config.save()
@@ -335,7 +365,8 @@ class MainWindow(ctk.CTk):
ip_frame.grid_columnconfigure(1, weight=1)
ctk.CTkLabel(ip_frame, text="Zynq IP:", font=FONT_BODY).grid(row=0, column=0, sticky="w")
self._ip_entry = ctk.CTkEntry(ip_frame, textvariable=ctk.StringVar(value=self._config.zynq_ip))
self._ip_string_var = ctk.StringVar(value=self._config.zynq_ip)
self._ip_entry = ctk.CTkEntry(ip_frame, textvariable=self._ip_string_var)
self._ip_entry.grid(row=0, column=1, sticky="nsew", padx=PADDING_SMALL)
# BIT path (Bootloader)
@@ -946,6 +977,7 @@ class MainWindow(ctk.CTk):
def _on_close(self) -> None:
"""Handle window close event: save config and cleanup."""
if self._config:
self._sync_config_from_ui() # Sync UI values first
try:
self._config.save()
except Exception: