Thanks for creating dash-plotly which has made building an analytic webpage easier.
I recently built a dashboard using dash and flask. The dashboard has three tabs, the first two tabs will take a few dozens of inputs, while the last tab shows the final results. In order for the selected input values to be shared across three tabs, I implemented a trick from another post- first use dcc.store to store the configs in each tab (storage type = memory) then use the following code to store the states.
@dash_app.callback(
Output("tab1_config", "data"),
Input("A", "value"),
[State("tab1_config", "data")]
)
tab1_inputs = [Input("A", "value")]
def store_state_global_config(*args):
"""
Store inputs into states
"""
input_names = [item.component_id for item in tab1_inputs]
kwargs_dict = dict(zip(input_names, args))
return kwargs_dict
At first, I thought the website is working fine.
(1) The input values in the first two tabs persists when switching from one tab to another
(2) When F5 is hit, it restores the original values
However, I ran into another problem. After I click on the action button to compute the results, the inputs values will no longer return to the default after F5 is clicked (but stayed at the values at the last time the action button is clicked)
See the below for the full code: I would highly appreciate if someone can point me to the right direction.
import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import plotly.express as px
import dash_bootstrap_components as dbc
from pytz import timezone
from dash import html, dcc, dash_table
from dash.dependencies import Input, Output, State
import plotly.express as px
import pandas as pd
from datetime import datetime
from flask import Flask
CONFIG = {
'A' : 100,
"B" : 200
}
app = Flask(__name__)
dash_app = dash.Dash(
__name__,
server=app,
suppress_callback_exceptions=True,
external_stylesheets=[dbc.themes.BOOTSTRAP, dbc.icons.BOOTSTRAP],
url_base_pathname="/",
)
## Final layout on CAISO Day-Ahead Page
def layout():
"""
layout defines your overall layout of a page
"""
welcome = jumbotron()
tabs = tab_collections()
return html.Div(
[
dcc.Store(id="tab1_config", storage_type="memory"),
dcc.Store(id="tab2_config", storage_type="memory"),
welcome,
tabs,
]
)
def jumbotron():
"""
jumbotron is a UI component that can be used to display large eyecatching text across a page
"""
jumbotron_content = html.Div(
dbc.Container(
[
html.Center(
html.H1(
"Application name", className="display-3"
)
),
html.P(),
html.Center(
dcc.Markdown(
"Press **F5** to restore to default configuration",
className="lead",
style={"color": "#0000FF"},
)
),
],
fluid=True,
className="py-3",
),
className="p-3 bg-white rounded-3",
)
return jumbotron_content
def tab_collections():
"""
Generate tab layout
"""
return html.Div(
[
dcc.Tabs(
id="tab_collections",
value="tab1",
children=[
dcc.Tab(
label="Tab 1",
value="tab1",
),
dcc.Tab(
label="Tab 2",
value="tab2",
),
dcc.Tab(
label="Tab 3",
value="tab3",
),
],
),
html.Div(id="tab_content"),
]
)
def generate_inputs_tab1():
"""
Generate inputs for global_configuration tab
**global_configuration_tab**
"""
return [
Input("A", "value"),
]
def generate_inputs_tab2():
"""
Generate inputs for global_configuration tab
**global_configuration_tab**
"""
return [
Input("B", "value"),
]
tab1_inputs = generate_inputs_tab1()
tab2_inputs = generate_inputs_tab2()
tab1_config_states = [State("tab1_config", "data")]
tab2_config_states = [State("tab2_config", "data")]
@dash_app.callback(
Output("tab1_config", "data"),
tab1_inputs,
tab1_config_states
)
def store_state_global_config(*args):
"""
Store inputs into states
"""
input_names = [item.component_id for item in tab1_inputs]
kwargs_dict = dict(zip(input_names, args))
return kwargs_dict
@dash_app.callback(
Output("tab2_config", "data"),
tab2_inputs,
tab2_config_states
)
def store_state_global_config(*args):
"""
Store inputs into states
"""
input_names = [item.component_id for item in tab2_inputs]
kwargs_dict = dict(zip(input_names, args))
return kwargs_dict
@dash_app.callback(
Output("tab_content", "children"),
Input("tab_collections", "value"),
)
def tab_selector(tab):
"""
Generate tab selector
"""
if tab == "tab1":
tab_layout = html.Div([
html.H3("Enter the value for A:"),
html.P(),
dbc.Input(
id="A",
value=CONFIG["A"],
type="number",
style={"width": "35%"},
persistence=True,
persistence_type="memory",
),
]) #global_configuration_tab_layout(CONFIG)
elif tab == "tab2":
tab_layout = html.Div([
html.H3("Enter the value for B:"),
html.P(),
dbc.Input(
id="B",
value=CONFIG["B"],
type="number",
style={"width": "35%"},
persistence=True,
persistence_type="memory",
),
]) #node_specific_configuration_tab_layout(CONFIG)
else:
tab_layout = html.Div([
html.H3("Final Output:"),
html.P(),
dbc.Col(
dbc.Button(
"Compute A + B",
color="success",
size="lg",
id="action_button",
className="me-1",
active=True,
n_clicks=0,
style={
"font-size": "30px",
"width": "500px",
"display": "inline-block",
"margin-bottom": "10px",
"margin-right": "5px",
"height": "100px",
"verticalAlign": "top",
},
)
),
html.P(),
dbc.Spinner(
html.Div(
id="results",
children=[],
style={"padding": 10, "flex": 1},
),
color="primary",
),
]) #energy_bid_as_offer_tab_layout(CONFIG)
return tab_layout
@dash_app.callback(
Output("results", "children"),
[Input("action_button", "n_clicks")],
[
State("tab1_config", "data"),
State("tab2_config", "data"),
],
)
def compute_results(n, tab1_config, tab2_config):
"""
Generate plot/tables for forecasted hourly price
**global_configuration_tab**
"""
if n == 0:
output_layout = html.H4(
"Please click on the button to display resultws",
style={"margin-left": "15px"},
)
else:
selected_config = CONFIG
selected_config["A"] = tab1_config["A"]
selected_config["B"] = tab2_config["B"]
if (
selected_config["A"] is not None
and selected_config["B"] is not None
):
C = selected_config["A"] + selected_config["B"]
output_layout = (
html.Div(
[
html.H4("Result", className="display-9"),
html.H5(f"{C}"),
]
),
)
return output_layout
dash_app.layout = layout()
if __name__ == '__main__':
dash_app.run_server(debug=True)
Please help!
Thanks,
Anson