feat(crosshair): keep the pin across data/scan resets

This commit is contained in:
2026-07-10 14:48:01 +02:00
committed by Jan Wyzula
parent 34e432e0ed
commit 41c01e6612
3 changed files with 42 additions and 3 deletions
+7 -2
View File
@@ -865,18 +865,23 @@ class Crosshair(QObject):
@SafeSlot()
def reset(self):
"""Resets the crosshair to its initial state."""
"""Reset the transient crosshair markers (e.g. on new data / a new scan).
The pinned marker is intentionally *not* cleared here: it is a persistent
user annotation that survives data and scan changes. Use :meth:`clear_pin`
for an explicit removal and :meth:`cleanup` for full teardown.
"""
if self.marker_2d_row is not None:
self.plot_item.removeItem(self.marker_2d_row)
self.marker_2d_row = None
if self.marker_2d_col is not None:
self.plot_item.removeItem(self.marker_2d_col)
self.marker_2d_col = None
self.clear_pin()
self.clear_markers()
def cleanup(self):
self.reset()
self.clear_pin()
self.plot_item.removeItem(self.v_line)
self.plot_item.removeItem(self.h_line)
self.plot_item.removeItem(self.coord_label)
+8 -1
View File
@@ -573,10 +573,17 @@ def test_set_pin_2d_emits_pixel_coordinates(image_widget_with_crosshair):
assert (px, py) == (40, 60)
def test_reset_clears_pin(image_widget_with_crosshair):
def test_reset_preserves_pin_cleanup_removes_it(image_widget_with_crosshair):
crosshair, _ = image_widget_with_crosshair
crosshair.set_pin(40, 60)
assert crosshair.pinned_point is not None
# A data/scan reset keeps the pin (a persistent annotation).
crosshair.reset()
assert crosshair.pinned_point is not None
assert crosshair.pinned_pos is not None
# Full teardown removes it.
crosshair.cleanup()
assert crosshair.pinned_point is None
assert crosshair.pinned_pos is None
@@ -1070,6 +1070,31 @@ def test_pinned_roi_profiles_keep_style_on_theme_change(qtbot, mocked_client):
assert bec_image_view.x_roi_curve.opts["pen"].color().name() == "#000000"
def test_pin_survives_scan_reset(qtbot, mocked_client):
"""A scan transition (crosshair.reset()) must not wipe the pin or its frozen profiles."""
import numpy as np
bec_image_view = create_widget(qtbot, Image, client=mocked_client)
bec_image_view.on_image_update_2d({"data": np.arange(25).reshape(5, 5)}, {})
switch = bec_image_view.toolbar.components.get_action("image_switch_crosshair")
switch.actions["crosshair_roi"].action.trigger()
qtbot.wait(50)
bec_image_view.crosshair.set_pin(2.0, 3.0)
assert bec_image_view.x_roi_pinned is not None
pin_point = bec_image_view.crosshair.pinned_point
assert pin_point is not None
# Image._handle_scan_change calls crosshair.reset() on each new scan id.
bec_image_view.crosshair.reset()
# The pin marker and the frozen reference profiles must still be there.
assert bec_image_view.crosshair.pinned_point is pin_point
assert bec_image_view.crosshair.pinned_pos is not None
assert bec_image_view.x_roi_pinned is not None
assert bec_image_view.y_roi_pinned is not None
def test_detached_pin_can_be_removed_with_right_click(qtbot, mocked_client, monkeypatch):
"""A pin left on the plot after disabling crosshair remains removable."""
from qtpy.QtWidgets import QMenu
@@ -1100,6 +1125,8 @@ def test_detached_pin_can_be_removed_with_right_click(qtbot, mocked_client, monk
assert cleared == [True]
##############################################
# Device selection toolbar sync
##############################################