diff --git a/bec_widgets/widgets/plots/image/image_base.py b/bec_widgets/widgets/plots/image/image_base.py index ba8345df..493e9992 100644 --- a/bec_widgets/widgets/plots/image/image_base.py +++ b/bec_widgets/widgets/plots/image/image_base.py @@ -753,6 +753,12 @@ class ImageBase(PlotBase): live curves; ignored for the pinned curves. pinned(bool): Update the pinned reference curves instead of the live ones. """ + image_item = self.layer_manager["main"].image + image = image_item.image + if image is not None and image.ndim != 2: + self.clear_image_slices() + return + if pinned: if self._pinned_slice_coords is None: return @@ -770,7 +776,6 @@ class ImageBase(PlotBase): else: return - image_item = self.layer_manager["main"].image slices = self._compute_image_slices(image_item, row, col) if slices is None: return @@ -815,10 +820,10 @@ class ImageBase(PlotBase): Returns: tuple | None: ``(h_world_x, h_slice, v_world_y, v_slice)`` in world coordinates, - or ``None`` if there is no image or the pixel is out of bounds. + or ``None`` if the image is not scalar 2-D or the pixel is out of bounds. """ image = image_item.image - if image is None: + if image is None or image.ndim != 2: return None max_row, max_col = image.shape[0] - 1, image.shape[1] - 1 if not (0 <= row <= max_row and 0 <= col <= max_col): @@ -843,6 +848,17 @@ class ImageBase(PlotBase): ] return h_world_x, h_slice, v_world_y, v_slice + @SafeSlot() + def clear_image_slices(self): + """Remove live and pinned profile curves for an unsupported image.""" + if self.x_roi_curve is not None: + self.x_roi.plot_item.removeItem(self.x_roi_curve) + self.x_roi_curve = None + if self.y_roi_curve is not None: + self.y_roi.plot_item.removeItem(self.y_roi_curve) + self.y_roi_curve = None + self.clear_pinned_slices() + @SafeSlot(tuple) def pin_image_slices(self, _coordinates: tuple): """Create or update the pinned profile curves at the pinned pixel. diff --git a/tests/unit_tests/test_image_view_next_gen.py b/tests/unit_tests/test_image_view_next_gen.py index e7920156..02c8a277 100644 --- a/tests/unit_tests/test_image_view_next_gen.py +++ b/tests/unit_tests/test_image_view_next_gen.py @@ -1160,6 +1160,33 @@ def test_roi_plot_data_from_image(qtbot, mocked_client): np.testing.assert_array_equal(h_slice, test_data[2]) +def test_roi_plots_ignore_rgb_images_and_clear_stale_curves(qtbot, mocked_client): + """RGB images have vector-valued pixels and cannot produce scalar profile curves.""" + bec_image_view = create_widget(qtbot, Image, client=mocked_client) + scalar_image = np.arange(25).reshape(5, 5) + bec_image_view.on_image_update_2d({"data": scalar_image}, {}) + + switch = bec_image_view.toolbar.components.get_action("image_switch_crosshair") + switch.actions["crosshair_roi"].action.trigger() + bec_image_view.update_image_slices((0, 2, 3)) + bec_image_view.crosshair.set_pin(2.0, 3.0) + assert bec_image_view.x_roi_curve is not None + assert bec_image_view.y_roi_curve is not None + assert bec_image_view.x_roi_pinned is not None + assert bec_image_view.y_roi_pinned is not None + + rgb_image = np.zeros((5, 5, 3), dtype=np.uint8) + bec_image_view.on_image_update_2d({"data": rgb_image}, {}) + + assert bec_image_view._compute_image_slices(bec_image_view.main_image, 2, 3) is None + assert bec_image_view.x_roi_curve is None + assert bec_image_view.y_roi_curve is None + assert bec_image_view.x_roi_pinned is None + assert bec_image_view.y_roi_pinned is None + assert bec_image_view.x_roi.plot_item.listDataItems() == [] + assert bec_image_view.y_roi.plot_item.listDataItems() == [] + + def test_pinned_roi_profiles_freeze_and_clear(qtbot, mocked_client): """Clicking pins a frozen copy of the X/Y profiles that survives live updates.""" import numpy as np