MarcSkovMadsen
commited on
Update app.py
Browse files
app.py
CHANGED
@@ -1,95 +1,133 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
pn.extension(sizing_mode="stretch_width", design="material")
|
4 |
-
|
5 |
-
BUTTON_WIDTH = 125
|
6 |
-
|
7 |
-
# We use intslider to avoid teaching users pn.rx. Is that a good thing?
|
8 |
-
state_changed_count = pn.widgets.IntInput()
|
9 |
-
tasks = pn.Column()
|
10 |
-
|
11 |
-
def update_state_changed_count(*args):
|
12 |
-
state_changed_count.value += 1
|
13 |
-
|
14 |
-
def remove_task(task, *args):
|
15 |
-
index = tasks.index(task)
|
16 |
-
tasks.pop(index)
|
17 |
-
|
18 |
-
def remove_all_tasks(*args):
|
19 |
-
tasks.clear()
|
20 |
-
|
21 |
-
def create_task(text):
|
22 |
-
state = pn.widgets.Checkbox(align="center", sizing_mode="fixed")
|
23 |
-
content = pn.pane.Markdown(text)
|
24 |
-
remove = pn.widgets.Button(width=BUTTON_WIDTH, icon="trash", sizing_mode="fixed")
|
25 |
-
task = pn.Row(state, content, remove, sizing_mode="stretch_width")
|
26 |
-
|
27 |
-
pn.bind(remove_task, task, remove, watch=True)
|
28 |
-
# We have to bind the below after the above!
|
29 |
-
pn.bind(update_state_changed_count, state, remove, watch=True)
|
30 |
-
|
31 |
-
return task
|
32 |
-
|
33 |
-
def add_task(text, *args):
|
34 |
-
if not text:
|
35 |
-
return
|
36 |
|
37 |
-
|
38 |
-
tasks.append(new_task)
|
39 |
|
40 |
-
|
41 |
-
|
42 |
-
def get_state(*args):
|
43 |
-
total_tasks = len(tasks)
|
44 |
-
completed_tasks = sum(check[0].value for check in tasks)
|
45 |
-
return f"{completed_tasks} of {total_tasks} tasks completed"
|
46 |
-
|
47 |
-
def can_add(value_input):
|
48 |
-
return not bool(value_input)
|
49 |
-
|
50 |
-
def has_tasks(*args):
|
51 |
-
return len(tasks) > 0
|
52 |
-
|
53 |
-
|
54 |
-
add_task("Inspect the blades")
|
55 |
-
add_task("Inspect the nacelle")
|
56 |
-
add_task("Tighten the bolts")
|
57 |
-
|
58 |
-
text_input = pn.widgets.TextInput(name="Task", placeholder="Enter a task")
|
59 |
-
|
60 |
-
submit_task = pn.widgets.Button(
|
61 |
-
name="Add",
|
62 |
-
align="center",
|
63 |
-
button_type="primary",
|
64 |
-
width=BUTTON_WIDTH,
|
65 |
-
sizing_mode="fixed",
|
66 |
-
disabled=pn.bind(can_add, text_input.param.value_input)
|
67 |
-
)
|
68 |
-
clear = pn.widgets.Button(
|
69 |
-
name="Remove All",
|
70 |
-
button_type="primary",
|
71 |
-
button_style="outline",
|
72 |
-
width=BUTTON_WIDTH,
|
73 |
-
sizing_mode="fixed",
|
74 |
-
visible=pn.bind(has_tasks, state_changed_count)
|
75 |
-
)
|
76 |
-
|
77 |
-
def reset_text_input(*args):
|
78 |
-
text_input.value = text_input.value_input = ""
|
79 |
-
|
80 |
-
pn.bind(add_task, text_input, submit_task, watch=True)
|
81 |
-
pn.bind(reset_text_input, text_input, submit_task, watch=True)
|
82 |
-
pn.bind(remove_all_tasks, clear, watch=True)
|
83 |
-
# We have to bind the below after the above!
|
84 |
-
pn.bind(update_state_changed_count, text_input, submit_task, clear, watch=True)
|
85 |
|
86 |
-
status_report = pn.bind(get_state, state_changed_count, tasks.param.objects)
|
87 |
|
88 |
-
pn.
|
89 |
-
"
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""The FeatureInput enables a user to select from a list of features and set their values"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
|
3 |
+
import param
|
|
|
4 |
|
5 |
+
import panel as pn
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
|
|
|
7 |
|
8 |
+
class FeatureInput(pn.widgets.CompositeWidget):
|
9 |
+
"""The FeatureInput enables a user to select from a list of features and set their values
|
10 |
+
|
11 |
+
## Example
|
12 |
+
|
13 |
+
```python
|
14 |
+
features = {
|
15 |
+
"A": 1.0,
|
16 |
+
"B": 2.0,
|
17 |
+
"C": 3.0,
|
18 |
+
"D": 4.0,
|
19 |
+
}
|
20 |
+
selected_features = ["A", "C"]
|
21 |
+
widget = FeatureInput(features=features, selected_features=selected_features)
|
22 |
+
```
|
23 |
+
"""
|
24 |
+
|
25 |
+
value = param.Dict(
|
26 |
+
doc="The names of the features selected and their set values", allow_None=False
|
27 |
+
)
|
28 |
+
|
29 |
+
features = param.Dict(
|
30 |
+
doc="The names of the available features and their default values",
|
31 |
+
allow_None=False,
|
32 |
+
)
|
33 |
+
selected_features = param.ListSelector(
|
34 |
+
doc="The list of selected features", allow_None=False
|
35 |
+
)
|
36 |
+
|
37 |
+
_selected_widgets = param.ClassSelector(
|
38 |
+
class_=pn.Column, doc="The widgets used to edit the selected features"
|
39 |
+
)
|
40 |
+
|
41 |
+
_composite_type = pn.Column
|
42 |
+
|
43 |
+
def __init__(self, **params):
|
44 |
+
params["value"] = params.get("value", {})
|
45 |
+
params["features"] = params.get("features", {})
|
46 |
+
params["selected_features"] = params.get("selected_features", [])
|
47 |
+
|
48 |
+
params["_selected_widgets"] = self.param._selected_widgets.class_()
|
49 |
+
|
50 |
+
super().__init__(**params)
|
51 |
+
|
52 |
+
selected_features_widget = pn.widgets.MultiChoice.from_param(
|
53 |
+
self.param.selected_features, sizing_mode="stretch_width"
|
54 |
+
)
|
55 |
+
|
56 |
+
self._composite[:] = [selected_features_widget, self._selected_widgets]
|
57 |
+
|
58 |
+
@param.depends("features", watch=True, on_init=True)
|
59 |
+
def _reset_selected_features(self):
|
60 |
+
selected_features = []
|
61 |
+
for (
|
62 |
+
feature
|
63 |
+
) in self.selected_features.copy():
|
64 |
+
if feature in self.features.copy():
|
65 |
+
selected_features.append(
|
66 |
+
feature
|
67 |
+
)
|
68 |
+
|
69 |
+
self.param.selected_features.objects = list(self.features)
|
70 |
+
self.selected_features = selected_features
|
71 |
+
|
72 |
+
@param.depends("selected_features", watch=True, on_init=True)
|
73 |
+
def _handle_selected_features_change(self):
|
74 |
+
org_value = self.value
|
75 |
+
|
76 |
+
self._update_selected_widgets(org_value)
|
77 |
+
self._update_value()
|
78 |
+
|
79 |
+
def _update_value(self, *args): # pylint: disable=unused-argument
|
80 |
+
new_value = {}
|
81 |
+
|
82 |
+
for widget in self._selected_widgets:
|
83 |
+
new_value[widget.name] = widget.value
|
84 |
+
|
85 |
+
self.value = new_value
|
86 |
+
|
87 |
+
def _update_selected_widgets(self, org_value):
|
88 |
+
new_widgets = {}
|
89 |
+
|
90 |
+
for feature in self.selected_features:
|
91 |
+
value = org_value.get(feature, self.features[feature])
|
92 |
+
widget = self._new_widget(feature, value)
|
93 |
+
new_widgets[feature] = widget
|
94 |
+
|
95 |
+
self._selected_widgets[:] = list(new_widgets.values())
|
96 |
+
|
97 |
+
def _new_widget(self, feature, value):
|
98 |
+
widget = pn.widgets.FloatInput(
|
99 |
+
name=feature, value=value, sizing_mode="stretch_width"
|
100 |
+
)
|
101 |
+
pn.bind(self._update_value, widget, watch=True)
|
102 |
+
return widget
|
103 |
+
|
104 |
+
def create_app():
|
105 |
+
features = {
|
106 |
+
"Blade Length (m)": 73.5,
|
107 |
+
"Cut-in Wind Speed (m/s)": 3.5,
|
108 |
+
"Cut-out Wind Speed (m/s)": 25,
|
109 |
+
"Grid Connection Capacity (MW)": 5,
|
110 |
+
"Hub Height (m)": 100,
|
111 |
+
"Rated Wind Speed (m/s)": 12,
|
112 |
+
"Rotor Diameter (m)": 150,
|
113 |
+
"Turbine Efficiency (%)": 45,
|
114 |
+
"Water Depth (m)": 30,
|
115 |
+
"Wind Speed (m/s)": 10,
|
116 |
+
}
|
117 |
+
selected_features = ["Wind Speed (m/s)", "Rotor Diameter (m)"]
|
118 |
+
widget = FeatureInput(
|
119 |
+
features=features,
|
120 |
+
selected_features=selected_features,
|
121 |
+
width=500,
|
122 |
+
styles={"border": "1px solid lightgray", "border-radius": "5px"},
|
123 |
+
)
|
124 |
+
|
125 |
+
return pn.Column(
|
126 |
+
"## Widget",
|
127 |
+
widget,
|
128 |
+
"## Value",
|
129 |
+
pn.pane.JSON(widget.param.value, width=500, height=300),
|
130 |
+
)
|
131 |
+
|
132 |
+
if pn.state.served:
|
133 |
+
create_app().servable()
|