From 7db8ba91639d14d0e5a11f5d1f74c82c7f479707 Mon Sep 17 00:00:00 2001 From: YooSunyoung Date: Wed, 11 Dec 2024 14:43:25 +0100 Subject: [PATCH 1/2] Switchable fields set/getter. --- src/ess/reduce/widgets/_switchable_widget.py | 36 ++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/ess/reduce/widgets/_switchable_widget.py b/src/ess/reduce/widgets/_switchable_widget.py index 2231d16f..3aa2cd4a 100644 --- a/src/ess/reduce/widgets/_switchable_widget.py +++ b/src/ess/reduce/widgets/_switchable_widget.py @@ -4,6 +4,7 @@ from ipywidgets import Checkbox, HBox, Label, Stack, Widget +from ._base import WidgetWithFieldsProtocol, get_fields, set_fields from ._config import default_style @@ -49,3 +50,38 @@ def value(self) -> Any: @value.setter def value(self, value: Any) -> None: self.wrapped.value = value + + def set_fields(self, new_values: dict[str, Any]) -> None: + # Check if the new values are a dictionary + if not isinstance(new_values, dict): + raise ValueError(f"Expected a dictionary, got {new_values}.") + # Retrieve and set the enabled flag first + new_values = dict(new_values) + enabled_flag = new_values.pop('enabled', self.enabled) + if not isinstance(enabled_flag, bool): + raise ValueError(f"`enabled` must be a boolean, got {enabled_flag}") + self.enabled = enabled_flag + # Set fields or value of the wrapped widget + if isinstance(self.wrapped, WidgetWithFieldsProtocol): + # Expecting {'enabled': True/False, **wrapped_fields} + self.wrapped.set_fields(new_values) + elif 'value' in new_values: # Skip if 'value' is not in new_values + # i.e. User might only want to change the enabled flag + # We use ``set_fields`` to set the value of the wrapped widget + # so that it handles availability of a value setter. + # Expecting {'enabled': True/False, 'value': value} + set_fields(self.wrapped, new_values.pop('value')) + if new_values: + # Check if there are any fields left except for 'enabled' and 'value' + raise ValueError( + f"Unexpected field(s) {new_values.keys()} for widget {self.wrapped}" + ) + + def get_fields(self) -> dict[str, Any]: + wrapped_fields = get_fields(self.wrapped) + if isinstance(self.wrapped, WidgetWithFieldsProtocol) and isinstance( + wrapped_fields, dict + ): + return {'enabled': self.enabled, **wrapped_fields} + else: + return {'enabled': self.enabled, 'value': wrapped_fields} From 81fd146c71d027d1ea9c40a42b42908537d0a877 Mon Sep 17 00:00:00 2001 From: YooSunyoung Date: Wed, 11 Dec 2024 14:43:50 +0100 Subject: [PATCH 2/2] Test switchable widget. --- tests/widget_test.py | 70 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/tests/widget_test.py b/tests/widget_test.py index 91e5959f..d88f92ac 100644 --- a/tests/widget_test.py +++ b/tests/widget_test.py @@ -440,3 +440,73 @@ def test_bin_edges_widget_with_default_values() -> None: assert param_widget.fields['stop'].value == 0.6 assert param_widget.fields['nbins'].value == 150 assert param_widget.fields['spacing'].value == 'linear' + + +def test_switchable_widget_set_values() -> None: + param = IntParam('a', 'a', 1, switchable=True) + widget = create_parameter_widget(param) + assert isinstance(widget, SwitchWidget) + assert not widget.enabled + widget.set_fields({'enabled': True}) + assert widget.enabled + widget.set_fields({'enabled': False}) + assert not widget.enabled + widget.set_fields({'enabled': True, 'value': 2}) + assert widget.enabled + assert widget.value == 2 + widget.set_fields({'enabled': False, 'value': 3}) + assert not widget.enabled + assert widget.value == 3 + widget.set_fields({'value': 4}) + assert not widget.enabled + assert widget.value == 4 + + +def test_switchable_widget_get_fields_only_value() -> None: + param = IntParam('a', 'a', 1, switchable=True) + widget = create_parameter_widget(param) + assert isinstance(widget, SwitchWidget) + assert widget.get_fields() == {'enabled': False, 'value': 1} + widget.enabled = True + assert widget.get_fields() == {'enabled': True, 'value': 1} + widget.value = 2 + assert widget.get_fields() == {'enabled': True, 'value': 2} + widget.enabled = False + assert widget.get_fields() == {'enabled': False, 'value': 2} + + +def test_switchable_widget_set_fields() -> None: + param = Vector3dParameter('a', 'a', sc.vector([1, 2, 3], unit='m'), switchable=True) + widget = create_parameter_widget(param) + assert isinstance(widget, SwitchWidget) + assert not widget.enabled + assert widget.value == sc.vector([1, 2, 3], unit='m') + widget.set_fields({'enabled': True, 'x': 4, 'y': 5, 'z': 6, 'unit': 'm'}) + assert widget.enabled + assert widget.value == sc.vector([4, 5, 6], unit='m') + widget.set_fields({'x': 7, 'y': 8}) + assert widget.enabled + assert widget.value == sc.vector([7, 8, 6], unit='m') + + +def test_switchable_widget_get_fields_sub_fields() -> None: + param = Vector3dParameter('a', 'a', sc.vector([1, 2, 3], unit='m'), switchable=True) + widget = create_parameter_widget(param) + assert isinstance(widget, SwitchWidget) + assert widget.get_fields() == { + 'enabled': False, + 'x': 1, + 'y': 2, + 'z': 3, + 'unit': 'm', + } + widget.enabled = True + assert widget.get_fields() == {'enabled': True, 'x': 1, 'y': 2, 'z': 3, 'unit': 'm'} + widget.set_fields({'enabled': False, 'x': 4, 'y': 5, 'unit': 'mm'}) + assert widget.get_fields() == { + 'enabled': False, + 'x': 4, + 'y': 5, + 'z': 3, + 'unit': 'mm', + }