diff --git a/src/aare/gui/widgets/camera_image.py b/src/aare/gui/widgets/camera_image.py index dc3b42b2..74565ad5 100644 --- a/src/aare/gui/widgets/camera_image.py +++ b/src/aare/gui/widgets/camera_image.py @@ -56,6 +56,7 @@ from aare.gui.styles import ( SCALE_BAR_GREY, SHADOW, TARGET_COLORS, + TEXT, THEME_SUNSET, TOOLTIP_TEXT, WHITE, @@ -139,6 +140,9 @@ class SampleCameraImageLabel(QGraphicsView): # toggle can still switch back to 1:1. self._autoscale = True self._show_coords = False + # Full-frame crosshair through the beam center; off by default so the + # default view stays as before, toggled from the right-click menu. + self._show_beam_crosshair = False self._helical_start = SmargonCoordinate() self._helical_end = SmargonCoordinate() self._raster_alpha = 127 @@ -682,6 +686,9 @@ class SampleCameraImageLabel(QGraphicsView): autofocus_action = menu.addAction("Auto-focus") beam_mark_action = menu.addAction("Mark beam center") + crosshair_action = menu.addAction("Show beam crosshair") + crosshair_action.setCheckable(True) + crosshair_action.setChecked(self._show_beam_crosshair) delete_action = None evaluate_action = None @@ -730,6 +737,8 @@ class SampleCameraImageLabel(QGraphicsView): elif action == beam_mark_action: c = self.mapToScene(event.pos()) self.update_beam_mark.emit(c.x(), c.y()) + elif action == crosshair_action: + self._show_beam_crosshair = not self._show_beam_crosshair self.update() def _screenshot_with_dialog(self, overlay: bool): @@ -1278,10 +1287,11 @@ class SampleCameraImageLabel(QGraphicsView): return bar_um, label def _mounted_hud_ink(self) -> tuple[str, str]: - """(text, shadow) for the mounted-sample HUD: black on a white halo in - the light themes, white on black in Sunset. The camera image behind - is arbitrary, so the pair follows the theme rather than the pixels.""" - return (WHITE, SHADOW) if self._dark_theme else (SHADOW, WHITE) + """(text, shadow) for the mounted-sample HUD: theme text ink on a white + halo in the light themes (pure black read too harsh over the camera), + white on black in Sunset. The camera image behind is arbitrary, so + the pair follows the theme rather than the pixels.""" + return (WHITE, SHADOW) if self._dark_theme else (TEXT, WHITE) def _draw_mounted_sample(self, painter: QPainter): # Top-left HUD line (legend sits bottom-left, scale bar bottom-right): @@ -1446,6 +1456,13 @@ class SampleCameraImageLabel(QGraphicsView): int(beam_size_pxl.y), ) ) + if self._show_beam_crosshair: + # Anuschka and Katherine really want a crosshair at 10s + arm = 33 + cx = int(self._geom.beam_location_pxl.x) + cy = int(self._geom.beam_location_pxl.y) + painter.drawLine(cx - arm, cy, cx + arm, cy) + painter.drawLine(cx, cy - arm, cx, cy + arm) @staticmethod def _draw_circle(painter, coord: Coordinate, color: QColor, radius=10): diff --git a/tests/unit/gui/test_camera_image.py b/tests/unit/gui/test_camera_image.py index 51ff331a..e94814bf 100644 --- a/tests/unit/gui/test_camera_image.py +++ b/tests/unit/gui/test_camera_image.py @@ -327,10 +327,34 @@ def test_mounted_sample_hud_follows_status(camera): def test_mounted_hud_ink_follows_theme(camera): - """Mounted-sample HUD: dark ink + white halo on light themes, inverted on Sunset.""" - from aare.gui.styles import SHADOW, THEME_SUNRISE, THEME_SUNSET, WHITE + """Mounted-sample HUD: theme text ink + white halo on light themes, inverted on Sunset.""" + from aare.gui.styles import SHADOW, TEXT, THEME_SUNRISE, THEME_SUNSET, WHITE camera.set_theme(THEME_SUNRISE) - assert camera._mounted_hud_ink() == (SHADOW, WHITE) + assert camera._mounted_hud_ink() == (TEXT, WHITE) camera.set_theme(THEME_SUNSET) assert camera._mounted_hud_ink() == (WHITE, SHADOW) + + +def test_beam_crosshair_toggle_draws(camera, monkeypatch): + from PySide6.QtWidgets import QMenu + + assert camera._show_beam_crosshair is False + # Pick the crosshair action out of the real menu instead of exec'ing it. + monkeypatch.setattr( + QMenu, + "exec_", + lambda self, *_: next(a for a in self.actions() if a.text() == "Show beam crosshair"), + ) + camera._camera_interaction_enabled = lambda: True + press = QMouseEvent( + QEvent.Type.MouseButtonPress, + QPointF(10, 10), + QPointF(camera.mapToGlobal(QPoint(10, 10))), + Qt.MouseButton.RightButton, + Qt.MouseButton.RightButton, + Qt.KeyboardModifier.NoModifier, + ) + camera._right_click_menu(press) + assert camera._show_beam_crosshair is True + camera.grab() # exercises the crosshair draw path