From 550a5821ab23e7c689598db861ae83210983d084 Mon Sep 17 00:00:00 2001 From: markus Date: Wed, 11 Nov 2020 11:34:15 +0100 Subject: [PATCH 1/3] Add tabs to interface --- cara/apps/expert.py | 72 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 64 insertions(+), 8 deletions(-) diff --git a/cara/apps/expert.py b/cara/apps/expert.py index d4037ae5..d87ad3f3 100644 --- a/cara/apps/expert.py +++ b/cara/apps/expert.py @@ -402,17 +402,73 @@ class CARAStateBuilder(state.StateBuilder): class ExpertApplication: def __init__(self): - self.model_state = state.DataclassInstanceState( - models.ExposureModel, - state_builder=CARAStateBuilder(), - ) - self.model_state.dcs_update_from(baseline_model) + default_scenario = state.DataclassInstanceState( + models.Model, + state_builder=CARAStateBuilder(), + ) + default_scenario.dcs_update_from(baseline_model) # For the time-being, we have to initialise the select states. Careful # as values might not correspond to what the baseline model says. - self.model_state.concentration_model.infected.mask.dcs_select('No mask') + default_scenario.infected.mask.dcs_select('No mask') + self.scenarios = (default_scenario,) + self.scenario_names = ('Scenario 1',) + self.views = (WidgetView(default_scenario),) + self.selected_tab = 0 + self.tabs = (widgets.VBox(children=(self.build_settings_menu(0), self.views[0].present())),) + self.tab_widget = widgets.Tab(children=self.tabs) + self.display_titles() - self.view = WidgetView(self.model_state) + def display_titles(self): + for i, name in enumerate(self.scenario_names): + self.tab_widget.set_title(i, name) + + def update_tab_widget(self): + self.tab_widget.children = self.tabs + self.display_titles() + + def build_settings_menu(self, tab_index): + delete_button = widgets.Button(description='Delete Scenario', button_style='danger') + rename_text_field = widgets.Text(description='Rename Scenario:', value=self.scenario_names[tab_index], + style={'description_width': 'auto'}) + duplicate_button = widgets.Button(description='Duplicate Scenario', button_style='success') + + def on_delete_click(b): + self.scenario_names = tuple_without_index(self.scenario_names, tab_index) + self.scenarios = tuple_without_index(self.scenarios, tab_index) + self.views = tuple_without_index(self.views, tab_index) + self.selected_tab = min(0, self.selected_tab - 1) + self.tabs = tuple(widgets.VBox(children=(self.build_settings_menu(i), view.present())) + for i, view in enumerate(self.views)) + self.update_tab_widget() + + def on_rename_text_field(change): + self.scenario_names = tuple(change['new'] if i == tab_index else value + for i, value in enumerate(self.scenario_names)) + self.update_tab_widget() + + def on_duplicate_click(b): + self.scenario_names += (self.scenario_names[tab_index] + " (copy)",) + new_scenario = state.DataclassInstanceState( + models.Model, + state_builder=CARAStateBuilder(), + ) + new_scenario.dcs_update_from(self.scenarios[tab_index].dcs_instance()) + self.scenarios += (new_scenario,) + + self.views += (WidgetView(new_scenario),) + self.tabs += (widgets.VBox(children=(self.build_settings_menu(len(self.scenario_names) - 1), self.views[-1].present())),) + self.update_tab_widget() + + delete_button.on_click(on_delete_click) + duplicate_button.on_click(on_duplicate_click) + rename_text_field.observe(on_rename_text_field, 'value') + buttons = duplicate_button if tab_index == 0 else widgets.HBox(children=(duplicate_button, delete_button)) + return widgets.VBox(children=(buttons, rename_text_field)) @property def widget(self): - return self.view.present() + return self.tab_widget + + +def tuple_without_index(t: typing.Tuple, index: int) -> typing.Tuple: + return t[:index] + t[index + 1:] From 48e0b9d6cce82d92c9d16c4e5abb294f3add9a15 Mon Sep 17 00:00:00 2001 From: markus Date: Wed, 11 Nov 2020 11:35:28 +0100 Subject: [PATCH 2/3] update test --- cara/tests/apps/test_expert_app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cara/tests/apps/test_expert_app.py b/cara/tests/apps/test_expert_app.py index af441e99..1cb55d3b 100644 --- a/cara/tests/apps/test_expert_app.py +++ b/cara/tests/apps/test_expert_app.py @@ -6,4 +6,4 @@ def test_app(): # do anything fancy to verify how it looks etc., we leave that for manual # testing. expert_app = cara.apps.ExpertApplication() - assert expert_app.model_state.concentration_model.room.volume == 75 + assert expert_app.scenario_names[0] == "Scenario 1" From 55e1a1a8321ce54b265ac59732ee4fdfe3b6620d Mon Sep 17 00:00:00 2001 From: markus Date: Wed, 11 Nov 2020 12:01:28 +0100 Subject: [PATCH 3/3] fix ExposureModel attribute bugs --- cara/apps/expert.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cara/apps/expert.py b/cara/apps/expert.py index d87ad3f3..3a5f0326 100644 --- a/cara/apps/expert.py +++ b/cara/apps/expert.py @@ -403,13 +403,13 @@ class CARAStateBuilder(state.StateBuilder): class ExpertApplication: def __init__(self): default_scenario = state.DataclassInstanceState( - models.Model, + models.ExposureModel, state_builder=CARAStateBuilder(), ) default_scenario.dcs_update_from(baseline_model) # For the time-being, we have to initialise the select states. Careful # as values might not correspond to what the baseline model says. - default_scenario.infected.mask.dcs_select('No mask') + default_scenario.concentration_model.infected.mask.dcs_select('No mask') self.scenarios = (default_scenario,) self.scenario_names = ('Scenario 1',) self.views = (WidgetView(default_scenario),) @@ -449,7 +449,7 @@ class ExpertApplication: def on_duplicate_click(b): self.scenario_names += (self.scenario_names[tab_index] + " (copy)",) new_scenario = state.DataclassInstanceState( - models.Model, + models.ExposureModel, state_builder=CARAStateBuilder(), ) new_scenario.dcs_update_from(self.scenarios[tab_index].dcs_instance())