feat: Add Camera Settings dialog for UVC controls and Qt parameters
- Implemented CameraSettingsDialog to manage UVC and Qt camera controls. - Integrated UVC parameter sliders and auto controls for brightness, contrast, saturation, hue, sharpness, gamma, white balance, backlight compensation, and exposure. - Added functionality to change white balance and exposure settings via Qt controls. - Updated MainWindow to open CameraSettingsDialog and manage UVC controller lifecycle. - Enhanced AppMenuBar to include a Camera Settings option. - Created tests for UVC controller abstraction layer and parameter info. - Documented camera specifications and supported features in new markdown files.
This commit is contained in:
@@ -8,13 +8,17 @@ from pathlib import Path
|
||||
from PySide6.QtCore import QTimer
|
||||
from PySide6.QtWidgets import QLabel, QMainWindow, QSizePolicy, QStatusBar
|
||||
|
||||
from app.camera.camera_enumerator import CameraEnumerator, CameraInfo
|
||||
from app.camera.camera_enumerator import CameraEnumerator, CameraFormat, CameraInfo
|
||||
from app.camera.camera_service import CameraService
|
||||
from app.camera.uvc import make_uvc_controller
|
||||
from app.camera.uvc.base import UvcControllerBase
|
||||
from app.camera.uvc.stub import NullUvcController
|
||||
from app.config import APP_NAME, APP_VERSION
|
||||
from app.overlay.telemetry_overlay import TelemetryOverlay
|
||||
from app.pipeline.frame_dispatcher import FrameDispatcher
|
||||
from app.telemetry.csv_logger import CsvTelemetryLogger
|
||||
from app.telemetry.telemetry_collector import TelemetryCollector
|
||||
from app.ui.camera_settings_dialog import CameraSettingsDialog
|
||||
from app.ui.camera_view import CameraView
|
||||
from app.ui.menu_bar import AppMenuBar
|
||||
|
||||
@@ -52,6 +56,9 @@ class MainWindow(QMainWindow):
|
||||
self._dispatcher = FrameDispatcher(self)
|
||||
self._telemetry = TelemetryCollector(parent=self)
|
||||
|
||||
# --- UVC controller (platform-specific, lazy-opened per camera) ---
|
||||
self._uvc: UvcControllerBase = NullUvcController()
|
||||
|
||||
# --- CSV telemetry logger ---
|
||||
self._csv_logger: CsvTelemetryLogger | None = None
|
||||
if log_path is not None:
|
||||
@@ -79,7 +86,7 @@ class MainWindow(QMainWindow):
|
||||
# --- Status bar ---
|
||||
self._status_bar = QStatusBar(self)
|
||||
self.setStatusBar(self._status_bar)
|
||||
self._status_label = QLabel("Initialising…")
|
||||
self._status_label = QLabel("Initialising\u2026")
|
||||
self._status_bar.addWidget(self._status_label)
|
||||
|
||||
# --- Wire signals ---
|
||||
@@ -113,6 +120,17 @@ class MainWindow(QMainWindow):
|
||||
self._camera_service.start(cam)
|
||||
self._menu.set_active_camera(cam)
|
||||
self._status_label.setText(f"Opening: {cam.name}")
|
||||
self._open_uvc(cam)
|
||||
|
||||
def _open_uvc(self, cam: CameraInfo) -> None:
|
||||
"""Open or reopen the UVC controller for the given camera."""
|
||||
if self._uvc.is_open():
|
||||
self._uvc.close()
|
||||
ctrl = make_uvc_controller(cam.name)
|
||||
if not ctrl.is_open():
|
||||
# factory may return a pre-opened controller or a NullUvcController
|
||||
ctrl.open(cam.name)
|
||||
self._uvc = ctrl
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Signal wiring
|
||||
@@ -125,11 +143,13 @@ class MainWindow(QMainWindow):
|
||||
# FrameDispatcher → CameraView (render) — drop if busy
|
||||
self._dispatcher.subscribe(self._camera_view.on_frame, drop_if_busy=True)
|
||||
|
||||
# FrameDispatcher → TelemetryCollector — never drop, count every frame
|
||||
# FrameDispatcher → TelemetryCollector — never drop
|
||||
self._dispatcher.subscribe(self._telemetry.on_frame, drop_if_busy=False)
|
||||
|
||||
# TelemetryCollector → overlay
|
||||
self._telemetry.metrics_updated.connect(self._telemetry_overlay.on_metrics_updated)
|
||||
self._telemetry.metrics_updated.connect(
|
||||
self._telemetry_overlay.on_metrics_updated
|
||||
)
|
||||
|
||||
# TelemetryCollector → CSV logger (throttled internally)
|
||||
if self._csv_logger is not None:
|
||||
@@ -145,10 +165,10 @@ class MainWindow(QMainWindow):
|
||||
|
||||
# Menu signals
|
||||
self._menu.camera_selected.connect(self._on_camera_selected)
|
||||
self._menu.resolution_selected.connect(self._on_resolution_selected)
|
||||
self._menu.fps_selected.connect(self._on_fps_selected)
|
||||
self._menu.format_selected.connect(self._on_format_selected)
|
||||
self._menu.reconnect_requested.connect(self._camera_service.reconnect)
|
||||
self._menu.overlay_toggled.connect(self._camera_view.set_all_overlays_visible)
|
||||
self._menu.camera_settings_requested.connect(self._on_settings_requested)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Camera status slots
|
||||
@@ -174,11 +194,20 @@ class MainWindow(QMainWindow):
|
||||
def _on_camera_selected(self, cam: CameraInfo) -> None:
|
||||
self._start_camera(cam)
|
||||
|
||||
def _on_resolution_selected(self, width: int, height: int) -> None:
|
||||
self._camera_service.set_resolution(width, height)
|
||||
def _on_format_selected(self, fmt: CameraFormat) -> None:
|
||||
logger.info(
|
||||
"Format selected via menu: %dx%d @ %.4g fps (%s)",
|
||||
fmt.width, fmt.height, fmt.max_fps, fmt.pixel_format,
|
||||
)
|
||||
self._camera_service.set_format(fmt)
|
||||
|
||||
def _on_fps_selected(self, fps: float) -> None:
|
||||
self._camera_service.set_fps(fps)
|
||||
def _on_settings_requested(self) -> None:
|
||||
qt_cam = self._camera_service.qt_camera
|
||||
if qt_cam is None:
|
||||
logger.warning("Settings requested but no camera is active")
|
||||
return
|
||||
dlg = CameraSettingsDialog(qt_cam, self._uvc, parent=self)
|
||||
dlg.exec()
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Qt overrides
|
||||
@@ -186,6 +215,8 @@ class MainWindow(QMainWindow):
|
||||
|
||||
def closeEvent(self, event) -> None: # noqa: N802
|
||||
self._camera_service.stop()
|
||||
if self._uvc.is_open():
|
||||
self._uvc.close()
|
||||
if self._csv_logger is not None:
|
||||
logger.info(
|
||||
"CSV telemetry: %d rows written", self._csv_logger.rows_written
|
||||
|
||||
Reference in New Issue
Block a user