updated expert app layout

This commit is contained in:
Luis Aleixo 2023-02-23 10:46:34 +01:00
parent b941808317
commit 22f12f67b0
3 changed files with 113 additions and 8 deletions

View file

@ -12,14 +12,30 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 1,
"metadata": {
"pycharm": {
"name": "#%%\n"
},
"scrolled": false
},
"outputs": [],
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "e8c4e2146d4847d5a1443781f2018483",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(Tab(children=(VBox(children=(VBox(children=(Button(button_style='success', description='Duplica…"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import caimira.apps\n",
"\n",
@ -30,7 +46,7 @@
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"display_name": "caimira",
"language": "python",
"name": "python3"
},
@ -44,7 +60,12 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.12"
"version": "3.9.6"
},
"vscode": {
"interpreter": {
"hash": "c77495895472738765eb97c8f848f37a4e60c741d594ab92dd40b6b8f4cac818"
}
}
},
"nbformat": 4,

View file

@ -122,7 +122,9 @@ class ExposureModelResult(View):
right = max(model.CO2_emitters.presence.boundaries()[1])*1.05)
figure_legends = [mlines.Line2D([], [], color='#3530fe', markersize=15, label='CO₂ concentration'),
patches.Patch(edgecolor="#96cbff", facecolor='#96cbff', label='Presence of person(s)')]
mlines.Line2D([], [], color='salmon', markersize=15, label='Insufficient level', linestyle='--'),
mlines.Line2D([], [], color='limegreen', markersize=15, label='Acceptable level', linestyle='--'),
patches.Patch(edgecolor="#96cbff", facecolor='#96cbff', label='Presence of person(s)')]
self.ax.legend(handles=figure_legends)
if 1500 < concentration_top:
self.ax.set_ylim(top=concentration_top*1.1)
@ -132,6 +134,61 @@ class ExposureModelResult(View):
self.figure.canvas.draw()
class ExposureComparissonResult(View):
def __init__(self):
self.figure = matplotlib.figure.Figure(figsize=(9, 6))
ipympl_canvas(self.figure)
self.html_output = widgets.HTML()
self.ax = self.initialize_axes()
@property
def widget(self):
# Workaround to a bug with ipymlp, which doesn't work well with tabs
# unless the widget is wrapped in a container (it is seen on all tabs otherwise!).
return widgets.HBox([self.figure.canvas])
def initialize_axes(self) -> matplotlib.figure.Axes:
ax = self.figure.add_subplot(1, 1, 1)
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.set_xlabel('Time (hours)')
ax.set_ylabel('CO₂ concentration (ppm)')
ax.set_title('CO₂ Concentration')
return ax
def scenarios_updated(self, scenarios: typing.Sequence[ScenarioType], _):
updated_labels, updated_models = zip(*scenarios)
CO2_models = tuple(
model.dcs_instance() for model in updated_models
)
self.update_plot(CO2_models, updated_labels)
def update_plot(self, CO2_models: typing.Tuple[models.CO2ConcentrationModel, ...], labels: typing.Tuple[str, ...]):
[line.remove() for line in self.ax.lines]
start, finish = models_start_end(CO2_models)
colors=['blue', 'red', 'orange', 'yellow', 'pink', 'purple', 'green', 'brown', 'black' ]
ts = np.linspace(start, finish, num=250)
concentrations = [[conc_model.concentration(t) for t in ts] for conc_model in CO2_models]
for label, concentration, color in zip(labels, concentrations, colors):
self.ax.plot(ts, concentration, label=label, color=color)
concentration_top = max([max(np.array(concentration)) for concentration in concentrations])
self.ax.set_ylim(bottom=400., top=concentration_top*1.1)
self.ax.set_xlim(left = start*0.95,
right = finish*1.05)
if 1500 < concentration_top:
self.ax.set_ylim(top=concentration_top*1.1)
else:
self.ax.set_ylim(top=1550)
self.ax.hlines([800, 1500], xmin=start*0.95, xmax=finish*1.05, colors=['limegreen', 'salmon'], linestyles='dashed')
self.ax.legend()
self.figure.canvas.draw()
class CO2Application(Controller):
def __init__(self) -> None:
@ -142,14 +199,16 @@ class CO2Application(Controller):
self._model_scenarios: typing.List[ScenarioType] = []
self._active_scenario = 0
self.multi_model_view = MultiModelView(self)
# self.comparison_view = ExposureComparissonResult()
self.comparison_view = ExposureComparissonResult()
self.current_scenario_figure = ExposureModelResult()
self._results_tab = widgets.Tab(children=(
self.current_scenario_figure.widget,
self.comparison_view.widget,
# self._debug_output,
))
# for i, title in enumerate(['Current scenario', 'Scenario comparison', "Debug"]):
# self._results_tab.set_title(i, title)
self._results_tab.titles = ['Current scenario', 'Scenario comparison', "Debug"]
self.widget = widgets.HBox(
children=(
self.multi_model_view.widget,
@ -182,6 +241,18 @@ class CO2Application(Controller):
return index, name, model
else:
raise ValueError("Model not found")
def rename_scenario(self, model_id, new_name):
index, _, model = self._find_model_id(model_id)
self._model_scenarios[index] = (new_name, model)
self.notify_scenarios_changed()
def remove_scenario(self, model_id):
index, _, model = self._find_model_id(model_id)
self._model_scenarios.pop(index)
if self._active_scenario >= index:
self._active_scenario = max(self._active_scenario - 1, 0)
self.notify_scenarios_changed()
def set_active_scenario(self, model_id):
index, _, model = self._find_model_id(model_id)
@ -195,12 +266,14 @@ class CO2Application(Controller):
"""
self.multi_model_view.scenarios_updated(self._model_scenarios, self._active_scenario)
self.comparison_view.scenarios_updated(self._model_scenarios, self._active_scenario)
def notify_model_values_changed(self):
"""
Occurs when *any* value in *any* of the scenarios has been modified.
"""
self.current_scenario_figure.update(self._model_scenarios[self._active_scenario][1].dcs_instance())
self.comparison_view.scenarios_updated(self._model_scenarios, self._active_scenario)
class ModelWidgets(View):
@ -593,7 +666,7 @@ class MultiModelView(View):
self.add_tab(scenario_name, model)
model_scenario_ids.append(id(model))
tab_index = self._tab_model_ids.index(id(model))
self.widget.set_title(tab_index, scenario_name)
self.widget.titles = [scenario_name for (scenario_name, _) in model_scenarios]
# Any remaining model_scenario_ids are no longer needed, so remove
# their tabs.
@ -661,4 +734,14 @@ class MultiModelView(View):
# last scenario, so this should be controlled in the remove_tab method.
buttons_w_delete = widgets.HBox(children=(duplicate_button, delete_button))
buttons = duplicate_button if len(self._tab_model_ids) < 2 else buttons_w_delete
return widgets.VBox(children=(buttons, rename_text_field))
return widgets.VBox(children=(buttons, rename_text_field))
def models_start_end(models: typing.Sequence[models.CO2ConcentrationModel]) -> typing.Tuple[float, float]:
"""
Returns the earliest start and latest end time of a collection of v objects
"""
emitters_start = min(model.CO2_emitters.presence.boundaries()[0][0] for model in models)
emitters_finish = min(model.CO2_emitters.presence.boundaries()[-1][1] for model in models)
return emitters_start, emitters_finish

View file

@ -46,6 +46,7 @@
<li><a href="{{ get_calculator_url() }}" class="{{ "header-navbar nav-link active" if "calculator/" == active_page else "header-navbar nav-link" }}">Calculator</a></li>
<li><div class="d-flex"><span class="d-flex align-self-center submenu-division"></span><a href="{{ get_calculator_url() }}/user-guide" class="{{ "header-navbar nav-link active" if "user-guide" in active_page else "header-navbar nav-link" }}">User Guide</a></div></li>
<li><a href="/expert-app" class="{{ "header-navbar nav-link active" if "/expert-app" == active_page else "header-navbar nav-link" }}">Expert app (beta)</a></li>
<li><a href="/CO2-app" class="{{ "header-navbar nav-link active" if "/CO2-app" == active_page else "header-navbar nav-link" }}">CO₂ Simulator</a></li>
</ul>
</li>
</div>