From ae87e1b1a08a4ee14bb44ae6e130cad6033b51c1 Mon Sep 17 00:00:00 2001 From: Chris Meyer Date: Wed, 31 Jan 2024 13:58:41 -0800 Subject: [PATCH] Fix #253. Fix significant digit handling in display limits. --- nion/swift/Inspector.py | 48 +++++++------------------------ nion/swift/resources/changes.json | 4 +++ nion/swift/test/Inspector_test.py | 8 +++--- 3 files changed, 19 insertions(+), 41 deletions(-) diff --git a/nion/swift/Inspector.py b/nion/swift/Inspector.py index c05b4f579..ef842ed22 100755 --- a/nion/swift/Inspector.py +++ b/nion/swift/Inspector.py @@ -1097,8 +1097,8 @@ def __init__(self, document_controller: DocumentController.DocumentController, d self.display_limits_range_row = ui.create_row_widget() self.display_limits_range_low = ui.create_label_widget(properties={"width": 80}) self.display_limits_range_high = ui.create_label_widget(properties={"width": 80}) - float_point_2_converter = Converter.FloatToStringConverter(format="{0:#.5g}") - float_point_2_none_converter = Converter.FloatToStringConverter(format="{0:#.5g}", pass_none=True) + float_point_2_converter = BetterFloatToStringConverter() + float_point_2_none_converter = BetterFloatToStringConverter(pass_none=True) self.display_limits_range_low.bind_text(Binding.TuplePropertyBinding(self.__data_range_model, "value", 0, converter=float_point_2_converter, fallback=_("N/A"))) self.display_limits_range_high.bind_text(Binding.TuplePropertyBinding(self.__data_range_model, "value", 1, converter=float_point_2_converter, fallback=_("N/A"))) self.display_limits_range_row.add(ui.create_label_widget(_("Data Range:"), properties={"width": 120})) @@ -1400,42 +1400,10 @@ def convert_back(self, value: typing.Optional[int]) -> typing.Optional[str]: return widget -def make_calibration_row_widget(ui: UserInterface.UserInterface, data_item: DataItem.DataItem, - calibration_observable: Observable.Observable, - label: typing.Optional[str] = None) -> InspectorSectionWidget: - """Called when an item (calibration_observable) is inserted into the list widget. Returns a widget.""" - widget = InspectorSectionWidget(ui) - calibration_row = ui.create_row_widget() - row_label = ui.create_label_widget(label, properties={"width": 60}) - row_label.widget_id = "label" - offset_field = ui.create_line_edit_widget(properties={"width": 60}) - offset_field.widget_id = "offset" - scale_field = ui.create_line_edit_widget(properties={"width": 60}) - scale_field.widget_id = "scale" - units_field = ui.create_line_edit_widget(properties={"width": 60}) - units_field.widget_id = "units" - float_point_4_converter = Converter.FloatToStringConverter(format="{0:.4f}") - offset_field.bind_text(Binding.PropertyBinding(calibration_observable, "offset", converter=float_point_4_converter)) - scale_field.bind_text(Binding.PropertyBinding(calibration_observable, "scale", converter=float_point_4_converter)) - units_field.bind_text(Binding.PropertyBinding(calibration_observable, "units")) - # notice the binding of calibration_index below. - calibration_row.add(row_label) - calibration_row.add_spacing(12) - calibration_row.add(offset_field) - calibration_row.add_spacing(12) - calibration_row.add(scale_field) - calibration_row.add_spacing(12) - calibration_row.add(units_field) - calibration_row.add_stretch() - widget.add(calibration_row) - widget.add_unbinder([data_item], [offset_field.unbind_text, scale_field.unbind_text, units_field.unbind_text]) - return widget - - class BetterFloatToStringConverter(Converter.FloatToStringConverter): def __init__(self, *, pass_none: bool = False) -> None: super().__init__(pass_none=pass_none) - self.__pass_none = False + self.__pass_none = pass_none def convert(self, value: typing.Optional[float]) -> typing.Optional[str]: if value is None: @@ -1445,7 +1413,13 @@ def convert(self, value: typing.Optional[float]) -> typing.Optional[str]: if mag < 0: return "{0:0.4g}".format(value) elif mag > 5: - return "{0:0.1e}".format(value) + result = "{0:0.3e}".format(value) + while not ".0e" in result: + last_result = result + result = result.replace("0e", "e") + if last_result == result: + break + return result else: result = "{0:.4f}".format(value) while result.endswith("0") and not result.endswith(".0"): @@ -2096,7 +2070,7 @@ def __init__(self, document_controller: DocumentController.DocumentController, d # display type display_type_row, self.__display_type_changed_listener = make_display_type_chooser(document_controller, display_item) - float_point_2_none_converter = Converter.FloatToStringConverter(format="{0:#.5g}", pass_none=True) + float_point_2_none_converter = BetterFloatToStringConverter(pass_none=True) self.display_limits_limit_row = self.ui.create_row_widget() self.display_limits_limit_low = self.ui.create_line_edit_widget(properties={"width": 80}) diff --git a/nion/swift/resources/changes.json b/nion/swift/resources/changes.json index 4e1c4c24f..35cc94a7e 100644 --- a/nion/swift/resources/changes.json +++ b/nion/swift/resources/changes.json @@ -2,6 +2,10 @@ { "version": "UNRELEASED", "notes": [ + { + "issues": ["https://github.com/nion-software/nionswift/issues/253"], + "summary": "Improve handling of number precision for inspector calibrations and display limits." + }, { "issues": ["https://github.com/nion-software/nionswift/issues/998"], "summary": "Fix issues with line plot when dimensional calibrations are invalid. Fall back to pixel calibration." diff --git a/nion/swift/test/Inspector_test.py b/nion/swift/test/Inspector_test.py index 21a0b2d2a..c89f9b164 100755 --- a/nion/swift/test/Inspector_test.py +++ b/nion/swift/test/Inspector_test.py @@ -581,16 +581,16 @@ def test_image_display_inspector_shows_empty_fields_for_none_display_limits(self self.assertEqual(inspector_section.display_limits_limit_high.text, None) display_data_channel.display_limits = (1, None) document_controller.periodic() # needed to update the inspector - self.assertEqual(inspector_section.display_limits_limit_low.text, "1.0000") + self.assertEqual(inspector_section.display_limits_limit_low.text, "1.0") self.assertEqual(inspector_section.display_limits_limit_high.text, None) display_data_channel.display_limits = (None, 2) document_controller.periodic() # needed to update the inspector self.assertEqual(inspector_section.display_limits_limit_low.text, None) - self.assertEqual(inspector_section.display_limits_limit_high.text, "2.0000") + self.assertEqual(inspector_section.display_limits_limit_high.text, "2.0") display_data_channel.display_limits = (1, 2) document_controller.periodic() # needed to update the inspector - self.assertEqual(inspector_section.display_limits_limit_low.text, "1.0000") - self.assertEqual(inspector_section.display_limits_limit_high.text, "2.0000") + self.assertEqual(inspector_section.display_limits_limit_low.text, "1.0") + self.assertEqual(inspector_section.display_limits_limit_high.text, "2.0") def test_image_display_inspector_sets_display_limits_when_text_is_changed(self): with TestContext.create_memory_context() as test_context: