Skip to content

Commit e6aa601

Browse files
committed
Apply gentl trigger defaults when saving
Add with_save_defaults helpers to CameraSettings and MultiCameraSettings and use them when serializing ApplicationSettings so gentl "trigger" defaults are applied to saved configs. Remove duplicated camera-default helper logic from DLCLiveMainWindow and simplify _current_config to rely on model methods. Add unit tests to verify gentl trigger defaults are included for top-level and multi-camera cases.
1 parent 53ea4c2 commit e6aa601

3 files changed

Lines changed: 77 additions & 35 deletions

File tree

dlclivegui/config.py

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,25 @@ def set_trigger_settings(self, trigger: CameraTriggerSettings, backend: str | No
190190
self.properties[str(key).lower()] = ns
191191
ns["trigger"] = trigger.to_properties()
192192

193+
def with_save_defaults(self) -> CameraSettings:
194+
out = self.model_copy(deep=True)
195+
196+
backend = (out.backend or "").lower()
197+
if backend != "gentl":
198+
return out
199+
200+
if not isinstance(out.properties, dict):
201+
out.properties = {}
202+
203+
ns = out.properties.setdefault("gentl", {})
204+
if not isinstance(ns, dict):
205+
ns = {}
206+
out.properties["gentl"] = ns
207+
208+
ns.setdefault("trigger", CameraTriggerSettings().to_properties())
209+
210+
return out
211+
193212

194213
class CameraTriggerSettings(BaseModel):
195214
"""
@@ -302,12 +321,19 @@ def from_dict(cls, data: dict[str, Any]) -> MultiCameraSettings:
302321
return cls(cameras=cameras, max_cameras=max_cameras, tile_layout=tile_layout)
303322

304323
def to_dict(self) -> dict[str, Any]:
324+
out = self.with_save_defaults()
305325
return {
306-
"cameras": [cam.model_dump() for cam in self.cameras],
307-
"max_cameras": self.max_cameras,
308-
"tile_layout": self.tile_layout,
326+
"cameras": [cam.model_dump() for cam in out.cameras],
327+
"max_cameras": out.max_cameras,
328+
"tile_layout": out.tile_layout,
309329
}
310330

331+
def with_save_defaults(self) -> MultiCameraSettings:
332+
"""Return a copy with save defaults applied to all cameras."""
333+
out = self.model_copy(deep=True)
334+
out.cameras = [cam.with_save_defaults() for cam in out.cameras]
335+
return out
336+
311337

312338
class DynamicCropModel(BaseModel):
313339
enabled: bool = False
@@ -473,10 +499,18 @@ def from_dict(cls, data: dict[str, Any]) -> ApplicationSettings:
473499
)
474500

475501
def to_dict(self) -> dict[str, Any]:
502+
multi_camera = self.multi_camera.with_save_defaults()
503+
active_cameras = multi_camera.get_active_cameras()
504+
505+
if active_cameras:
506+
camera = active_cameras[0].model_copy(deep=True)
507+
else:
508+
camera = self.camera.with_save_defaults()
509+
476510
return {
477511
"version": self.version,
478-
"camera": self.camera.model_dump(),
479-
"multi_camera": self.multi_camera.to_dict(),
512+
"camera": camera.model_dump(),
513+
"multi_camera": multi_camera.to_dict(),
480514
"dlc": self.dlc.model_dump(),
481515
"recording": self.recording.model_dump(),
482516
"bbox": self.bbox.model_dump(),

dlclivegui/gui/main_window.py

Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@
5656
ApplicationSettings,
5757
BoundingBoxSettings,
5858
CameraSettings,
59-
CameraTriggerSettings,
6059
DLCProcessorSettings,
6160
MultiCameraSettings,
6261
RecordingSettings,
@@ -855,39 +854,12 @@ def _apply_config(self, config: ApplicationSettings) -> None:
855854
# Update recording path preview
856855
self._update_recording_path_preview()
857856

858-
def _with_camera_trigger_defaults_for_save(self, cam: CameraSettings) -> CameraSettings:
859-
out = cam.model_copy(deep=True)
860-
861-
backend = (out.backend or "").lower()
862-
if backend != "gentl":
863-
return out
864-
865-
if not isinstance(out.properties, dict):
866-
out.properties = {}
867-
868-
ns = out.properties.setdefault("gentl", {})
869-
if not isinstance(ns, dict):
870-
ns = {}
871-
out.properties["gentl"] = ns
872-
873-
ns.setdefault("trigger", CameraTriggerSettings().model_dump(exclude_none=True))
874-
875-
return out
876-
877-
def _with_camera_defaults_for_save(self, settings: MultiCameraSettings) -> MultiCameraSettings:
878-
out = settings.model_copy(deep=True)
879-
880-
out.cameras = [self._with_camera_trigger_defaults_for_save(cam) for cam in out.cameras]
881-
882-
return out
883-
884857
def _current_config(self, *, allow_empty_model_path=False) -> ApplicationSettings:
885-
# Get the first camera from multi-camera config for backward compatibility
886-
multi_camera = self._with_camera_defaults_for_save(self._config.multi_camera)
858+
multi_camera = self._config.multi_camera
887859
camera = (
888860
multi_camera.cameras[0].model_copy(deep=True)
889861
if multi_camera.cameras
890-
else self._with_camera_trigger_defaults_for_save(self._config.camera)
862+
else self._config.camera.model_copy(deep=True)
891863
)
892864

893865
return ApplicationSettings(

tests/test_config.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import pytest
2+
3+
from dlclivegui.config import ApplicationSettings, CameraSettings, MultiCameraSettings
4+
5+
6+
@pytest.mark.unit
7+
def test_save_applies_gentl_trigger_defaults_to_top_level_camera():
8+
cam = CameraSettings(
9+
backend="gentl",
10+
properties={"gentl": {}},
11+
)
12+
13+
settings = ApplicationSettings(
14+
camera=cam,
15+
multi_camera=MultiCameraSettings(cameras=[cam]),
16+
)
17+
18+
data = settings.to_dict()
19+
20+
assert "trigger" in data["camera"]["properties"]["gentl"]
21+
22+
23+
@pytest.mark.unit
24+
def test_save_applies_gentl_trigger_defaults_to_multi_camera():
25+
cam = CameraSettings(
26+
backend="gentl",
27+
properties={"gentl": {}},
28+
)
29+
30+
settings = ApplicationSettings(
31+
multi_camera=MultiCameraSettings(cameras=[cam]),
32+
)
33+
34+
data = settings.to_dict()
35+
36+
assert "trigger" in data["multi_camera"]["cameras"][0]["properties"]["gentl"]

0 commit comments

Comments
 (0)