<jupyter_start><jupyter_text>Scipp-widgets advanced concepts This page describes the concepts and interfaces used by the `scippwidgets` module if more advanced users wish to create their own input classes or validators.  Input specificationAn input object describes how an input widget should be constructed and how its user-input should be validated and pre-processed. For the majority of cases the provided generic [classes](inputs.ipynb) provided should suffice. If you wish to make your own, the interface that needs to be implemented has two properties. These are: * `widget`: returns the ipywidget used for user input. * `function_arguments`: returns a dict of {func arg name: func arg value} Programmatically this interface looks like:<jupyter_code>from abc import ABC, abstractmethod
class IInput(ABC):
    """
    Interfaces detailing which methods and properties an
    input specification must have.
    """
    @property
    @abstractmethod
    def widget(self):
        """
        Returns a constructed widget to insert into the graphical interface.
        """
        pass

    @property
    @abstractmethod
    def function_arguments(self):
        """
        Returns a dict made up of func_arg_name: func_arg_value
        """
        pass<jupyter_output><empty_output><jupyter_text>Examples Float slider input<jupyter_code>import ipywidgets as widgets
class FloatSliderInput(IInput):
    def __init__(self, func_arg_name, range_min, range_max, intial_value):
        self._widget = widgets.FloatSlider(value=intial_value, min=range_min, max=range_max)
        self._func_arg_name = func_arg_name
        
    @property
    def widget(self):
        return self._widget
    
    @property
    def function_arguments(self):
        return {self._func_arg_name: self._widget.value}
    
FloatSliderInput('example_param', 0, 5, 2.5).widget<jupyter_output><empty_output><jupyter_text>Linked scipp object and dimension inputs<jupyter_code>from typing import Any, Sequence, MutableMapping, Dict, Callable
from scippwidgets.inputs import get_notebook_global_scope
from scippwidgets.validators import ScippObjectValidator

scipp_object_validator = ScippObjectValidator

class ScippInputWithDim(IInput):
    """
    Input widget which takes a scipp object and a linked
    dimension field.
    """
    def __init__(self,
                 func_arg_names: Sequence[str],
                 data_name: str = 'data',
                 scope: MutableMapping[str, Any] = {}):
        self._scope = scope if scope else get_notebook_global_scope()
        self._func_arg_names = func_arg_names
        self._scipp_obj_input = widgets.Text(placeholder=data_name,
                                             continuous_update=False)
        self._dimension_input = widgets.Combobox(placeholder='dim',
                                                 continuous_update=False)
        self._scipp_obj_input.observe(self._handle_scipp_obj_change,
                                      names='value')
        self._widget = widgets.HBox(
            [self._scipp_obj_input, self._dimension_input])
        self._validators = (self._scipp_obj_validator, self._dims_validator)
        self._allowed_dims = []

    @property
    def function_arguments(self):
        return {
            name: validator(widget.value)
            for name, widget, validator in zip(
                self._func_arg_names, self.widget.children, self._validators)
        }

    @property
    def widget(self):
        return self._widget

    def _handle_scipp_obj_change(self, change):
        try:
            scipp_obj = self._scipp_obj_validator(change['new'])
            dims = scipp_obj.dims
            self._dimension_input.options = dims
            self._allowed_dims = dims
        except ValueError:
            pass

    def _scipp_obj_validator(self, input):
        scipp_object = _wrapped_eval(input, self._scope)
        scipp_object_validator(scipp_object)
        has_attr_validator(scipp_object, 'dims')
        return scipp_object

    def _dims_validator(self, input):
        if not input:
            raise ValueError('No dimension selected')
        if input in self._allowed_dims:
            return input
        else:
            raise ValueError(f'Dimension {input} does no exist in'
                             f' {self._scipp_obj_input.value}')
            
ScippInputWithDim(['arg1', 'arg2']).widget<jupyter_output><empty_output><jupyter_text>ValidatorsA validator can be assigned to each input.These serve the dual purpose of doing any required pre-processing and validating the user inputs.See [validators](validators.ipynb) for a list of supplied validators.They should take the form of a function with a single input and single output, and throw a `ValueError` with an appropriate message if the validation fails. Examples Allowed value validatorIf an input is required to belong to a certain set of allowed values, the following validator could be used.<jupyter_code>def integer_range_validator(input):
    allowed_values = ['a', 'b', 'c']
    if input in allowed_values:
        return input
    
    raise ValueError(
                f'{input} is invalid. Allowed values are {allowed_values}.')
integer_range_validator('a')<jupyter_output><empty_output><jupyter_text>A factory is supplied which performs a similar function.<jupyter_code>from scippwidgets.validators import ValueValidator
allowed_values_validator = ValueValidator(allowed_values=['a', 'b', 'c'])
allowed_values_validator('b')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.0728225514998193, 'fraction_numerical': 0.0010842067220816769, 'mean_word_length': 3.1060830860534123, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 5, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '5', 'dir': 'jupyter-structured-clean-dedup', 'id': '238459', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 1512, 'n_tokens_neox': 1502, 'n_words': 442, 'path': 'docs/concepts.ipynb', 'repo_name': 'scipp/scipp-widgets', 'size': '5534'}