import pytest from aarecommon.math.coordinate import Coordinate, SmargonCoordinate from aarecommon.math.diffraction_geometry import DiffractionGeometry from aarecommon.math.sample_geometry import SampleGeometryModel from aarecommon.models.models import ( BeamlineStateEnum, BeamlineStatus, CrystalSize, DAQStatusModel, SampleCameraSettings, SessionsStateEnum, SessionStatus, ) from PySide6.QtCore import QEvent, QPoint, QPointF, Qt from PySide6.QtGui import QMouseEvent from aare.gui.scan_logic.raster_grid_manager import RasterGridManager from aare.gui.styles import THEME_SUNRISE, THEME_SUNSET from aare.gui.widgets.camera_image import SampleCameraImageLabel def _geom() -> SampleGeometryModel: return SampleGeometryModel( beam_location_pxl=Coordinate(x=1000, y=1000), pixel_in_mm=0.001, aerotech=Coordinate(), aerotech_meas=Coordinate(), smargon=SmargonCoordinate(sh_mm=Coordinate(), phi_deg=0, chi_deg=0), omega_deg=0, beam_size_mm=Coordinate(x=0.01, y=0.01), ) def _status(*, busy: bool, session: SessionsStateEnum) -> DAQStatusModel: return DAQStatusModel( geom=_geom(), diffraction=DiffractionGeometry( energy_keV=12.4, dtz_mm=100.0, detector_size_pxl=(1553, 1630), pixel_size_mm=0.150, beam_center_pxl=(750.0, 750.0), detector_description="PILATUS 4", detector_serial_number="1", poni_rot1_rad=0.0, poni_rot2_rad=0.0, ), bl=BeamlineStatus( name="SIMULATED", ring_current_mA=400.0, front_light=50.0, back_light=50.0, cryojet_K=100.0, shutter_open=False, exp_shutter_open=False, flux_ph_s=1e12, sample_camera=SampleCameraSettings(gain=1.0, exposure=0.02), transmission=1.0, zoom=1.0, commissioning_mode=False, dtz_min=120.0, dtz_max=1600.0, ), state=BeamlineStateEnum.SampleAlignment, busy=busy, session=SessionStatus(session=session, current_pgroup="p123", staff=True), crystal_size=CrystalSize(x=0, y=0, z=0), ) def _mouse_move(widget, pos: QPoint) -> None: # qtbot.mouseMove drives the real cursor, which the offscreen platform # ignores — deliver the move event directly instead. event = QMouseEvent( QEvent.Type.MouseMove, QPointF(pos), QPointF(widget.mapToGlobal(pos)), Qt.MouseButton.NoButton, Qt.MouseButton.NoButton, Qt.KeyboardModifier.NoModifier, ) widget.mouseMoveEvent(event) @pytest.fixture def camera(qtbot): geom = _geom() label = SampleCameraImageLabel(geom=geom, raster=RasterGridManager(geom), default_image=None) qtbot.addWidget(label) label.resize(800, 600) return label def test_help_badge_click_toggles_cheatsheet(camera, qtbot): camera.grab() # paint records the collapsed "?" badge hit rect badge = camera._help_hit_rect assert badge is not None assert not camera._help_expanded qtbot.mouseClick(camera.viewport(), Qt.MouseButton.LeftButton, pos=badge.center().toPoint()) assert camera._help_expanded camera.grab() # expanded overlay: hit rect grows to the whole cheatsheet box box = camera._help_hit_rect assert box is not None assert box.height() > badge.height() qtbot.mouseClick(camera.viewport(), Qt.MouseButton.LeftButton, pos=box.center().toPoint()) assert not camera._help_expanded def test_camera_error_message_rewords_and_draws(camera): camera.set_camera_available(False) camera.set_camera_error_message("Sample camera feed unavailable: cable unplugged") assert camera._camera_error_message == ( "Sample camera feed unavailable because cable unplugged" ) camera.grab() # exercises the bottom-center unavailable overlay text path camera.set_camera_available(True) assert camera._camera_error_message is None def test_busy_warning_is_not_a_click_target(camera): camera.update_daq_status(_status(busy=True, session=SessionsStateEnum.OwnedByYou)) style = camera._busy_overlay_style assert style is not None assert style.text == "BEAMLINE BUSY" camera.grab() assert camera._session_badge_rect is None def test_vacant_badge_hover_click_and_theme(camera, qtbot): camera.update_daq_status(_status(busy=False, session=SessionsStateEnum.Vacant)) style = camera._busy_overlay_style assert style is not None assert style.text == "In viewing mode" assert style.subtext # the grab-baton hint line camera.grab() # paint records the badge rect badge = camera._session_badge_rect assert badge is not None _mouse_move(camera, badge.center()) assert camera._session_badge_hovered camera.grab() # hover fill, light-theme darken branch camera.set_theme(THEME_SUNSET) assert camera._dark_theme camera.grab() # hover fill, sunset brighten branch camera.set_theme(THEME_SUNRISE) assert not camera._dark_theme _mouse_move(camera, QPoint(1, 1)) assert not camera._session_badge_hovered _mouse_move(camera, badge.center()) camera.leaveEvent(QEvent(QEvent.Type.Leave)) assert not camera._session_badge_hovered with qtbot.waitSignal(camera.session_badge_clicked, timeout=1000): qtbot.mouseClick(camera.viewport(), Qt.MouseButton.LeftButton, pos=badge.center())