From 941d7453bb1345975cc9c8e4924f9d9e9a5d0320 Mon Sep 17 00:00:00 2001 From: Jeremy Shen Date: Fri, 12 Jun 2026 10:24:36 +0800 Subject: [PATCH] fix: defensive attribute access in _sync_config_from_ui _sync_config_from_ui() now uses hasattr() for _port_var, _erase_cb_var, _xilinx_path_var, and _ip_string_var so it doesn't crash when called during _build_config_panel() before those widgets are created. This fixes the AttributeError: '_tkinter.tkapp' object has no attribute '_port_var' that occurred on startup. --- src/gui/main_window.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/gui/main_window.py b/src/gui/main_window.py index 55a0286..b8a3e1a 100644 --- a/src/gui/main_window.py +++ b/src/gui/main_window.py @@ -218,16 +218,21 @@ class MainWindow(ctk.CTk): object's perspective); the FileSelector stores both the absolute path (for internal use) and the relative path (for saving to YAML). + + All attribute accesses are defensive — widgets may not exist + yet during construction (e.g. _port_var created in _build_serial). """ if not self._config: return - # Sync IP address + # Sync IP address (may not exist yet during _build_config_panel) if self._ip_string_var: self._config.zynq_ip = self._ip_string_var.get() # Sync Xilinx Kit path - self._config.xilinx_path = self._xilinx_path_var.get() - # Sync serial port - self._config.serial_port = self._port_var.get() + if hasattr(self, '_xilinx_path_var'): + self._config.xilinx_path = self._xilinx_path_var.get() + # Sync serial port (may not exist yet during _build_config_panel) + if hasattr(self, '_port_var'): + self._config.serial_port = self._port_var.get() # Sync file paths from FileSelectors files = self._get_selected_files() for attr, selector in [ @@ -245,7 +250,9 @@ class MainWindow(ctk.CTk): if abs_path: self._config._config_path # ensure _config_path is set setattr(self._config, attr, self._config._relative_path(abs_path)) - self._config.erase_all = self._erase_cb_var.get() + # Sync erase checkbox (may not exist yet) + if hasattr(self, '_erase_cb_var'): + self._config.erase_all = self._erase_cb_var.get() def _auto_save_config(self) -> None: """Auto-save config to disk after UI changes.