Files
AareDAQ/src/aare/gui/new_gui/widgets/camera_controls.py
T
appleb_mandClaude Opus 4.8 5b833bf9eb new_gui Phase 2: camera ML overlays, controls/screenshot, gonio/beamline video, preconditions
- camera: ML detection boxes+polygons (per-class colours), state-coloured beam
  centre marker, focus readout
- camera controls in tab strip: gain/exposure (samcam_settings), autofocus,
  screenshot-to-DB
- Gonio/Beamline tabs wired to axis IP video via VideoThread
- precondition gate (ring/shutter/hutch + 1h snooze) on scans; hutch-PSS mount
  block; sample-missing modal

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 16:51:54 +02:00

96 lines
3.3 KiB
Python

"""Compact sample-camera controls: settings (gain/exposure), autofocus, screenshot."""
from __future__ import annotations
from PySide6.QtCore import Qt, Signal
from PySide6.QtWidgets import (
QDialog,
QDialogButtonBox,
QDoubleSpinBox,
QFormLayout,
QHBoxLayout,
QLineEdit,
QPushButton,
QWidget,
)
from aare.common.models import AutofocusSettings, SampleCameraSettings
from aare.gui.new_gui.theme import Palette
class CameraControls(QWidget):
"""Lives in the camera tab strip. Emits backend-ready models."""
samcam_changed = Signal(object) # SampleCameraSettings
autofocus_requested = Signal(object) # AutofocusSettings
screenshot_requested = Signal(str, str) # filename, message
def __init__(self, palette: Palette, parent=None):
super().__init__(parent)
self._p = palette
lay = QHBoxLayout(self)
lay.setContentsMargins(0, 0, 0, 0)
lay.setSpacing(6)
for text, tip, slot in (
("⚙", "Camera settings (gain / exposure)", self._open_settings),
("AF", "Autofocus", self._on_autofocus),
("◉", "Screenshot to database", self._open_screenshot),
):
b = QPushButton(text)
b.setCursor(Qt.PointingHandCursor)
b.setToolTip(tip)
b.setFixedHeight(26)
b.setStyleSheet(
f"QPushButton {{ background:{palette.surface};"
f" border:1px solid {palette.border_control}; border-radius:6px;"
f" padding:3px 10px; font-size:12px; color:{palette.text_secondary}; }}"
)
b.clicked.connect(slot)
lay.addWidget(b)
def _open_settings(self) -> None:
dlg = QDialog(self)
dlg.setWindowTitle("Camera settings")
form = QFormLayout(dlg)
gain = QDoubleSpinBox()
gain.setRange(0, 1000)
gain.setValue(1.0)
exposure = QDoubleSpinBox()
exposure.setRange(0.0, 1.0)
exposure.setDecimals(4)
exposure.setSingleStep(0.001)
exposure.setValue(0.02)
form.addRow("Gain", gain)
form.addRow("Exposure (s)", exposure)
bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
form.addRow(bb)
bb.accepted.connect(dlg.accept)
bb.rejected.connect(dlg.reject)
if dlg.exec() == QDialog.Accepted:
self.samcam_changed.emit(
SampleCameraSettings(gain=gain.value(), exposure=exposure.value())
)
def _on_autofocus(self) -> None:
self.autofocus_requested.emit(
AutofocusSettings(center_x_pxl=None, center_y_pxl=None,
radius_pxl=30, z_range_um=2000, z_steps=10)
)
def _open_screenshot(self) -> None:
dlg = QDialog(self)
dlg.setWindowTitle("Screenshot to database")
form = QFormLayout(dlg)
fn = QLineEdit()
fn.setPlaceholderText("optional")
msg = QLineEdit()
msg.setPlaceholderText("optional")
form.addRow("Filename", fn)
form.addRow("Message", msg)
bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
form.addRow(bb)
bb.accepted.connect(dlg.accept)
bb.rejected.connect(dlg.reject)
if dlg.exec() == QDialog.Accepted:
self.screenshot_requested.emit(fn.text(), msg.text())