Source code for scwidgets.code._widget_parameters_panel

from typing import Any, Callable, Dict, List, Union

from ipywidgets import Output, VBox, Widget, fixed, interactive
from traitlets.utils.sentinel import Sentinel

from ..check import Check


[docs] class ParametersPanel(VBox): """ A wrapper around ipywidgets.interactive to have more control how to connect the parameters and the observation of parameters by buttons and the panels :param parameters: Can be any input that is allowed as keyword arguments in ipywidgets.interactive for the parameters. _options and other widget layout parameter are controlled by CodeExercise. """ def __init__( self, **parameters: Dict[str, Union[Check.FunInParamT, Widget]], ): if "_option" in parameters.keys(): raise ValueError( "Found interactive argument `_option` in paramaters, but " "ParametersPanels should be controled by an exercise widget " "to ensure correct initialization." ) # we use a dummy function because interactive executes it once on init # and the actual function might be expensive to compute def dummy_function(**kwargs): pass self._interactive_widget = interactive(dummy_function, **parameters) assert isinstance(self._interactive_widget.children[-1], Output), ( "Assumed that interactive returns an output as last child. " "Parameter will be wrongly initialized if this is not True." ) # Because interact only keeps a list of the widgets we build a map # so the params can be changed in arbitrary order. # Last widget is an output that interact adds to the widgets. self._param_to_widget_map = { key: widget for key, widget in zip( parameters.keys(), self._interactive_widget.kwargs_widgets ) } super().__init__(self.panel_parameters_widget) @property def param_to_widget_map(self) -> dict[str, Widget]: return self._param_to_widget_map @property def panel_parameters_trait(self) -> List[str]: return ["value"] * len(self.panel_parameters) @property def panel_parameters_widget(self) -> List[Widget]: """ :return: Only parameters that are tunable in the parameter panel are returned. Fixed parameters are ignored. """ return [ widget for widget in self._param_to_widget_map.values() if not (isinstance(widget, fixed)) ] @property def parameters(self) -> Dict[str, Any]: """ :return: All parameters that were given on initialization are returned, also including also fixed parameters. """ return {key: widget.value for key, widget in self._param_to_widget_map.items()} @property def panel_parameters(self) -> Dict[str, Any]: """ :return: Only parameters that are tunable in the parameter panel are returned. Fixed parameters are ignored. """ return { key: widget.value for key, widget in self._param_to_widget_map.items() if not (isinstance(widget, fixed)) } def update_parameters(self, new_parameters: Dict[str, Any]): for key, value in new_parameters.items(): self.param_to_widget_map[key].value = value def observe_parameters( self, handler: Callable[[dict], None], trait_name: Union[str, Sentinel, List[str]], notification_type: Union[None, str, Sentinel] = "change", ): """ """ for widget in self.panel_parameters_widget: widget.observe(handler, trait_name, notification_type) def unobserve_parameters( self, handler: Callable[[dict], None], trait_name: Union[str, Sentinel, List[str]], notification_type: Union[None, str, Sentinel] = "change", ): for widget in self.panel_parameters_widget: widget.unobserve(handler, trait_name, notification_type) def set_parameters_widget_attr(self, name: str, value): for widget in self.panel_parameters_widget: if hasattr(widget, name): setattr(widget, name, value)