Skip to content

Commit 71ef8d5

Browse files
committed
Add width/height UI hooks and adjust OpenCV tests
Enable apply button when cam_width/cam_height change in CameraConfigDialog. Expand test helper make_settings to include width, height, backend and name, and update OpenCV backend tests to: prefer properties.resolution over width/height; fall back to width/height when no property; default to (720,540) when nothing requested; and use width/height if legacy resolution is absent. Adjust tests to expect fast_start to preserve the requested resolution (do not overwrite settings) and to nest OpenCV-specific flags under an "opencv" properties key. Update GUI unit test monkeypatch for backend capabilities and switch a recording UI test to use allow_processor_ctrl_checkbox instead of auto_record_checkbox.
1 parent 3ea06cc commit 71ef8d5

4 files changed

Lines changed: 102 additions & 24 deletions

File tree

dlclivegui/gui/camera_config_dialog.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,15 @@ def _connect_signals(self) -> None:
645645
self.cancel_btn.clicked.connect(self.reject)
646646
self.scan_started.connect(lambda _: setattr(self, "_dialog_active", True))
647647
self.scan_finished.connect(lambda: setattr(self, "_dialog_active", False))
648-
for sb in (self.cam_fps, self.cam_crop_x0, self.cam_crop_y0, self.cam_crop_x1, self.cam_crop_y1):
648+
for sb in (
649+
self.cam_fps,
650+
self.cam_crop_x0,
651+
self.cam_crop_y0,
652+
self.cam_crop_x1,
653+
self.cam_crop_y1,
654+
self.cam_width,
655+
self.cam_height,
656+
):
649657
if hasattr(sb, "valueChanged"):
650658
sb.valueChanged.connect(lambda _=None: self.apply_settings_btn.setEnabled(True))
651659
self.cam_rotation.currentIndexChanged.connect(lambda _: self.apply_settings_btn.setEnabled(True))

tests/cameras/backends/test_opencv_backend.py

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,45 @@
88
pytestmark = pytest.mark.unit
99

1010

11-
def make_settings(index=0, fps=30.0, properties=None):
12-
"""Minimal settings object compatible with CameraBackend usage."""
11+
def make_settings(index=0, fps=30.0, properties=None, *, width=0, height=0, backend="opencv", name="Test"):
12+
"""Minimal settings object compatible with OpenCVCameraBackend usage."""
1313
if properties is None:
1414
properties = {}
15-
return SimpleNamespace(index=index, fps=fps, properties=properties)
15+
return SimpleNamespace(
16+
index=index,
17+
fps=fps,
18+
properties=properties,
19+
width=width,
20+
height=height,
21+
backend=backend,
22+
name=name,
23+
)
24+
1625

26+
def test_requested_resolution_precedence_property_over_width_height():
27+
settings = make_settings(
28+
properties={"resolution": (800, 600)},
29+
width=1280,
30+
height=720,
31+
)
32+
backend = ob.OpenCVCameraBackend(settings)
33+
assert backend._get_requested_resolution() == (800, 600)
1734

18-
def test_parse_resolution_defaults_and_invalid_values():
19-
backend = ob.OpenCVCameraBackend(make_settings(properties={}))
20-
assert backend._parse_resolution(None) == (720, 540)
21-
assert backend._parse_resolution([1280, 720]) == (1280, 720)
22-
assert backend._parse_resolution(("1920", "1080")) == (1920, 1080)
23-
assert backend._parse_resolution(("bad", 123)) == (720, 540)
24-
assert backend._parse_resolution("nope") == (720, 540)
35+
36+
def test_requested_resolution_uses_width_height_when_no_property_resolution():
37+
settings = make_settings(
38+
properties={},
39+
width=1280,
40+
height=720,
41+
)
42+
backend = ob.OpenCVCameraBackend(settings)
43+
assert backend._get_requested_resolution() == (1280, 720)
44+
45+
46+
def test_requested_resolution_defaults_when_no_request():
47+
settings = make_settings(properties={}, width=0, height=0)
48+
backend = ob.OpenCVCameraBackend(settings)
49+
assert backend._get_requested_resolution() == (720, 540)
2550

2651

2752
def test_try_open_windows_fallback_to_msmf(monkeypatch, fake_capture_factory):
@@ -68,7 +93,7 @@ def fake_videocapture(index, flag):
6893

6994
monkeypatch.setattr(ob.cv2, "VideoCapture", fake_videocapture)
7095

71-
settings = make_settings(index=0, fps=30.0, properties={"alt_index_probe": True})
96+
settings = make_settings(index=0, fps=30.0, properties={"opencv": {"alt_index_probe": True}})
7297
backend = ob.OpenCVCameraBackend(settings)
7398
backend.open()
7499

@@ -145,7 +170,6 @@ def test_configure_capture_sets_resolution_and_fps_non_faststart_windows(monkeyp
145170
backend._configure_capture()
146171

147172
assert backend.actual_resolution == (800, 600)
148-
assert settings.properties["resolution"] == (800, 600)
149173
assert backend.actual_fps is not None
150174
assert isinstance(backend.actual_fps, float)
151175

@@ -157,18 +181,20 @@ def test_configure_capture_fast_start_does_not_force_resolution(monkeypatch, fak
157181
cap.props[ob.cv2.CAP_PROP_FRAME_WIDTH] = 1920.0
158182
cap.props[ob.cv2.CAP_PROP_FRAME_HEIGHT] = 1080.0
159183

160-
settings = make_settings(index=0, fps=30.0, properties={"resolution": (1280, 720), "opencv": {"fast_start": True}})
184+
settings = make_settings(
185+
index=0,
186+
fps=30.0,
187+
properties={"resolution": (1280, 720), "opencv": {"fast_start": True}},
188+
)
161189
backend = ob.OpenCVCameraBackend(settings)
162190
backend._capture = cap
163191

164192
backend._configure_capture()
165193

166-
assert backend.actual_resolution == (1920, 1080)
194+
# Fast-start still applies requested resolution, just without verification
195+
assert backend.actual_resolution == (1280, 720)
167196

168-
# Fast-start does not configure the camera; it only reports the current mode.
169-
# Keep "resolution" as the requested intent (do not overwrite with observed values).
170-
# Settings are applied later when user clicks "Apply settings" in the UI,
171-
# so it's important not to overwrite them here.
197+
# Requested intent must remain unchanged
172198
assert settings.properties["resolution"] == (1280, 720)
173199

174200

@@ -181,9 +207,7 @@ def test_configure_capture_applies_only_safe_numeric_properties(monkeypatch, fak
181207
fps=30.0,
182208
properties={
183209
"resolution": (640, 480),
184-
"api": "ANY",
185-
"fast_start": False,
186-
"alt_index_probe": False,
210+
"opencv": {"api": "ANY", "fast_start": False, "alt_index_probe": False},
187211
str(int(getattr(ob.cv2, "CAP_PROP_GAIN", 14))): 7,
188212
"999": 123,
189213
"not-a-number": 1,
@@ -213,3 +237,20 @@ def test_close_and_stop_release_capture(fake_capture_factory):
213237
backend.stop()
214238
assert backend._capture is None
215239
assert cap2.released is True
240+
241+
242+
def test_configure_capture_uses_width_height_when_no_legacy_resolution(monkeypatch, fake_capture_factory):
243+
monkeypatch.setattr(ob.platform, "system", lambda: "Windows")
244+
245+
cap = fake_capture_factory(opened=True, backend_name="DSHOW")
246+
cap.props[ob.cv2.CAP_PROP_FRAME_WIDTH] = 640.0
247+
cap.props[ob.cv2.CAP_PROP_FRAME_HEIGHT] = 480.0
248+
cap.props[ob.cv2.CAP_PROP_FPS] = 30.0
249+
250+
settings = make_settings(index=0, fps=30.0, properties={}, width=1024, height=768)
251+
backend = ob.OpenCVCameraBackend(settings)
252+
backend._capture = cap
253+
254+
backend._configure_capture()
255+
256+
assert backend.actual_resolution == (1024, 768)

tests/gui/camera_config/test_cam_dialog_unit.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,36 @@ def test_apply_settings_updates_model(dialog, qtbot):
6868

6969

7070
@pytest.mark.gui
71-
def test_backend_control_disables_exposure_gain_for_opencv(dialog):
71+
def test_backend_control_disables_exposure_gain_for_opencv(dialog, monkeypatch):
72+
from dlclivegui.cameras.base import SupportLevel
73+
74+
def fake_caps(name: str):
75+
if name == "opencv":
76+
return {
77+
"set_exposure": SupportLevel.UNSUPPORTED, # or UNSUPPORTED if you prefer
78+
"set_gain": SupportLevel.UNSUPPORTED,
79+
"set_resolution": SupportLevel.SUPPORTED,
80+
"set_fps": SupportLevel.BEST_EFFORT,
81+
"device_discovery": SupportLevel.SUPPORTED,
82+
"stable_identity": SupportLevel.SUPPORTED,
83+
}
84+
if name == "basler":
85+
return {
86+
"set_exposure": SupportLevel.SUPPORTED,
87+
"set_gain": SupportLevel.SUPPORTED,
88+
"set_resolution": SupportLevel.SUPPORTED,
89+
"set_fps": SupportLevel.SUPPORTED,
90+
"device_discovery": SupportLevel.BEST_EFFORT,
91+
"stable_identity": SupportLevel.SUPPORTED,
92+
}
93+
return {}
94+
95+
monkeypatch.setattr(
96+
"dlclivegui.cameras.CameraFactory.backend_capabilities",
97+
lambda backend_name: fake_caps(backend_name),
98+
raising=False,
99+
)
100+
72101
dialog._update_controls_for_backend("opencv")
73102
assert not dialog.cam_exposure.isEnabled()
74103
assert not dialog.cam_gain.isEnabled()

tests/gui/test_recording_paths_ui.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ def test_processor_overrides_session_name_and_persists(window, start_all_spy, mo
139139
# Arrange window state so processor status logic runs
140140
window._dlc_active = True
141141
window._dlc_initialized = True
142-
window.auto_record_checkbox.setChecked(True)
142+
window.allow_processor_ctrl_checkbox.setChecked(True)
143143

144144
# Patch start_recording to avoid preview start/timers
145145
monkeypatch.setattr(window, "_start_recording", lambda: window._start_multi_camera_recording())

0 commit comments

Comments
 (0)