Graphic user interfaces for data reduction.#
ess.reduce.parameter.Parameter
is an interface between params
of sciline.Pipeline
(workflow) and GUI components.
In this page, we will explain how to implement new Parameter and widgets and map those components with each other.
Registries: How to map Parameter
, Workflow
and Pipeline
.#
Parameters, workflows and widgets should be registered once they are implemented in order to be used automatically by workflow-widget building helper.
The helper uses registries to - Find workflows - Find input arguments that can be set by widget - Find output types that can be computed by widget - Map Parameter
and Pipeline.params
to build smaller widgets - Set Pipeline.params
based on the inputs of corresponding widgets
Tutorial: Implement Random Distribution Histogram Workflow GUI#
Prepare
workflow
interface that returns asciline.Pipeline
We are going to use this random number histogram workflow as an example. We would like to implement a widget that users can putNumBins
as an input.If you want to specify
typical_outputs
, thePipeline
object should have a property calledtypical_outputs: tuple[type, ...]
. If it does not have atypical_outputs
, leaf nodes will be used as typical outputs.
[1]:
import scipp as sc
import sciline as sl
from typing import NewType
import numpy as np
NumBins = NewType('NumBins', int)
Histogram = NewType('Histogram', sc.Variable)
def histogram(num_bins: NumBins) -> Histogram:
rng = np.random.default_rng()
events = sc.array(dims=['event'], values=rng.normal(size=500))
return Histogram(events.hist(event=num_bins))
def RandomDistributionWorkflow() -> sl.Pipeline:
wf = sl.Pipeline(providers=(histogram,))
# wf.typical_outputs = (Histogram, ) # Can be skipped since it's the only leaf node.
return wf
RandomDistributionWorkflow()
[1]:
Name | Value | Source |
---|---|---|
Histogram |
histogram__main__.histogram | |
NumBins |
Register workflow to the
workflow_registry
.
[2]:
from ess.reduce.workflow import register_workflow
register_workflow(RandomDistributionWorkflow)
[2]:
<function __main__.RandomDistributionWorkflow() -> sciline.pipeline.Pipeline>
Register
domain-type
-Parameter
instance mapping toparameter_registry
.
Parameter
and its subclasses have a class method from_type
that helps to create a new parameter instance for a specific domain type.
There are various parameter types already exist in ess.reduce.parameter
module, but here we will show you how to make a new one.
Once you know which Parameter
to use for the specific domain-type
,
you can register the new parameter instance to the domain type in the parameter_registry
.
[3]:
from ess.reduce.parameter import parameter_registry, Parameter
class BinNumberParameter(Parameter): ...
parameter_registry[NumBins] = BinNumberParameter(
name=NumBins.__name__,
description='Number of bins in the histogram',
default=NumBins(10),
)
# You can also use ``from_type``(class method) helper to instantiate the parameter.
# parameter_registry[NumBins] = BinNumberParameter.from_type(NumBins)
Register
type[Parameter]
-type[Widget]
mapping tocreate_parameter_widget
distpatch.
[4]:
from ess.reduce.widgets import create_parameter_widget
import ipywidgets as widgets
@create_parameter_widget.register(BinNumberParameter)
def scalar_parameter_widget(param: BinNumberParameter):
return widgets.IntText(
value=param.default, description=param.name, tooltip=param.description
)
Example UI from the tutorial.#
[5]:
from ess.reduce.ui import workflow_widget
ess_widget = workflow_widget()
[7]:
ess_widget
[7]:
Wrapper Widgets#
In order to handle special cases of parameter settings, we have wrapper widgets.
Each wrapper widget is associated with certain attribute of Parameter
object.
They are implemented as a decorator around widget type dispatch function like below.
It is because of @singledispatch
decorator.
# In ess.reduce.widgets module
@switchable_widget
@optional_widget # optional_widget should be applied first
@singledispatch
def create_parameter_widget(param: Parameter) -> widgets.Widget: ...
Switchable Widget: Parameter.switchable
#
Widgets are wrapped in SwitchableWidget
if Parameter
is switchable
.
The wrapped parameter input widget can be turned off and on.
If the widget is enabled
(on), the workflow-compute handling widget should set the value as a parameter into the Pipeline(workflow)
, but if the widget is not enabled
(off), the workflow-compute handling widget should skip setting the value as a parameter.
It means it will either use the default parameter that was set or computed by providers.
[8]:
from ess.reduce.widgets import create_parameter_widget
from ess.reduce.parameter import Parameter
switchable_parameter = Parameter(
name='SwitchableParameter', description="", default="", switchable=True
)
switchable_widget = create_parameter_widget(switchable_parameter)
switchable_widget.enabled = True
switchable_widget
[8]:
Optional Widget: Parameter.optional
#
Widgets are wrapped in a OptionalWidget
if Parameter
is optional
.
The wrapped parameter input widget can select None
as a value.
If None
is selected, the workflow-compute handling widget should set None
as a parameter into the Pipeline(workflow)
, but if the widget is not None
, the workflow-compute handling widget will retrieve the value from the wrapped widget.
This wrapper is for the providers expecting optional arguments and handle the None
itself.
[9]:
from ess.reduce.widgets import create_parameter_widget
from ess.reduce.parameter import Parameter
optional_parameter = Parameter(
name='OptionalParameter', description="", default="", optional=True
)
optional_widget = create_parameter_widget(optional_parameter)
optional_widget.value = "Test String"
optional_widget
[9]:
If Parameter
object is both switchable
and optional
, the widget is wrapped both in SwitchWidget
and OptionalWidget
.
[10]:
from ess.reduce.widgets import create_parameter_widget
from ess.reduce.parameter import Parameter
switchable_and_optional_widget = create_parameter_widget(
Parameter(
name='Parameter', description="", default="", switchable=True, optional=True
)
)
switchable_and_optional_widget.enabled = True
switchable_and_optional_widget
[10]: