Skip to content

Commit af4c748

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 0bf06c0 commit af4c748

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
@@ -98,13 +98,19 @@ def __init__(self, config: ApplicationSettings | None = None):
9898
self._model_path_store = ModelPathStore(self.settings)
9999
self._settings_store = DLCLiveGUISettingsStore(self.settings)
100100

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

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

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

10391091
def _action_save_config_as(self) -> None:
1040-
file_name, _ = QFileDialog.getSaveFileName(self, "Save configuration", str(Path.home()), "JSON files (*.json)")
1092+
file_name, _ = QFileDialog.getSaveFileName(
1093+
self, "Save configuration", self._suggest_config_dialog_path(), "JSON files (*.json)"
1094+
)
10411095
if not file_name:
10421096
return
1043-
path = Path(file_name)
1097+
path = Path(file_name).expanduser()
10441098
if path.suffix.lower() != ".json":
10451099
path = path.with_suffix(".json")
1046-
self._config_path = path
1047-
self._save_config_to_path(path)
1100+
if self._save_config_to_path(path):
1101+
self._config_path = path
10481102

1049-
def _save_config_to_path(self, path: Path) -> None:
1103+
def _save_config_to_path(self, path: Path) -> bool:
10501104
try:
10511105
config = self._current_config(allow_empty_model_path=True)
10521106
config.save(path)
10531107
self._settings_store.set_last_config_path(str(path))
10541108
self._settings_store.save_full_config_snapshot(config)
10551109
except Exception as exc: # pragma: no cover - GUI interaction
10561110
self._show_error(str(exc))
1057-
return
1111+
return False
10581112
self.statusBar().showMessage(f"Saved configuration to {path}", 5000)
1113+
return True
10591114

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

0 commit comments

Comments
 (0)