Skip to content

Commit

Permalink
- add ConfigurationHandler to handle updating the config state upon s…
Browse files Browse the repository at this point in the history
…ignals from the UI (#78)

- remove obsolete remove_files.py
  • Loading branch information
backmari authored Apr 24, 2024
1 parent f6d3e86 commit 1e4e8be
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 40 deletions.
83 changes: 83 additions & 0 deletions reflectivity_ui/interfaces/event_handlers/configuration_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QSpinBox, QCheckBox, QDoubleSpinBox

from reflectivity_ui.interfaces.configuration import Configuration


class ConfigurationHandler:
"""
Handles updating the configuration state upon changes in the UI
"""

def __init__(self, main_window):
self.main_window = main_window
self.ui = main_window.ui
self.connect_config_events()

def config_setter_factory(self, config_name: str, is_checkbox: bool):
"""
Generate anonymous functions to serve as callback when any of the global configurations
(`Configuration` class variables) are updated in the UI.
Each callback will be associated to one configuration parameter. Upon invoked,
the `Configuration` class variable value will be updated.
Parameters
----------
config_name: str
Name of the Configuration variable to update
is_checkbox: bool
True if the widget type is QCheckBox
"""

def config_setter(value: int | float):
if is_checkbox:
bool_value = value == Qt.CheckState.Checked
setattr(Configuration, config_name, bool_value)
else:
setattr(Configuration, config_name, value)

return config_setter

def connect_config_events(self):
widget_names = [
"final_rebin_checkbox",
"q_rebin_spinbox",
"normalize_to_unity_checkbox",
"normalization_q_cutoff_spinbox",
"global_fit_checkbox",
"polynomial_stitching_degree_spinbox",
"polynomial_stitching_points_spinbox",
"polynomial_stitching_checkbox",
"fanReflectivity",
"sample_size_spinbox",
"bandwidth_spinbox",
]
config_names = [
"do_final_rebin",
"final_rebin_step",
"normalize_to_unity",
"total_reflectivity_q_cutoff",
"global_stitching",
"polynomial_stitching_degree",
"polynomial_stitching_points",
"polynomial_stitching",
"use_constant_q",
"sample_size",
"wl_bandwidth",
]

for widget_name, config_name in zip(widget_names, config_names):
# get the widget signal to connect
widget = getattr(self.ui, widget_name)
is_checkbox = False
if isinstance(widget, (QSpinBox, QDoubleSpinBox)):
signal_name = "valueChanged"
elif isinstance(widget, QCheckBox):
is_checkbox = True
signal_name = "stateChanged"
else:
raise ValueError(f"{type(widget)} not supported by ConfigurationHandler")
signal = getattr(widget, signal_name)
# connect the signal to the updater
signal.connect(self.config_setter_factory(config_name, is_checkbox))
2 changes: 2 additions & 0 deletions reflectivity_ui/interfaces/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from .smooth_dialog import SmoothDialog
import reflectivity_ui
from reflectivity_ui.interfaces.data_handling.filepath import FilePath
from reflectivity_ui.interfaces.event_handlers.configuration_handler import ConfigurationHandler
from reflectivity_ui.interfaces.event_handlers.plot_handler import PlotHandler
from reflectivity_ui.interfaces.event_handlers.main_handler import MainHandler
from reflectivity_ui.interfaces import load_ui
Expand Down Expand Up @@ -66,6 +67,7 @@ def __init__(self):
# Event handlers
self.plot_handler = PlotHandler(self)
self.file_handler = MainHandler(self)
self.config_handler = ConfigurationHandler(self)
self.ui.compare_widget.data_manager = self.data_manager

# Initialization for specific instrument
Expand Down
11 changes: 0 additions & 11 deletions test/data/remote_files.json

This file was deleted.

29 changes: 0 additions & 29 deletions test/data/remote_files.py

This file was deleted.

84 changes: 84 additions & 0 deletions test/ui/test_reflectivity_extraction_global.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# local imports
from reflectivity_ui.interfaces.configuration import Configuration
from reflectivity_ui.interfaces.data_handling.data_set import NexusData, CrossSectionData
from reflectivity_ui.interfaces.main_window import MainWindow

# third party imports
import pytest

# standard library imports


def _initialize_test_data(main_window):
"""Add one run with two cross-sections to the data manager"""
config = Configuration()
nexus_data = NexusData("file/path", config)
off_off = CrossSectionData("Off_Off", config)
on_off = CrossSectionData("On_Off", config)
nexus_data.cross_sections["Off_Off"] = off_off
nexus_data.cross_sections["On_Off"] = on_off
main_window.data_manager._nexus_data = nexus_data
main_window.data_manager.set_channel(0)
main_window.data_manager.add_active_to_reduction()


def _assert_configuration_value(main_window, param_name, gold_value):
"""Check parameter value through the data hierarchy"""
assert getattr(main_window.data_manager.active_channel.configuration, param_name) is gold_value
for nexus_data in main_window.data_manager.reduction_list:
assert getattr(nexus_data.configuration, param_name) is gold_value
for xs_data in nexus_data.cross_sections.values():
assert getattr(xs_data.configuration, param_name) is gold_value


def _assert_configuration_float_value(main_window, param_name, gold_value):
"""Check float parameter value through the data hierarchy"""
assert getattr(main_window.data_manager.active_channel.configuration, param_name) == pytest.approx(gold_value)
for nexus_data in main_window.data_manager.reduction_list:
assert getattr(nexus_data.configuration, param_name) == pytest.approx(gold_value)
for xs_data in nexus_data.cross_sections.values():
assert getattr(xs_data.configuration, param_name) == pytest.approx(gold_value)


@pytest.mark.parametrize(
"widget, config_param",
[
("final_rebin_checkbox", "do_final_rebin"),
("normalize_to_unity_checkbox", "normalize_to_unity"),
("global_fit_checkbox", "global_stitching"),
("polynomial_stitching_checkbox", "polynomial_stitching"),
("fanReflectivity", "use_constant_q"),
],
)
def test_global_checkboxes(qtbot, widget, config_param):
"""Test that UI global config checkbox changes get propagated to all configuration levels"""
main_window = MainWindow()
qtbot.addWidget(main_window)
_initialize_test_data(main_window)

getattr(main_window.ui, widget).setChecked(True)
_assert_configuration_value(main_window, config_param, True)

getattr(main_window.ui, widget).setChecked(False)
_assert_configuration_value(main_window, config_param, False)


@pytest.mark.parametrize(
"widget, config_param, gold_value",
[
("q_rebin_spinbox", "final_rebin_step", 0.01),
("normalization_q_cutoff_spinbox", "total_reflectivity_q_cutoff", 0.02),
("polynomial_stitching_degree_spinbox", "polynomial_stitching_degree", 2),
("polynomial_stitching_points_spinbox", "polynomial_stitching_points", 5),
("sample_size_spinbox", "sample_size", 15.0),
("bandwidth_spinbox", "wl_bandwidth", 2.5),
],
)
def test_global_spinboxes(qtbot, widget, config_param, gold_value):
"""Test that UI global config spinbox changes get propagated to all configuration levels"""
main_window = MainWindow()
qtbot.addWidget(main_window)
_initialize_test_data(main_window)

getattr(main_window.ui, widget).setValue(gold_value)
_assert_configuration_float_value(main_window, config_param, gold_value)

0 comments on commit 1e4e8be

Please sign in to comment.