Skip to content

Commit d36aca8

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 963620f commit d36aca8

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."""
@@ -1011,10 +1030,36 @@ def _visualization_settings_from_ui(self) -> VisualizationSettings:
10111030
bbox_color=self._bbox_color,
10121031
)
10131032

1033+
def _suggest_config_dialog_path(self) -> str:
1034+
"""Return best initial path for load/save config dialogs."""
1035+
if getattr(self, "_config_path", None) is not None:
1036+
try:
1037+
return str(self._config_path)
1038+
except Exception:
1039+
pass
1040+
1041+
last_cfg = self._settings_store.get_last_config_path()
1042+
valid_last = self._valid_config_file_path(last_cfg)
1043+
if valid_last is not None:
1044+
return str(valid_last)
1045+
1046+
if last_cfg:
1047+
try:
1048+
p = Path(last_cfg).expanduser()
1049+
parent = p.parent
1050+
if parent.exists() and parent.is_dir():
1051+
return str(parent / (p.name or "config.json"))
1052+
except Exception:
1053+
logger.debug("Failed to derive config dialog path from %s", last_cfg, exc_info=True)
1054+
1055+
return str(Path.home() / "config.json")
1056+
10141057
# ------------------------------------------------------------------
10151058
# Actions
10161059
def _action_load_config(self) -> None:
1017-
file_name, _ = QFileDialog.getOpenFileName(self, "Load configuration", str(Path.home()), "JSON files (*.json)")
1060+
file_name, _ = QFileDialog.getOpenFileName(
1061+
self, "Load configuration", self._suggest_config_dialog_path(), "JSON files (*.json)"
1062+
)
10181063
if not file_name:
10191064
return
10201065
try:
@@ -1024,6 +1069,12 @@ def _action_load_config(self) -> None:
10241069
return
10251070
self._settings_store.set_last_config_path(file_name)
10261071
self._settings_store.save_full_config_snapshot(config)
1072+
1073+
try:
1074+
self.settings.sync()
1075+
except Exception:
1076+
logger.debug("Failed to sync settings after loading config", exc_info=True)
1077+
10271078
self._config = config
10281079
self._config_path = Path(file_name)
10291080
self._apply_config(config)
@@ -1035,28 +1086,32 @@ def _action_save_config(self) -> None:
10351086
if self._config_path is None:
10361087
self._action_save_config_as()
10371088
return
1038-
self._save_config_to_path(self._config_path)
1089+
if self._save_config_to_path(self._config_path):
1090+
self._config_path = self._config_path.expanduser()
10391091

10401092
def _action_save_config_as(self) -> None:
1041-
file_name, _ = QFileDialog.getSaveFileName(self, "Save configuration", str(Path.home()), "JSON files (*.json)")
1093+
file_name, _ = QFileDialog.getSaveFileName(
1094+
self, "Save configuration", self._suggest_config_dialog_path(), "JSON files (*.json)"
1095+
)
10421096
if not file_name:
10431097
return
1044-
path = Path(file_name)
1098+
path = Path(file_name).expanduser()
10451099
if path.suffix.lower() != ".json":
10461100
path = path.with_suffix(".json")
1047-
self._config_path = path
1048-
self._save_config_to_path(path)
1101+
if self._save_config_to_path(path):
1102+
self._config_path = path
10491103

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

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

0 commit comments

Comments
 (0)