MarcSkovMadsen's picture
update
e1895d5
"""The FeatureInput enables a user to select from a list of features and set their values"""
import panel as pn
import param
class FeatureInput(pn.widgets.CompositeWidget):
"""The FeatureInput enables a user to select from a list of features and set their values
## Example
```python
features = {
"A": 1.0,
"B": 2.0,
"C": 3.0,
"D": 4.0,
}
selected_features = ["A", "C"]
widget = FeatureInput(features=features, selected_features=selected_features)
```
"""
value = param.Dict(
doc="The names of the features selected and their set values", allow_None=False
)
features = param.Dict(
doc="The names of the available features and their default values",
allow_None=False,
)
selected_features = param.ListSelector(
doc="The list of selected features", allow_None=False
)
_selected_widgets = param.ClassSelector(
class_=pn.Column, doc="The widgets used to edit the selected features"
)
_composite_type = pn.Column
def __init__(self, **params):
params["value"] = params.get("value", {})
params["features"] = params.get("features", {})
params["selected_features"] = params.get("selected_features", [])
params["_selected_widgets"] = self.param._selected_widgets.class_()
super().__init__(**params)
selected_features_widget = pn.widgets.MultiChoice.from_param(
self.param.selected_features, sizing_mode="stretch_width"
)
self._composite[:] = [selected_features_widget, self._selected_widgets]
@param.depends("features", watch=True, on_init=True)
def _reset_selected_features(self):
selected_features = []
for feature in self.selected_features.copy():
if feature in self.features.copy():
selected_features.append(feature)
self.param.selected_features.objects = list(self.features)
self.selected_features = selected_features
@param.depends("selected_features", watch=True, on_init=True)
def _handle_selected_features_change(self):
org_value = self.value
self._update_selected_widgets(org_value)
self._update_value()
def _update_value(self, *args): # pylint: disable=unused-argument
new_value = {}
for widget in self._selected_widgets:
new_value[widget.name] = widget.value
self.value = new_value
def _update_selected_widgets(self, org_value):
new_widgets = {}
for feature in self.selected_features:
value = org_value.get(feature, self.features[feature])
widget = self._new_widget(feature, value)
new_widgets[feature] = widget
self._selected_widgets[:] = list(new_widgets.values())
def _new_widget(self, feature, value):
widget = pn.widgets.FloatInput(
name=feature, value=value, sizing_mode="stretch_width"
)
pn.bind(self._update_value, widget, watch=True)
return widget
def create_app():
features = {
"Blade Length (m)": 73.5,
"Cut-in Wind Speed (m/s)": 3.5,
"Cut-out Wind Speed (m/s)": 25,
"Grid Connection Capacity (MW)": 5,
"Hub Height (m)": 100,
"Rated Wind Speed (m/s)": 12,
"Rotor Diameter (m)": 150,
"Turbine Efficiency (%)": 45,
"Water Depth (m)": 30,
"Wind Speed (m/s)": 10,
}
selected_features = ["Wind Speed (m/s)", "Rotor Diameter (m)"]
widget = FeatureInput(
features=features,
selected_features=selected_features,
width=500,
)
return pn.FlexBox(
pn.Column(
"## Widget",
widget,
),
pn.Column(
"## Value",
pn.pane.JSON(widget.param.value, width=500, height=200),
),
)
if pn.state.served:
pn.extension(design="material")
create_app().servable()