Skip to content

Conversation

@C-Achard
Copy link

@C-Achard C-Achard commented Jan 30, 2026

Finalizing the PySide6+multi-camera GUI

Further refinement of GUI added in #35 by @arturoptophys

⚠️This currently requires DLC-live 2.0.0 which is not on PyPi yet ⚠️
Please install using the live-latest-* extras in pyproject.toml

Features

  • Full project file tree refactor for more granularity and ease of use
  • Typed configs
  • Improved settings UX/UI Works as is, delayed
  • Theme/UI alignment with DLC, icons, colors, etc
  • Improved camera loading UX and validation
  • Many fixes to OpenCV backend
  • Finish config refactor
  • Fixes all issues in Pre-release fixes & improvements checklist #37
  • Fixes all issues in Feedback thread for PySide6 GUI #40
  • Figure out a proper multi-platform, multi-device OpenCV backend design
  • Make processor socket design a bit safer by default
    • Added proper warnings about executing custom code, leaving this up to user responsibiity

Also tweaks error handling, UI and UX.


Additional TODOs

arturoptophys and others added 30 commits October 21, 2025 11:22
…modern-python-and-pyqt6

Add Basler and GenTL camera backends for modular capture
…camera-functionality

Rework layout and camera handling controls
…r integration

- Implemented `get_device_count` method in `GenTLCameraBackend` to retrieve the number of GenTL devices detected.
- Added `max_devices` configuration option in `CameraSettings` to limit device probing.
- Introduced `BoundingBoxSettings` for bounding box visualization, integrated into the main GUI.
- Enhanced `DLCLiveProcessor` to accept a processor instance during configuration.
- Updated GUI to support processor selection and auto-recording based on processor commands.
- Refactored camera properties handling and removed deprecated advanced properties editor.
- Improved error handling and logging for processor connections and recording states.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
C-Achard and others added 20 commits February 9, 2026 14:04
Add a FIXME note to the top-level README; remove the now-unnecessary Installation Guide link from docs/README.md. Update tests: fix the header comment in tests/custom_processors/test_builtin_discovery_utils.py and add pytest.mark.gui to multiple unit tests in tests/gui/camera_config/test_cam_dialog_unit.py to categorize them as GUI tests.
Refactor socket-based DLC processors: add optional server startup (bind/authkey can be None), extract start_server(), and enforce per-socket timeouts. Replace LOG with logger and avoid duplicate StreamHandler registrations. Improve safe shutdown/cleanup (close listener, per-client cleanup, Windows TIME_WAIT delay), add broadcast no-op when no clients, and guard save_original usage. Rename example processor classes and update metadata, simplify filter initialization/usage, remove redundant docstrings/comments, and tighten logging messages. Overall cleanup and robustness improvements for multi-client socket usage and recording control.
Improve BaseProcessorSocket shutdown and restart behavior by tracking and joining threads.

- Import multiprocessing.connection.Client to wake a blocking accept() during stop.
- Add _accept_thread, _rx_thread and _rx_threads to track the accept thread and per-connection receiver threads.
- Start and store the accept thread instead of launching it anonymously; store and start rx threads when clients connect.
- On stop(), set _stop, attempt a Client connect to unblock accept(), close connections/listener, then join the accept thread and best-effort join tracked rx threads before clearing them.
- Clear _stop in start_server if previously set to allow restart, and minor whitespace/newline cleanup.

These changes reduce race conditions on restart, make shutdowns cleaner (especially on Windows), and help avoid lingering daemon threads.
Add an explicit "Allow processor-based control" opt-in toggle and gating logic so processor plugins are only instantiated and acted on when enabled. Update UI tooltips and enable/disable behavior, add _processor_control_enabled helper, and surface a status message when processor selection is ignored. Expand PLUGIN_SYSTEM.md with usage, security guidance, registry/scan examples, and recommended opt-in semantics. Change example socket processor defaults to bind to 127.0.0.1 instead of 0.0.0.0.
Introduce backend capability metadata and resolution negotiation features for the OpenCV backend and GUI.

Key changes:
- cameras/base.py: Add SupportLevel enum and DEFAULT_CAPABILITIES; expose CameraBackend.static_capabilities() for UI use.
- cameras/factory.py: Provide CameraFactory.backend_capabilities() to query backend feature support safely.
- cameras/backends/opencv_backend.py: Add OpenCV options (resolution_policy, persist_last_applied_resolution), track requested resolution separately, enforce mismatch policy (warn/strict/accept), persist last-applied resolution, and declare static_capabilities for OpenCV. Improve resolution/fps configuration logic and fast-start handling.
- cameras/backends/utils/opencv_discovery.py: Remove unused rebind helper and clarify apply_mode_with_verification docstring.
- config.py: Add width/height fields to CameraSettings with defaults.
- gui/camera_config_dialog.py: Add width/height controls, wire them into the form and preview logic, store fast_start under backend-specific properties, and enable/disable UI controls based on backend capabilities.

Why: These changes let the UI accurately reflect and control backend capabilities, provide configurable resolution mismatch handling, and improve robustness when negotiating camera modes (including a fast-start path and optional persistence of the last-applied resolution).
Parse an 'aravis' options namespace from settings and add resolution handling/reporting. Introduces OPTIONS_KEY, requested vs actual resolution and actual_fps properties, and sets resolution only when explicitly requested; captures device-reported Width/Height/AcquisitionFrameRate. Adds static_capabilities entries and improves error messaging. Tests updated: FakeAravis gains GenICam-style get/set integer/float feature access, tests now use properties['aravis'], and new unit/integration tests cover device-default and requested resolution behavior.
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.
Refactor Basler camera backend to improve resolution/fps handling and provide capability metadata. Adds OPTIONS_KEY, explicit requested vs actual resolution/fps fields and accessors, and a static_capabilities() method. Resolution is now applied only if explicitly requested (legacy resolution or settings.width/height), otherwise the device default is preserved. Simplifies exposure/gain/fps error handling, captures actual camera width/height/fps on open/read for GUI use, and consolidates resolution configuration into _configure_resolution(). Miscellaneous logging and minor API cleanup to better surface device-reported settings.
Parse gentl-specific settings from a dedicated 'gentl' namespace while keeping backward compatibility with legacy properties. Introduce OPTIONS_KEY and robust property parsing (cti_file, serial_number, pixel_format, rotate, crop, exposure, gain, timeout, cti_search_paths). Add SupportLevel import and static_capabilities describing supported features. Track requested vs actual resolution via _get_requested_resolution_or_none and _configure_resolution (respecting node increments and logging mismatches), and capture actual width/height/fps (properties actual_resolution and actual_fps) during configuration and read. Misc: minor import additions and defensive handling of settings properties.
When creating a new CameraSettings for a detected camera, explicitly set width and height to 0. This ensures the new_cam instance has those fields initialized rather than relying on implicit defaults.
Update OpenCVCameraBackend support levels: set_exposure and set_gain changed from BEST_EFFORT to UNSUPPORTED in dlclivegui/cameras/backends/opencv_backend.py. This updates the backend's capability reporting to reflect that exposure and gain adjustments are not supported by this backend.
Introduce a CameraProbeWorker to perform quick open/close device probes and populate detected_resolution/detected_fps in the working model (writes into backend namespace and respects fast_start). Add UI improvements: compact two-field rows, read-only Detected res/FPS labels, grouped controls (Resolution, Capture, Analog), and device id wrapping. Replace the right column with a QScrollArea to prevent layout squishing and lock scroll contents to the viewport width via eventFilter. Wire up probe lifecycle (start on camera add, progress/success/error/finished handlers) and update labels when probe results are available. Misc: minor layout/policy tweaks, preview group sizing, and ensure probe skips when preview loader is active.
Update CameraConfigDialog layout: extend _make_two_field_row signature with left_stretch and right_stretch (default 1) to allow controlling widget stretch, increase label minimum width to 30 for better alignment, and reduce the inter-field spacing from 12 to 8. Also add a 10px bottom contents margin to the scroll area to provide extra padding. Changes are backwards-compatible due to default parameter values.
Make 0/None semantics explicit for camera settings and improve OpenCV probing/GUI behavior.

- Config: default fps/width/height/exposure/gain now use 0 to mean Auto; add coercion validators and avoid overwriting explicit zeros in apply_defaults.
- OpenCV backend: treat Auto resolution as (0,0), attempt FPS set even in fast-start (best-effort), readback FPS/resolution more robustly, add actual_exposure/actual_gain properties (unsupported -> None), and clarify capability support comments.
- Factory/tests: stop assuming 30 FPS for probe-created CameraSettings; update tests to expect Auto resolution (0,0).
- GUI: show elided device name, expose Auto FPS in UI, reorganize form rows/buttons, add probe/reset flow (quick probe that can apply detected values back to requested settings), adjust preview FPS reconciliation and status logging.

These changes enable explicit "Auto" requests, safer probing of device-reported values, and a user-facing Reset action to adopt device defaults.
Expose and track actual exposure and gain for Basler and GenTL backends, and treat non-positive values as "Auto" (do not set). Basler: add actual_exposure/actual_gain properties, read/write exposure/gain only when positive, probe actual FPS after opening. Introduce _settings_value(treat_nonpositive_as_none=True) to centralize zero-as-auto logic. GenTL: parse settings so 0 means auto, add actual_exposure/actual_gain fields and properties, and attempt to read those values from the device/node_map. Includes defensive error handling when reading or setting camera parameters.
Refactor _make_two_field_row to provide clear label/value styling instead of hard minimum widths. Adds a label_min_width parameter, muted label alignment, and a QSS-based style for value widgets (bolder text, subtle background, border and padding) that also supports custom widgets like ElidingPathLabel. Adjusts widget ordering and spacing for a compact two-field layout. Also wires the detected resolution/FPS labels into the new two-field row layout (removing commented-out addRow calls) for a consistent appearance.
Move the _make_two_field_row helper out of CameraConfigDialog into dlclivegui/gui/misc/layouts.py as a shared utility. Update camera_config_dialog.py to import the new function and replace the in-class implementation with calls to the extracted helper, adjusting calls to use the new parameter names (key_width, gap). This centralizes common layout code and reduces duplication while preserving the previous value styling and column stretch behavior.
Introduce ScrubSpinBox (click-drag scrub behavior) in dlclivegui/gui/misc/drag_spinbox.py and replace several QSpinBox usages with it. ScrubSpinBox adds horizontal drag-to-adjust, Shift for fine control, Ctrl for coarse control, keyboard tracking disabled, a hint cursor and tooltip instructions. Updated imports in camera_config_dialog.py and main_window.py and swapped crop/bbox spin boxes to use the new widget to improve UX when adjusting numeric camera and bounding-box fields.
Add gap=15 to the index/backend row in the camera settings form to improve spacing, and ensure the detected FPS label is reset to '—' when clearing camera configuration values so the UI consistently shows cleared state.
The run() method just emitted success with the camera settings and the actual camera open/close ran on the GUI thread, blocking the UI.

this is now changed to:
- `CameraProbeWorker.run()` — now does the actual camera open/read/close on the background thread and emits a plain dict with the results.
- `_on_probe_success()` — unpacks actual_res/actual_fps from the received dict instead of doing I/O. Everything else (model updates, UI refreshes) stays identical.
@deruyter92 deruyter92 self-requested a review February 10, 2026 11:23
Copy link

@deruyter92 deruyter92 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@C-Achard amazing work! Looks good to me as far as I can oversee all the changes.
I really like the opt-in with the processor. And great that you improved the socket

See my previous commit for fix in the CameraProbeWorker to run in the background. Please verify if you agree or prefer to drop/change it.

Thanks for all the great work. Hope everything will be well-received during bèta testing!

@C-Achard
Copy link
Author

@deruyter92 Thanks, but I had a problem previously with running another open() in a separate thread, it would interfere with ongoing previews and break.

I agree in the end this defeats the whole purpose of the probe worker, but it does work as-is so...

Let me try your fix and I will see if we can go ahead and merge.

@C-Achard
Copy link
Author

@deruyter92 Looks good to me, thanks !

@C-Achard C-Achard requested a review from MMathisLab February 10, 2026 11:40
@C-Achard
Copy link
Author

@MMathisLab I will stop working on this branch for now as I think it is ready, feel free to merge whenever convenient.
I will follow up with docs in the DLC repo, and if any urgent fixes need deployed I will open another PR. Thanks !

@C-Achard
Copy link
Author

C-Achard commented Feb 10, 2026

Actually @deruyter92 I have to undo this for now as it causes crashes when a camera is removed then added again in the config menu (it seems that some underlying C++ object is improperly destroyed)

Opening across threads seems to lead to really nasty behavior so this is why I moved it out of the worker, it does not seem to harm performance too much anyways.

DEBUG:dlclivegui.gui.camera_config_dialog:Preview status: Probing device defaults…
[ WARN:0@26.659] global cap.cpp:480 cv::VideoCapture::open VIDEOIO(DSHOW): backend is generally available but can't be used to capture by index  
[ WARN:0@26.867] global cap.cpp:480 cv::VideoCapture::open VIDEOIO(MSMF): backend is generally available but can't be used to capture by index
[ WARN:1@27.070] global cap.cpp:459 cv::VideoCapture::open VIDEOIO(DSHOW): raised unknown C++ exception!


[ WARN:1@27.072] global cap.cpp:480 cv::VideoCapture::open VIDEOIO(DSHOW): backend is generally available but can't be used to capture by index  
[ WARN:1@27.267] global cap.cpp:480 cv::VideoCapture::open VIDEOIO(MSMF): backend is generally available but can't be used to capture by index

Again I fully agree with the spirit of your fix but perhaps it is best if we address it separately to avoid additional issues in beta testing,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request help wanted Extra attention is needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants