- 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>
96 lines
3.3 KiB
Python
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())
|