I am relatively new to Dash, and thought I understood the callbacks pretty well. However, I am now in a situation where it seems I need to have all callbacks within one callback, as my program works fine when the one is called.
When I have multiple callbacks, they work fine individually; meaning that if I comment one out the callback works as desired. I am able to get the correct functionality using context.triggered, but I do not want to have everything in one massive callback, and I feel like this is an issue with my understanding. I have tried limiting to one input on the callback, but that still does not work. Am I passing the whole app layout to the callback?
If I am, how do I limit what is being passed beyond the ids? How
can callbacks?
An adapted working example is below:
import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import dash_table
from dash.dependencies import Input, Output, State
import plotly.express as px
from dash import callback_context, no_update
import webbrowser
from flask import request
chrome_path = 'C:/Program Files (x86)/Google/Chrome/Application/chrome.exe %s'
df = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/Mining-BTC-180.csv").drop('Unnamed: 0',axis=1)
selected_cols = ['Number-transactions', 'Output-volume(BTC)',
'Market-price', 'Hash-rate', 'Cost-per-trans-USD', 'Mining-revenue-USD',
'Transaction-fees-BTC']
class Parameter_Viewer():
def __init__(self, df, **kwargs):
revenue_plot = px.scatter(df, x="Date", y="Mining-revenue-USD",title='Mining-revenue-USD')
Hash_plot = px.scatter(df, x="Date", y="Hash-rate",title='Hash-rate')
parameter_table = dash_table.DataTable(
id='db-table',
columns=[{"name": i, "id": i} for i in selected_cols],
data=df.to_dict('records'),
#page_action="native",
page_action='none',
style_table={'height': '300px', 'overflowY': 'auto'},
editable=True
)
app = dash.Dash()
app.layout = html.Div(
children=[
#first row
html.Div(
children=[
html.Div(children=[
html.H1("Parameter", className="menu-title"),
dcc.Dropdown(
id="parameter-filter",
options=[
{"label": parameter, "value": parameter} for parameter in df.columns
],
value="Mining-revenue-USD",
clearable=False,
className="dropdown",
),]),
html.Div(children=[
html.H1("Data Type", className="menu-title"),
dcc.Dropdown(
id="data-selector",
options=[
{"label": data_col, "value": data_col} for data_col in selected_cols
],
value="Hash-rate",
clearable=False,
className="dropdown",
),]),
html.Button("Close Viewer", id="Close_Btn")
]
),
html.Div(children=[
html.H1(children="Database Analytics",),
parameter_table]),
html.Div(children=[
html.Div(
dcc.Graph(id='param-plot',figure=revenue_plot),
style={"width":'50%', "margin": 0, 'display': 'inline-block'},className="six columns"),
html.Div(
dcc.Graph(id='param-plot2',figure=Hash_plot),
style={"width":'50%', "margin": 0, 'display': 'inline-block'},className="six columns")],className="row")
])
#app.callback(
[Output("db-table", "data"), Output("param-plot", "figure"), Output("param-plot2", "figure")],
[Input("parameter-filter", "value"),Input("data-selector", "value"),Input('db-table', 'data')])#,prevent_initial_call=True)
def update_charts(parameter,data_type,table_data):
changed_inputs = [x["prop_id"]for x in callback_context.triggered]
if changed_inputs[0] == 'parameter-filter.value':
df = pd.DataFrame.from_dict(table_data)
ua_plot = px.scatter(df, x="Date", y=data_type)
aa_plot = px.scatter(df, x="Date", y=parameter)
return table_data, ua_plot, aa_plot
elif changed_inputs[0] == 'db-table.data':
df = pd.DataFrame.from_dict(table_data)
ua_plot = px.scatter(df, x="Date", y=data_type)
aa_plot = px.scatter(df, x="Date", y=parameter)
return no_update, ua_plot, aa_plot
else:
return no_update,no_update,no_update
#app.callback(Output("db-table", "data"),Input("Close_Btn", "n_clicks"),prevent_initial_call=True)
def close_browser(n_clicks):
print('In close callback\n')
if n_clicks>0:
self.shutdown()
return no_update
host='127.0.0.1'
port='8050'
url = 'http://{}:{}/'.format(host,port)
webbrowser.get(chrome_path).open(url)
app.run_server(debug=False)
def shutdown(self):
func = request.environ.get('werkzeug.server.shutdown')
if func is None:
raise RuntimeError('Not running with the Werkzeug Server')
func()
Parameter_Viewer(df)```
It's easier to consider base cases of what your callbacks may look like and then extend upon them. From my experience, the main reason I typically structure a callback is to handle a particular output caused by a particular input.
E.g.
If output O1 is an effect of input I1, then I make a callback C1(I1 → O1).
If output O1 is an effect of Inputs I1 and I2, then I make a callback C1([I1, I2] → O1)
If you happen to have multiple outputs that are effects of an input, then you combine those outputs in the same callback.
E.g.
If output O1 is an effect of input I1 and output O2 is an effect of input I1, then I make a callback C1(I1 → [O1, O2])
Also, keep in mind that Dash doesn't allow multiple callbacks to have the same output component (e.g., a particular component property can be associated with one and only one callback). In which case, if you have a particular output that requires to be updated simultaneously with another, but is not necessarily an effect of that same input, then you should still combine the outputs into the single callback.
E.g.
If output O1 is an effect of input I1 and output O2 must be updated with O1, then I make a callback C1(I1 → [O1, O2])
I'm not sure why in the comment you tried using State() instead of Input(), as State() is used in the event a property must still be read as input, but doesn't necessarily have to be triggered.
So using the above logic, e.g.,
if output O1 that is an effect of input I1 and is not an effect of Input (which we now call State) S1, but still requires S1, then I make a callback C1([I1, S1] → [O1])
It's normal to have large callbacks that have many inputs and outputs as long as the callback is not trying to do too many disjointed things at once (otherwise when you go back to look at your code you'll spend a lot of time trying to remember what you did believe me :)).
If you're a beginner like me. You're probably looking for the most valuable information, in my case, in Daniel Al Mouiee's answer:
Dash doesn't allow multiple callbacks to have the same output component
Related
so today, I'm trying to create a simple dahsboard with data from multiple dataframes. I use the dropdown to select the dataframe to show in the plot. However, when I run this code, I got callback error in the web page says
Callback error updating loglog.figure
Traceback (most recent call last):
File "C:\Users\nahar\AppData\Local\Temp/ipykernel_1556/982666652.py", line 63, in build_graph
TypeError: string indices must be integers
I don't understand why this occurs, here is the code
logs = ['CALI','RDEP','GR','RHOB','NPHI','SP','DTC']
colors = ['black','firebrick','green','mediumaquamarine','royalblue','goldenrod','lightcoral']
log_cols = np.arange(1,8)
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.SANDSTONE], meta_tags=[
{"name": "viewport", "content": "width=device-width, initial-scale=1"}
])
server = app.server
app.config.suppress_callback_exceptions = True
app.layout = dbc.Container([
dbc.Row([
dbc.Col(html.H1('FORCE 2020 Well Log Challange Dashboard',
className='text-center mb-4'),
width=12)
]),
dbc.Row([
dbc.Col([
dcc.Dropdown(id='droplog',
options=[
{'label':'15/9-13','value':'well1'},
{'label':'15/9-15','value':'well2'},
{'label':'15/9-17','value':'well3'},
{'label':'16/1-2','value':'well4'},
{'label':'16/1-6 A','value':'well5'},
{'label':'16/10-1','value':'well6'},
{'label':'16/10-2','value':'well7'},
{'label':'16/10-3','value':'well8'},
{'label':'16/10-5','value':'well9'},
{'label':'16/11-1 ST3','value':'well10'},
{'label':'16/2-11 A','value':'well11'},
{'label':'16/2-16','value':'well12'}
], multi=False, value='well1'),
dcc.Graph(id='loglog',figure={})
])
]),
dbc.Row([
])
])
#app.callback(
Output(component_id='loglog',component_property='figure'),
[Input(component_id='droplog',component_property='value')]
)
def build_graph(plot_chosen):
logplot = make_subplots(rows=1, cols=len(logs), shared_yaxes=True)
for i in range (len(logs)):
if i == 1:
logplot.add_trace(go.Scatter(x=plot_chosen[logs[i]], y=plot_chosen['DEPTH_MD'],
name=logs[i], line_color=colors[i]),row=1, col=log_cols[i])
logplot.update_xaxes(type='log',row=1, col=log_cols[i], title_text=logs[i],
tickfont_size=12, linecolor='#585858')
else:
logplot.add_trace(go.Scatter(x=plot_chosen[logs[i]], y=plot_chosen['DEPTH_MD'],
name=logs[i], line_color=colors[i]),row=1, col=log_cols[i])
logplot.update_xaxes(col=log_cols[i], title_text=logs[i], linecolor='#585858')
logplot.update_xaxes(showline=True, linewidth=2, linecolor='black', mirror=True, ticks='inside', tickangle= 0)
logplot.update_yaxes(tickmode='linear', tick0=0,dtick=250, showline=True, linewidth=2, linecolor='black',
mirror=True, ticks='outside')
logplot.update_yaxes(row=1, col=1, autorange='reversed')
logplot.update_layout(height=750, width=1200, showlegend=False)
logplot.update_layout(plot_bgcolor = "rgba(0,0,0,0)", paper_bgcolor = "rgba(0,0,0,0)")
return logplot
if __name__=='__main__':
app.run_server(debug=True,use_reloader=False)
and the input data used is in the form of many dataframes, one of which is like this
for well 1
I tried to run defined function only, which build_graph, and it worked like a charm. The result of figure is shown here
I think I know what the problem is: the variable plot_chosen which is the argument in the function build_graph is a string. As a result you can not type y=plot_chosen['DEPTH_MD']. Although after choosing 15/9-13 on the Dropdown menu it has a value of well1, it does not represent the dataframe but a simple string. Try creating for example a dictionary with the dataframes
dataframes = {'well1': well1, 'well2': well2, ...}
and then choose the appropriate DataFrame from the dictionary.
So for example when the user chooses 15/9-13 on the dropdown you will get plot_chosen='well1' and you can simply get the dataframe well1 by using dataframes[plot_chosen].
I have a 2D plotly graph with a hover feature. When you hover over each point, the label (e.g. 'image 2, cluster 1') associated with that point appears. I'd like for label to be appended onto an existing list if I were to click on the point (rather than just hover over it). The reason why is that I'd later like to use the data of this point to perform another task. Is there an example online that demonstrates how to do this-- have looked through the documentation but haven't found something for this yet. Thanks!
The hoverData that is available to you by default, with some sample data, is this:
{
"points": [
{
"curveNumber": 1,
"pointNumber": 7,
"pointIndex": 7,
"x": 1987,
"y": 74.32,
"bbox": {
"x0": 420.25,
"x1": 426.25,
"y0": 256,
"y1": 262
}
}
]
}
I'm not quite sure what you mean by 'label', so I can only assume that it would be the name of a trace or something similar, like in this example from the Plotly docs:
But as you can see, that's not readily available in the hoverData dict. This means that you'll have to use this information to reference your figure structure as well, so that you end up with something like this:
[['New Zealand', 2002, 79.11]]
And that's not a problem as long as you're willing to use Plotly Dash. I've made a complete setup for you that should meet your requirements. In the app in the image below you'll find a figure along with two output fields for strings. The first field shows the info from that last point you've clicked in the figure. On every click, a new element is added to a list named store. The last fields shows the complete information from the same click.
The answer to your question is, yes, there is a way to save the data of a clicked point in a list. And one way to do so is through the following callback that uses clickdata to reference your figure object, store those references in a list, and append new elements every time you click a new element.
App
Complete code:
import json
from textwrap import dedent as d
import pandas as pd
import plotly.graph_objects as go
import numpy as np
import dash
from dash import dcc
import dash_html_components as html
import plotly.express as px
from dash.dependencies import Input, Output
from jupyter_dash import JupyterDash
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
# app info
app = JupyterDash(__name__)
styles = {
'pre': {
'border': 'thin lightgrey solid',
'overflowX': 'scroll'
}
}
# data
df = px.data.gapminder().query("continent=='Oceania'")
# plotly figure
fig = px.line(df, x="year", y="lifeExp", color="country", title="No label selected")
fig.update_traces(mode="markers+lines")
app.layout = html.Div([
dcc.Graph(
id='figure1',
figure=fig,
),
html.Div(className
='row', children=[
html.Div([
dcc.Markdown(d("""Hoverdata using figure references""")),
html.Pre(id='hoverdata2', style=styles['pre']),
], className='three columns'),
html.Div([
dcc.Markdown(d("""
Full hoverdata
""")),
html.Pre(id='hoverdata1', style=styles['pre']),
], className='three columns')
]),
])
# container for clicked points in callbacks
store = []
#app.callback(
Output('figure1', 'figure'),
Output('hoverdata1', 'children'),
Output('hoverdata2', 'children'),
[Input('figure1', 'clickData')])
def display_hover_data(hoverData):
if hoverData is not None:
traceref = hoverData['points'][0]['curveNumber']
pointref = hoverData['points'][0]['pointNumber']
store.append([fig.data[traceref]['name'],
fig.data[traceref]['x'][pointref],
fig.data[traceref]['y'][pointref]])
fig.update_layout(title = 'Last label was ' + fig.data[traceref]['name'])
return fig, json.dumps(hoverData, indent=2), str(store)
else:
return fig, 'None selected', 'None selected'
app.run_server(mode='external', port = 7077, dev_tools_ui=True,
dev_tools_hot_reload =True, threaded=True)
You need to use callbacks to perform this type of action (register on_click()). Have defined clicked as a list of clicked points. Demonstrated how this can be achieved with ipwidgets or dash
ipwidgets
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
import ipywidgets as widgets
from pathlib import Path
import json
x = np.random.uniform(-10, 10, size=50)
y = np.sin(x)
clicked = []
# construct figure that has holders for points, interpolated line and final lines
fig = go.FigureWidget(
[
go.Scatter(x=x, y=y, mode="markers", name="base_points"),
]
)
fig.update_layout(template="simple_white")
out = widgets.Output(layout={"border": "1px solid black"})
out.append_stdout("Output appended with append_stdout\n")
# create our callback function
#out.capture()
def base_click(trace, points, selector):
global clicked
clicked.append(points.__dict__)
fig.data[0].on_click(base_click)
widgets.HBox([fig, out])
dash
from jupyter_dash import JupyterDash
import dash
from dash.dependencies import Input, Output, State
import numpy as np
import json
clicked = []
# Build App
app = JupyterDash(__name__)
app.layout = dash.html.Div(
[
dash.dcc.Graph(
id="fig",
figure=go.Figure(go.Scatter(x=x, y=y, mode="markers", name="base_points")),
),
dash.html.Div(id="debug"),
]
)
#app.callback(
Output("debug", "children"),
Input("fig", "clickData"),
)
def point_clicked(clickData):
global clicked
clicked.append(clickData)
return json.dumps(clickData)
# Run app and display result inline in the notebook
app.run_server(mode="inline")
I'm trying to create a dashboard in Pycharm using dash. Here is the error I keep receiving,
html.Div(dcc.Graph(id='line-plot')),
TypeError: Graph() takes no arguments
And below is a snippet of my code where the error is being found (bottom of code). This code ran fine and I was about to populate the dashboard without receiving any errors inside IBM's python environment. I'm assuming I have to tweak something
# TASK 3 - UPDATE LAYOUT COMPONENETS
# html.H1 tag for title , style, and overall font size
# html.Div & dcc.Input() tag to set inputs of the dashboard
# Update output componenent 2nd html.Div to layout the graph dcc.Graph()
app.layout = html.Div(children=[html.H1('Airline Performance Dashboard',
style={'textAlign': 'center', 'color': '#503D36',
'font-size': 40}),
html.Div(["Input Year: ", dcc.Input(id='input-year', value='2010',
type='number',
style={'height': '50px', 'font-size': 35}), ],
style={'font-size': 40}),
html.Br(),
html.Br(),
html.Div(dcc.Graph(id='line-plot')),
])
Here is the rest of the code,
# TASK 4 - ADD APPLICATION CALL BACK FUNCTION and outputs / inputs
# add callback decorator
#app.callback(Output(component_id='line-plot', component_property='figure'),
Input(component_id='input-year', component_property='value'))
# Add computation to callback function and return graph
def get_graph(entered_year):
# Select 2019 data
df = airline_data[airline_data['Year'] == int(entered_year)]
# Group the data by Month and compute average over arrival delay time.
line_data = df.groupby('Month')['ArrDelay'].mean().reset_index()
# TASK 5 - UPDATE CALL BACK FUNCTION go.Figure(data=) and update fig.update_layout()
fig = go.Figure(
data=go.Scatter(x=line_data['Month'], y=line_data['ArrDelay'], mode='lines', marker=dict(color='green')))
fig.update_layout(title='Month vs Average Flight Delay Time', xaxis_title='Month', yaxis_title='ArrDelay')
return fig
# Run the app
if __name__ == '__main__':
app.run_server()
Its safe to say I need an adult.
have added import and simulation of data frame
all other code runs without issue on plotly 5.1.0
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import plotly.graph_objects as go
from jupyter_dash import JupyterDash
import numpy as np
app = JupyterDash(__name__)
# simulate data...
dr = pd.date_range("1-jan-2010", freq="W", periods=200)
airline_data = pd.DataFrame({"Year":dr.year, "Month":dr.month, "ArrDelay":np.random.uniform(2,5,len(dr))})
# TASK 3 - UPDATE LAYOUT COMPONENETS
# html.H1 tag for title , style, and overall font size
# html.Div & dcc.Input() tag to set inputs of the dashboard
# Update output componenent 2nd html.Div to layout the graph dcc.Graph()
app.layout = html.Div(children=[html.H1('Airline Performance Dashboard',
style={'textAlign': 'center', 'color': '#503D36',
'font-size': 40}),
html.Div(["Input Year: ", dcc.Input(id='input-year', value='2010',
type='number',
style={'height': '50px', 'font-size': 35}), ],
style={'font-size': 40}),
html.Br(),
html.Br(),
html.Div(dcc.Graph(id='line-plot')),
])
# TASK 4 - ADD APPLICATION CALL BACK FUNCTION and outputs / inputs
# add callback decorator
#app.callback(Output(component_id='line-plot', component_property='figure'),
Input(component_id='input-year', component_property='value'))
# Add computation to callback function and return graph
def get_graph(entered_year):
# Select 2019 data
df = airline_data[airline_data['Year'] == int(entered_year)]
# Group the data by Month and compute average over arrival delay time.
line_data = df.groupby('Month')['ArrDelay'].mean().reset_index()
# TASK 5 - UPDATE CALL BACK FUNCTION go.Figure(data=) and update fig.update_layout()
fig = go.Figure(
data=go.Scatter(x=line_data['Month'], y=line_data['ArrDelay'], mode='lines', marker=dict(color='green')))
fig.update_layout(title='Month vs Average Flight Delay Time', xaxis_title='Month', yaxis_title='ArrDelay')
return fig
app.run_server(mode="inline")
Context
In a dashboard using plotly Dash I need to perform an expensive download from DB only when a component (DataPicker with the period to consider and so to be downloaded from DB) is updated and then use the resulting DataFrame with other components (e.g. Dropdowns filtering the DataFrame) avoiding the expensive download process.
The docs suggests to use dash_core_components.Store as Output of a callback that return the DataFrame serielized in json and than use the Store as Input of other callbacks that needs to deserialize from json to DataFrame.
Serialization from/to JSON is slow, and each time I update a component it takes 30 seconds to update the plot just for that.
I tried to use faster serializations like pickle, parquet and feather but in the deserialization part I get an error stating that the object is empty (when using JSON no such error appear).
Question
Is it possible to perform serializations in Dash Store with faster methods like pickle, feather or parquet (they takes approx half of time for my dataset) than JSON? How?
Code
import io
import traceback
import pandas as pd
from datetime import datetime, date, timedelta
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output
from plotly.subplots import make_subplots
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
today = date.today()
app.layout = html.Div([
dbc.Row(dbc.Col(html.H1('PMC'))),
dbc.Row(dbc.Col(html.H5('analysis'))),
html.Hr(),
html.Br(),
dbc.Container([
dbc.Row([
dbc.Col(
dcc.DatePickerRange(
id='date_ranges',
start_date=today - timedelta(days=20),
end_date=today,
max_date_allowed=today, display_format='MMM Do, YY',
),
width=4
),
]),
dbc.Row(
dbc.Col(
dcc.Dropdown(
id='dd_ycolnames',
options=options,
value=default_options,
multi=True,
),
),
),
]),
dbc.Row([
dbc.Col(
dcc.Graph(
id='graph_subplots',
figure={},
),
width=12
),
]),
dcc.Store(id='store')
])
#app.callback(
Output('store', 'data'),
[
Input(component_id='date_ranges', component_property='start_date'),
Input(component_id='date_ranges', component_property='end_date')
]
)
def load_dataset(date_ranges_start, date_ranges_end):
# some expensive clean data step
logger.info('loading dataset...')
date_ranges1_start = datetime.strptime(date_ranges_start, '%Y-%m-%d')
date_ranges1_end = datetime.strptime(date_ranges_end, '%Y-%m-%d')
df = expensive_load_from_db(date_ranges1_start, date_ranges1_end)
logger.info('dataset to json...')
#return df.to_json(date_format='iso', orient='split')
return df.to_parquet() # <----------------------
#app.callback(
Output(component_id='graph_subplots', component_property='figure'),
[
Input(component_id='store', component_property='data'),
Input(component_id='dd_ycolnames', component_property='value'),
],
)
def update_plot(df_bin, y_colnames):
logger.info('dataset from json')
#df = pd.read_json(df_bin, orient='split')
df = pd.read_parquet(io.BytesIO(df_bin)) # <----------------------
logger.info('building plot...')
traces = []
for y_colname in y_colnames:
if df[y_colname].dtype == 'bool':
df[y_colname] = df[y_colname].astype('int')
traces.append(
{'x': df['date'], 'y': df[y_colname].values, 'name': y_colname},
)
fig = make_subplots(
rows=len(y_colnames), cols=1, shared_xaxes=True, vertical_spacing=0.1
)
fig.layout.height = 1000
for i, trace in enumerate(traces):
fig.append_trace(trace, i+1, 1)
logger.info('plotted')
return fig
if __name__ == '__main__':
app.run_server(host='localhost', debug=True)
Error text
OSError: Could not open parquet input source '<Buffer>': Invalid: Parquet file size is 0 bytes
Due to the exchange of data between client and server, you are currently limited to JSON serialization. One way to circumvent this limitation is via the ServersideOutput component from dash-extensions, which stores the data on the server. It uses file storage and pickle serialization by default, but you can use other storage (e.g. Redis) and/or serialization protocols (e.g. arrow) as well. Here is a small example,
import time
import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px
from dash_extensions.enrich import Dash, Output, Input, State, ServersideOutput
app = Dash(prevent_initial_callbacks=True)
app.layout = html.Div([
html.Button("Query data", id="btn"), dcc.Dropdown(id="dd"), dcc.Graph(id="graph"),
dcc.Loading(dcc.Store(id='store'), fullscreen=True, type="dot")
])
#app.callback(ServersideOutput("store", "data"), Input("btn", "n_clicks"))
def query_data(n_clicks):
time.sleep(1)
return px.data.gapminder() # no JSON serialization here
#app.callback(Input("store", "data"), Output("dd", "options"))
def update_dd(df):
return [{"label": column, "value": column} for column in df["year"]] # no JSON de-serialization here
#app.callback(Output("graph", "figure"), [Input("dd", "value"), State("store", "data")])
def update_graph(value, df):
df = df.query("year == {}".format(value)) # no JSON de-serialization here
return px.sunburst(df, path=['continent', 'country'], values='pop', color='lifeExp', hover_data=['iso_alpha'])
if __name__ == '__main__':
app.run_server()
When a dash app is started, all callbacks should be fired according to the dash documentation. But when callbacks are linked (ones output is the other ones input) the initial callback of the latter is prevented when the first raises a dash.exceptions.PreventUpdate or returns a dash.no_update.
Although I can somehow follow that logic, it was not clear for me that this would happen, and I am interested in reading somewhere the reasons behind this, although I have no idea where.
More practical, I would like to fire the initial callback nevertheless, is this possible? The code below is a minimal example of the problem:
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import plotly.graph_objects as go
import numpy as np
from time import sleep
app = dash.Dash(__name__)
app.layout = html.Div([
html.Button('test', id='button'),
dcc.Dropdown(id='super', options=[{'label':x, 'value':x} for x in ['test1', 'test2']]),
dcc.Dropdown(id='under', options=[{'label':x, 'value':x} for x in ['under1', 'under2']]),
dcc.Graph(id='graph'),
])
#app.callback(
Output('super', 'value'),
Input('button', 'n_clicks'),
prevent_initiall_call=True
)
def set_super_value(nclicks):
if nclicks is None:
return dash.no_update
return 'test2'
#app.callback(
Output('under', 'value'),
Input('super', 'value')
)
def set_under_value(val):
sleep(5)
return 'under1'
#app.callback(
Output('graph', 'figure'),
Input('under', 'value'),
)
def make_figure(val):
sleep(5)
if val == 'under1':
return go.Figure(go.Scatter(x=np.arange(0,1000), y=np.sin(np.arange(0,1000)/100)))
else:
return go.Figure(go.Scatter(x=np.arange(0,1000), y=np.cos(np.arange(0,1000)/100)))
if __name__ == '__main__':
app.run_server(debug=True)
This is the initial state:
And this is how I would like it to be:
In this simple example I can always set this initial value as the 'value' option in the initial app.layout variable, but the problem in my actual application makes this a suboptimal solution in my eyes.
* Edits made to the code to make specify the problem a little more specific. Code still works and screenshots still apply (but