From b5e6159118d9d4067b779fc3e43eb4e4c1f9dc84 Mon Sep 17 00:00:00 2001 From: Dawn Date: Fri, 18 Sep 2026 09:14:31 +0200 Subject: [PATCH 1/3] fix(gui): add "Show beam crosshair" toggle to the camera right-click menu Right-clicking the sample camera now offers a checkable "Show beam crosshair" entry. When on, two full-frame lines through the beam center are drawn with the same pen as the beam box, so the crosshair follows the beam state color. Off by default so the view stays as before. Co-Authored-By: Claude Fable 5.1 --- src/aare/gui/widgets/camera_image.py | 15 +++++++++++++++ tests/unit/gui/test_camera_image.py | 24 ++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/src/aare/gui/widgets/camera_image.py b/src/aare/gui/widgets/camera_image.py index dc3b42b2..93c820b9 100644 --- a/src/aare/gui/widgets/camera_image.py +++ b/src/aare/gui/widgets/camera_image.py @@ -139,6 +139,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 +685,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 +736,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): @@ -1446,6 +1454,13 @@ class SampleCameraImageLabel(QGraphicsView): int(beam_size_pxl.y), ) ) + if self._show_beam_crosshair: + # Same pen as the box so the crosshair follows the beam state color. + r = self.sceneRect() + cx = int(self._geom.beam_location_pxl.x) + cy = int(self._geom.beam_location_pxl.y) + painter.drawLine(int(r.left()), cy, int(r.right()), cy) + painter.drawLine(cx, int(r.top()), cx, int(r.bottom())) @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..428129ba 100644 --- a/tests/unit/gui/test_camera_image.py +++ b/tests/unit/gui/test_camera_image.py @@ -334,3 +334,27 @@ def test_mounted_hud_ink_follows_theme(camera): assert camera._mounted_hud_ink() == (SHADOW, 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 -- 2.54.0 From 3f404af06942053a2b45600cb2f36ac9bdfc883e Mon Sep 17 00:00:00 2001 From: duan_j Date: Fri, 18 Sep 2026 15:09:09 +0200 Subject: [PATCH 2/3] style: have the cross hair not too long, just a small cross Anuschka and Katherine really need it --- src/aare/gui/widgets/camera_image.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/aare/gui/widgets/camera_image.py b/src/aare/gui/widgets/camera_image.py index 93c820b9..bd6d417b 100644 --- a/src/aare/gui/widgets/camera_image.py +++ b/src/aare/gui/widgets/camera_image.py @@ -1455,12 +1455,12 @@ class SampleCameraImageLabel(QGraphicsView): ) ) if self._show_beam_crosshair: - # Same pen as the box so the crosshair follows the beam state color. - r = self.sceneRect() + # 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(int(r.left()), cy, int(r.right()), cy) - painter.drawLine(cx, int(r.top()), cx, int(r.bottom())) + 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): -- 2.54.0 From 79bb351437b6db06369ca2388a8663e36ded850b Mon Sep 17 00:00:00 2001 From: Dawn Date: Fri, 18 Sep 2026 16:35:05 +0200 Subject: [PATCH 3/3] style(gui): soften the mounted-sample HUD ink on light themes Pure black (SHADOW) over the camera image read too harsh; use the theme text ink (TEXT) with the same white halo instead. Sunset keeps white on black. Co-Authored-By: Claude Fable 5.1 --- src/aare/gui/widgets/camera_image.py | 10 ++++++---- tests/unit/gui/test_camera_image.py | 6 +++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/aare/gui/widgets/camera_image.py b/src/aare/gui/widgets/camera_image.py index bd6d417b..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, @@ -1286,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): diff --git a/tests/unit/gui/test_camera_image.py b/tests/unit/gui/test_camera_image.py index 428129ba..e94814bf 100644 --- a/tests/unit/gui/test_camera_image.py +++ b/tests/unit/gui/test_camera_image.py @@ -327,11 +327,11 @@ 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) -- 2.54.0