Hello,
First time posting, i hope my post is clear enough.
VERSIONS
Dash 2.17.1
Plotly 5.20.0
Dash-core-components 2.0.0
Dash-html-components 2.0.0
Dash-table 5.0.0
GOAL
I am coding an app that lets a user choose the sheet of an excel file with a dropdown. After choosing the excel sheet, a table stores the sheet content and dropdowns appear and they are populated with the column names from this sheet, to let the user choose which excel column corresponds to the expected column type. This is used to perform calculations without hardcoding the column names. Once a button is pressed, graphs and data tables appear as well as new dropdowns that are meant to interact with one the graphs.
The aim here is to guide the user and make things appear sequentially. In the end, i would like the possibility to use any excel file that the user can drop (with an upload component).
PROBLEM
The layout is simple as I need just the first dropdown for excel sheet selection, and divs to receive the components.
Components are created inside callbacks. It works well until the last dropdowns to update the last graph. Using those doesn’t trigger the corresponding callback.
app.layout = html.Div(
[
html.H3(children="Start by selecting the excel sheet to import"),
dcc.Dropdown(
id="excel_tab_dropdown",
options=list(sheets),
placeholder="Select Excel tab to import",
persistence=True,
),
dcc.Loading(
type="default",
children=[
dash_table.DataTable(
id="df_table",
page_size="15",
style_table={"height": "300px", "overflowX": "auto"},
persistence=True,
)
],
),
html.Div(id="raw_data_div"),
html.Div(id="processed_data_div"),
]
)
###### RAW DATA FRAME TABLE AND COLUMN DROPDOWN ######
@callback(
Output(component_id="df_table", component_property="data"),
Output(component_id="df_table", component_property="columns"),
Output(component_id="raw_data_div", component_property="children"),
Input(component_id="excel_tab_dropdown", component_property="value"),
)
def update_df_table(selected_excel_sheet):
data = {}
columns = []
children = []
if selected_excel_sheet != None:
df = pd.read_excel(data_file, sheet_name=selected_excel_sheet)
data = df.to_dict("records")
columns = [{"name": i, "id": i} for i in df.columns]
options = df.columns
children = [
html.H3(children="Select the names for the corresponding columns"),
####### CLASS DROPDOWN #######
html.Div(
style={"display": "inline-block", "margin-left": "20px"},
children=[
"Class Column",
dcc.Dropdown(
id="class_dropdown", options=options, persistence=True)]),
...
html.Button("Generate Plots", id=("plot_button"), n_clicks=0)
]
return data, columns, children
###### GENERATE PLOTS AFTER COLUMN SELECTION ######
@callback(
Output(component_id="processed_data_div", component_property="children"),
Input(component_id="plot_button", component_property="n_clicks"),
State(component_id="df_table", component_property="data"),
State(component_id="class_dropdown", component_property="value"),
State(component_id="dbe_dropdown", component_property="value"),
State(component_id="formula_dropdown", component_property="value"),
State(component_id="c_dropdown", component_property="value"),
State(component_id="n_dropdown", component_property="value"),
State(component_id="o_dropdown", component_property="value"),
State(component_id="s_dropdown", component_property="value"),
State(component_id="samples_dropdown", component_property="value"),
prevent_initial_call=True,
)
def generate_plots(
n_clicks,
table_data,
class_col,
dbe_col,
formula_col,
c_col,
n_col,
o_col,
s_col,
samples_cols,
):
if table_data is not None:
###### VARIABLES TO COMPUTE ######
...
###### SHOW GRAPHS AND TABLES######
children = [
...,
html.Div(
children=[
html.Label(
["Class : "],
style={"font-weight": "bold", "text-align": "center"},
),
###Problematic dropdowns####
dcc.Dropdown(
options=classes,
value=classes[0],
id="dbe_class_dropdown",
placeholder="Class",
style={"display": "inline-block", "margin-right": "10px"},
),
html.Label(
["DBE : "],
style={"font-weight": "bold", "text-align": "center"},
),
dcc.Dropdown(
options=dbe,
value=[],
id="dbe_dbe_dropdown",
placeholder="DBE",
multi=True,
style={"display": "inline-block", "margin-right": "10px"},
),
],
style={
"display": "flex",
"align-items": "center",
"justify-content": "center",
"margin": "auto",
},
),
html.Div(children=[dcc.Graph(id="fig_dbe_classes")]),
]
return children
###This callback is not triggered###
###### DBE GRAPHS CALLBACK ######
@callback(
Output(component_id="fig_dbe_classes", component_property="figure"),
Input(component_id="dbe_class_dropdown", component_property="value"),
Input(component_id="dbe_dbe_dropdown", component_property="value"),
State(component_id="dbe_classes_df_table", component_property="data"),
State(component_id="classes_dropdown", component_property="value"),
State(component_id="dbe_dropdown", component_property="value"),
State(component_id="n_dropdown", component_property="value"),
State(component_id="o_dropdown", component_property="value"),
)
def update_dbe_graphs(
class_selection, dbe_selection, table_data, class_col, dbe_col, n_col, o_col
):
dbe_classes_df = pd.DataFrame.from_records(table_data)
print(dbe_classes_df)
if dbe_selection == []:
fig_dbe_classes = px.scatter(
data_frame=dbe_classes_df[(dbe_classes_df[class_col] == class_selection)],
x="#C",
y="DBE",
color="Intensity",
opacity=0.8,
hover_data=[n_col, o_col],
facet_col="Sample",
facet_col_wrap=5,
)
else:
fig_dbe_classes = px.scatter(
data_frame=dbe_classes_df[
(dbe_classes_df[class_col] == class_selection)
& (dbe_classes_df[dbe_col].isin(dbe_selection))
],
x="#C",
y="DBE",
color="Intensity",
opacity=0.8,
hover_data=[n_col, o_col],
facet_col="Sample",
facet_col_wrap=5,
)
return fig_dbe_classes
QUESTIONS
Is it an expected behaviour that these dropdowns don’t trigger the callback ?
Is my coding the proper way of showing sequentially the components ?