Skip to content

Commit aabbdae

Browse files
committed
Improve config path handling in main window
This updates configuration load/save flow to keep a valid associated config file path when restoring from QSettings snapshots, and to use a smarter default path for file dialogs (current config, last valid config, or derived parent path). It also makes saves return success/failure so `_config_path` is only updated on successful writes, and syncs settings immediately after loading a config to persist metadata reliably.
1 parent 420aedd commit aabbdae

1 file changed

Lines changed: 65 additions & 10 deletions

File tree

‎dlclivegui/gui/main_window.py‎

Lines changed: 65 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,19 @@ def __init__(self, config: ApplicationSettings | None = None):
9999
self._model_path_store = ModelPathStore(self.settings)
100100
self._settings_store = DLCLiveGUISettingsStore(self.settings)
101101

102+
last_cfg_path = self._settings_store.get_last_config_path()
103+
last_cfg_file = self._valid_config_file_path(last_cfg_path)
102104
if config is None:
103105
# 1) snapshot
104106
cfg = self._settings_store.load_full_config_snapshot()
105107
if cfg is not None:
106108
config = cfg
107-
self._config_path = None
108-
logger.info("Loaded configuration from QSettings snapshot.")
109+
self._config_path = last_cfg_file
110+
if self._config_path is not None:
111+
logger.info(f"Loaded configuration from QSettings snapshot; associated file: {self._config_path}")
112+
else:
113+
logger.info("Loaded configuration from QSettings snapshot without associated config file.")
114+
109115
else:
110116
# 2) last config file path
111117
last_cfg_path = self._settings_store.get_last_config_path()
@@ -226,6 +232,19 @@ def resizeEvent(self, event):
226232
if not self.multi_camera_controller.is_running():
227233
self._show_logo_and_text()
228234

235+
def _valid_config_file_path(self, path: str | None) -> Path | None:
236+
if not path:
237+
return None
238+
239+
try:
240+
p = Path(path).expanduser()
241+
if p.exists() and p.is_file():
242+
return p.resolve()
243+
except Exception:
244+
logger.debug("Invalid config file path: %s", path, exc_info=True)
245+
246+
return None
247+
229248
# ------------------------------------------------------------------ UI
230249
def _init_theme_actions(self) -> None:
231250
"""Set initial checked state for theme actions based on current app stylesheet."""
@@ -1013,10 +1032,36 @@ def _visualization_settings_from_ui(self) -> VisualizationSettings:
10131032
bbox_color=self._bbox_color,
10141033
)
10151034

1035+
def _suggest_config_dialog_path(self) -> str:
1036+
"""Return best initial path for load/save config dialogs."""
1037+
if getattr(self, "_config_path", None) is not None:
1038+
try:
1039+
return str(self._config_path)
1040+
except Exception:
1041+
pass
1042+
1043+
last_cfg = self._settings_store.get_last_config_path()
1044+
valid_last = self._valid_config_file_path(last_cfg)
1045+
if valid_last is not None:
1046+
return str(valid_last)
1047+
1048+
if last_cfg:
1049+
try:
1050+
p = Path(last_cfg).expanduser()
1051+
parent = p.parent
1052+
if parent.exists() and parent.is_dir():
1053+
return str(parent / (p.name or "config.json"))
1054+
except Exception:
1055+
logger.debug("Failed to derive config dialog path from %s", last_cfg, exc_info=True)
1056+
1057+
return str(Path.home() / "config.json")
1058+
10161059
# ------------------------------------------------------------------
10171060
# Actions
10181061
def _action_load_config(self) -> None:
1019-
file_name, _ = QFileDialog.getOpenFileName(self, "Load configuration", str(Path.home()), "JSON files (*.json)")
1062+
file_name, _ = QFileDialog.getOpenFileName(
1063+
self, "Load configuration", self._suggest_config_dialog_path(), "JSON files (*.json)"
1064+
)
10201065
if not file_name:
10211066
return
10221067
try:
@@ -1026,6 +1071,12 @@ def _action_load_config(self) -> None:
10261071
return
10271072
self._settings_store.set_last_config_path(file_name)
10281073
self._settings_store.save_full_config_snapshot(config)
1074+
1075+
try:
1076+
self.settings.sync()
1077+
except Exception:
1078+
logger.debug("Failed to sync settings after loading config", exc_info=True)
1079+
10291080
self._config = config
10301081
self._config_path = Path(file_name)
10311082
self._apply_config(config)
@@ -1037,28 +1088,32 @@ def _action_save_config(self) -> None:
10371088
if self._config_path is None:
10381089
self._action_save_config_as()
10391090
return
1040-
self._save_config_to_path(self._config_path)
1091+
if self._save_config_to_path(self._config_path):
1092+
self._config_path = self._config_path.expanduser()
10411093

10421094
def _action_save_config_as(self) -> None:
1043-
file_name, _ = QFileDialog.getSaveFileName(self, "Save configuration", str(Path.home()), "JSON files (*.json)")
1095+
file_name, _ = QFileDialog.getSaveFileName(
1096+
self, "Save configuration", self._suggest_config_dialog_path(), "JSON files (*.json)"
1097+
)
10441098
if not file_name:
10451099
return
1046-
path = Path(file_name)
1100+
path = Path(file_name).expanduser()
10471101
if path.suffix.lower() != ".json":
10481102
path = path.with_suffix(".json")
1049-
self._config_path = path
1050-
self._save_config_to_path(path)
1103+
if self._save_config_to_path(path):
1104+
self._config_path = path
10511105

1052-
def _save_config_to_path(self, path: Path) -> None:
1106+
def _save_config_to_path(self, path: Path) -> bool:
10531107
try:
10541108
config = self._current_config(allow_empty_model_path=True)
10551109
config.save(path)
10561110
self._settings_store.set_last_config_path(str(path))
10571111
self._settings_store.save_full_config_snapshot(config)
10581112
except Exception as exc: # pragma: no cover - GUI interaction
10591113
self._show_error(str(exc))
1060-
return
1114+
return False
10611115
self.statusBar().showMessage(f"Saved configuration to {path}", 5000)
1116+
return True
10621117

10631118
def _action_browse_model(self) -> None:
10641119
# Prefer persisted last-used directory, then config.dlc.model_directory, then home

0 commit comments

Comments
 (0)