The panels kept the effective detector distance, resolution and transmission in their own attributes, and a sample change overwrote those from the spreadsheet whatever the Database/User toggle said. The fields went on showing the user's numbers while the scan request carried the database ones, so a value had to be re-entered after every mount. The fields are now the only place a value lives. Each setting is worth, in order: what the user typed while "User values" is selected, what the mounted sample asks for, the panel default - one rule, ScanSettingsPanel ._setting, that each panel spells out a line at a time. Overrides are per setting, so a setting the user did not touch keeps following the sample. Along the way: * DbOverrideLineEdit is gone; the panels use NumberLineEdit directly, and the per-widget database/user bookkeeping is replaced by SampleParameters, the one place that knows the aareDB spreadsheet column names. * The (name, widget, converter) mapping loops with getattr/hasattr are replaced by explicit per-field commit slots. * The omega speed cap no longer drives one field's validator from another field's value: it is a read-out plus a check on the pair at Run time. * _add_row/_add_pair_row build the label/field/unit rows, which also puts the Total angle degree sign on its own row instead of the header above. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JHoUkj66jxByS2ypY5h9Mn
104 lines
3.4 KiB
Python
104 lines
3.4 KiB
Python
"""Omega speed cap (500 deg/s) and per-beamline min image time on the rotation panel.
|
|
|
|
Why: the image angle and the image time are each fine on their own - it is the
|
|
pair that can ask the goniometer to turn too fast. So the cap is a read-out
|
|
plus a gate on the Run button, and only the per-beamline minimum image time is
|
|
a limit of the field itself.
|
|
"""
|
|
|
|
import pytest
|
|
from aarecommon.math.diffraction_geometry import DiffractionGeometry
|
|
from aarecommon.models.models import BeamlineStateEnum
|
|
from PySide6.QtWidgets import QMessageBox
|
|
|
|
from aare.gui.panels.rotation_data_collection import RotationDataCollectionPanel
|
|
|
|
|
|
def _panel(monkeypatch, beamline):
|
|
# mx_beamline() reads the env on every call, so set it before construction
|
|
monkeypatch.setenv("BEAMLINE", beamline)
|
|
diffraction = DiffractionGeometry(
|
|
energy_keV=12.0,
|
|
dtz_mm=150.0,
|
|
pixel_size_mm=0.075,
|
|
beam_center_pxl=(1000.0, 1000.0),
|
|
detector_size_pxl=(2000, 2000),
|
|
detector_description="Eiger 16M",
|
|
detector_serial_number="123",
|
|
poni_rot1_rad=0.0,
|
|
poni_rot2_rad=0.0,
|
|
)
|
|
return RotationDataCollectionPanel(diffraction=diffraction)
|
|
|
|
|
|
def _runnable(panel, monkeypatch):
|
|
panel._beamline_state = BeamlineStateEnum.SampleAlignment
|
|
monkeypatch.setattr(panel, "check_before_run", lambda scan_kind: True)
|
|
return panel
|
|
|
|
|
|
def _commit(field, text):
|
|
field.setText(text)
|
|
field.on_editing_finished()
|
|
|
|
|
|
def _accepts(field, text) -> bool:
|
|
field.setText(text)
|
|
return field.hasAcceptableInput()
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"beamline,too_short,ok", [("X06DA", "0.0010", "0.0012"), ("X10SA", "0.0050", "0.0090")]
|
|
)
|
|
def test_min_image_time_per_beamline(qapp, monkeypatch, beamline, too_short, ok):
|
|
panel = _panel(monkeypatch, beamline)
|
|
for field in (panel.image_time_enter, panel.screening_image_time_enter):
|
|
assert not _accepts(field, too_short)
|
|
assert _accepts(field, ok)
|
|
|
|
|
|
def test_speed_readout_follows_the_pair(qapp, monkeypatch):
|
|
panel = _panel(monkeypatch, "X06DA")
|
|
|
|
_commit(panel.image_angle, "1.000")
|
|
_commit(panel.image_time_enter, "0.1000")
|
|
assert panel.omega_speed.text() == "10.0"
|
|
assert not panel.omega_speed.styleSheet() # within the cap
|
|
|
|
_commit(panel.image_time_enter, "0.0012") # 833 deg/s
|
|
assert panel.omega_speed.text() == "833.3"
|
|
assert panel.omega_speed.styleSheet() # marked red
|
|
|
|
|
|
def test_run_blocked_above_the_speed_cap(qapp, monkeypatch):
|
|
panel = _runnable(_panel(monkeypatch, "X06DA"), monkeypatch)
|
|
boxes = []
|
|
monkeypatch.setattr(QMessageBox, "critical", lambda *a, **k: boxes.append(a))
|
|
emitted = []
|
|
panel.rotation_scan.connect(emitted.append)
|
|
|
|
_commit(panel.image_angle, "1.000")
|
|
_commit(panel.image_time_enter, "0.0012") # 833 deg/s, over the cap
|
|
panel.run_measurement()
|
|
assert emitted == []
|
|
assert len(boxes) == 1
|
|
|
|
_commit(panel.image_time_enter, "0.0100") # 100 deg/s
|
|
panel.run_measurement()
|
|
assert len(emitted) == 1
|
|
|
|
|
|
def test_run_blocked_while_field_red(qapp, monkeypatch):
|
|
panel = _panel(monkeypatch, "X06DA")
|
|
boxes = []
|
|
monkeypatch.setattr(QMessageBox, "critical", lambda *a, **k: boxes.append(a))
|
|
emitted = []
|
|
panel.rotation_scan.connect(emitted.append)
|
|
panel._beamline_state = BeamlineStateEnum.SampleAlignment
|
|
|
|
panel.image_time_enter.setText("0.0010") # below 1/900 s, stays red
|
|
panel.run_measurement()
|
|
|
|
assert emitted == []
|
|
assert len(boxes) == 1
|