diff --git a/bec_widgets/widgets/plots/plot_base.py b/bec_widgets/widgets/plots/plot_base.py index c6f2c56c..772c9abb 100644 --- a/bec_widgets/widgets/plots/plot_base.py +++ b/bec_widgets/widgets/plots/plot_base.py @@ -623,7 +623,9 @@ class PlotBase(BECWidget, QWidget): raise TypeError("Limits must be numbers.") if min_val > max_val: raise ValueError("Minimum limit cannot be greater than maximum limit.") - return QPoint(*tuple) + # QPointF, not QPoint: QPoint truncates to int and silently discarded the + # fractional part of every range set from a tuple (v_range, x/y limits). + return QPointF(*tuple) ################################################################################ # X limits, has to be SaveProperty("QPointF") because of the tuple conversion for designer, diff --git a/tests/unit_tests/test_image_view_next_gen.py b/tests/unit_tests/test_image_view_next_gen.py index feb11525..7b1878e1 100644 --- a/tests/unit_tests/test_image_view_next_gen.py +++ b/tests/unit_tests/test_image_view_next_gen.py @@ -100,6 +100,30 @@ def test_set_vrange(qtbot, mocked_client): assert bec_image_view.main_image.config.v_range == (10, 100) +@pytest.mark.parametrize("colorbar_type", [None, "simple", "full"]) +def test_set_vrange_keeps_fractional_values(qtbot, mocked_client, colorbar_type): + """Regression: tuple ranges went through QPoint, which truncates to int, so + e.g. v_max = 10.5 could never produce a non-integer level.""" + bec_image_view = create_widget(qtbot, Image, client=mocked_client) + bec_image_view.main_image.set_data(np.arange(100, dtype=float).reshape(10, 10)) + if colorbar_type is not None: + bec_image_view.enable_colorbar(True, colorbar_type) + + bec_image_view.v_range = (0.25, 10.5) + assert bec_image_view.v_range == QPointF(0.25, 10.5) + assert bec_image_view.main_image.v_range == (0.25, 10.5) + + bec_image_view.v_max = 42.5 + assert bec_image_view.main_image.v_range == (0.25, 42.5) + bec_image_view.v_min = 0.75 + assert bec_image_view.main_image.v_range == (0.75, 42.5) + + if colorbar_type == "simple": + assert bec_image_view._color_bar.levels() == (0.75, 42.5) + elif colorbar_type == "full": + assert tuple(bec_image_view._color_bar.getLevels()) == (0.75, 42.5) + + def test_enable_simple_colorbar(qtbot, mocked_client): bec_image_view = create_widget(qtbot, Image, client=mocked_client) bec_image_view.enable_simple_colorbar = True @@ -142,11 +166,12 @@ def test_colorbar_menu_set_levels_updates_vrange(qtbot, mocked_client): bec_image_view.enable_full_colorbar = True assert bec_image_view.autorange is True - # Simulate the "Set levels…" context-menu action (image scaling via the region). - bec_image_view._color_bar.sigColorLevelsChangeRequested.emit((0, 50)) + # Simulate the "Set levels…" context-menu action (image scaling via the + # region); fractional values must survive (regression: QPoint truncation). + bec_image_view._color_bar.sigColorLevelsChangeRequested.emit((0.5, 50.5)) - assert bec_image_view.v_range == QPointF(0, 50) - assert bec_image_view.main_image.levels == (0, 50) + assert bec_image_view.v_range == QPointF(0.5, 50.5) + assert bec_image_view.main_image.levels == (0.5, 50.5) # Setting explicit levels disables autorange. assert bec_image_view.autorange is False diff --git a/tests/unit_tests/test_plot_base_next_gen.py b/tests/unit_tests/test_plot_base_next_gen.py index fa68222d..cadd5083 100644 --- a/tests/unit_tests/test_plot_base_next_gen.py +++ b/tests/unit_tests/test_plot_base_next_gen.py @@ -528,3 +528,13 @@ def test_minimal_crosshair_precision_after_hook(qtbot, mocked_client): assert sig.args == ["minimal_crosshair_precision", 1] assert pb.crosshair.min_precision == 1 + + +def test_limits_accept_fractional_values(qtbot, mocked_client): + """Regression: tuple limits were converted through QPoint, silently truncating + fractional values to integers.""" + pb = create_widget(qtbot, PlotBase, client=mocked_client) + pb.x_limits = (0.5, 9.25) + pb.y_limits = (-1.75, 3.5) + assert (pb.x_limits.x(), pb.x_limits.y()) == (0.5, 9.25) + assert (pb.y_limits.x(), pb.y_limits.y()) == (-1.75, 3.5)