Multi-page Dash app with synced dropdown menu not working - python

I'm quite new to Plotly-Dash, and I'm currently struggling with the multi-page-dropdown.py example I found on the Dash Recipes Github because it is not working as I would expect.
I would like to have a multi-page app, with a dropdown menu on all pages that works as a filter on what is displayed on such pages; the dropdown selection should be persistent on all pages. The behaviour I'm looking for is displayed in the following GIF.
https://github.com/plotly/dash-recipes/blob/master/multi-page-dropdown-example.gif
When I run the recipe, however, I see an empty plot, when I open a new page.
My understanding of the callbacks that regulate the functioning of the code is that update_graph updates the plot based on the dropdown selection or when the URL changes (i.e., when a new page opens).
What I notice when I run the code provided in the recipe is that the update_graph callback updates correctly the graph when the dropdown selection is changed, but it fails to update the graph on a new page, when I open it.
Please find below the code I'm running, for reference.
Is there anything I'm doing wrong and should be done differently?
Thanks in advance for your support!
import dash
from dash.dependencies import Input, State, Output
import dash_html_components as html
import dash_core_components as dcc
import pandas as pd
df = pd.DataFrame({
'x': [1, 2, 3, 1, 2, 3, 1, 2, 3],
'y': [3, 2, 4, 1, 4, 5, 4, 3, 1],
'group-1': ['/', '/exhibit-b', '/exhibit-c', '/', '/exhibit-b', '/exhibit-c', '/', '/exhibit-b', '/exhibit-c'],
'group-2': ['LA', 'LA', 'LA', 'London', 'London', 'London', 'Montreal', 'Montreal', 'Montreal'],
})
app = dash.Dash()
app.scripts.config.serve_locally=True
# app.config.supress_callback_exceptions = True
app.config.suppress_callback_exceptions = True
app.layout = html.Div([
# This "header" will persist across pages
html.H2('Multi Page Dash App'),
# Each "page" will modify this element
html.Div(id='content-container-part-1'),
dcc.Dropdown(
id='graph-control',
options=[{'label': i, 'value': i} for i in df['group-2'].unique()],
value='LA'
),
# Each "page" will modify this element
html.Div(id='content-container-part-2'),
# This Location component represents the URL bar
dcc.Location(id='url', refresh=False)
], className="container")
link_mapping = {
'/': 'Exhibit A',
'/exhibit-b': 'Exhibit B',
'/exhibit-c': 'Exhibit C',
}
styles = {
'link': {'padding': '20'}
}
#app.callback(
Output('content-container-part-1', 'children'),
[Input('url', 'pathname')])
def display_page(pathname):
return html.Div([
html.Div([
html.Span(
dcc.Link(link_mapping['/'], href="/") if pathname != '/' else 'Exhibit A',
style=styles['link']
),
html.Span(
dcc.Link(link_mapping['/exhibit-b'], href="/exhibit-b") if pathname != '/exhibit-b' else 'Exhibit B',
style=styles['link']
),
html.Span(
dcc.Link(link_mapping['/exhibit-c'], href="/exhibit-c") if pathname != '/exhibit-c' else 'Exhibit C',
style=styles['link']
)
]),
dcc.Markdown('### {}'.format(link_mapping[pathname])),
])
#app.callback(
Output('content-container-part-2', 'children'),
[Input('url', 'pathname')])
def display_page(*args):
return html.Div([
dcc.Graph(
id='graph',
)
])
#app.callback(
Output('graph', 'figure'),
[Input('graph-control', 'value'),
Input('url', 'pathname')])
def update_graph(value, pathname):
dff = df[(df['group-1'] == pathname) & (df['group-2'] == value)]
return {
'data': [{
'x': dff.x,
'y': dff.y,
'type': 'bar'
}],
'layout': {
'title': '{} in {}'.format(value, link_mapping[pathname])
}
}
app.css.append_css({"external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"})
if __name__ == '__main__':
app.run_server(debug=True)

The issue seems to be resolved by simply swapping the order of display_page and update_graph callbacks: the correct requires update_graph to come first in the code, and display_page to follow.
Now, my question is if it is possible to control the execution order of callbacks, to make sure everything is executed correctly?
Obviously, if anyone has more brilliant explanations or solutions, please come forward!
For reference, the piece should look like this.
#app.callback(
Output('graph', 'figure'),
[Input('graph-control', 'value'),
Input('url', 'pathname')])
def update_graph(value, pathname):
dff = df[(df['group-1'] == pathname) & (df['group-2'] == value)]
return {
'data': [{
'x': dff.x,
'y': dff.y,
'type': 'bar'
}],
'layout': {
'title': '{} in {}'.format(value, link_mapping[pathname])
}
}
#app.callback(
Output('content-container-part-2', 'children'),
[Input('url', 'pathname')])
def display_page(*args):
return html.Div([
dcc.Graph(
id='graph',
)
])

Related

Create dcc.Dropdown values Dynamically with Python Function

I am creating a search application with Dash by Plotly. I have a main search function for that creates a dataframe for the whole application that is defined as:
def search(term):
with index.searcher() as searcher:
parser = QueryParser("content", index.schema)
myquery = parser.parse(term)
results = searcher.search(myquery, limit=None)
print("Documents Containing ", term, ": ", len(results), "\n")
df = pd.DataFrame([i['date'], i['site'], i['system'], i['ticket'], i.score, i['level'], i['first'], i['last'], i['department'], \
i['detect'], i['code'],i['content'], i['description'], i['owner'], i['ownerGroup'], i['docId']] for i in results)
df.columns=['Reported Date', 'Site', 'System','Ticket ID', 'Score', 'Level', 'First', 'Last', 'Department', \
'Detection', 'Code', 'Content', 'Description', 'Owner', 'Owner Group', 'Document ID']
return df
I want users to be able to filter down search results with filters built into dcc.Dropdown. A couple of them can be hard-coded like so:
dcc.Dropdown(
id='siteFilter',
options=[
{'label': 'ABC', 'value': 'ABC'},
{'label': 'DEF', 'value': 'DEF'},
{'label': 'HIJ', 'value': 'HIJ'},
{'label': 'LMO', 'value': 'LMO'}
],
value=['ABC', 'DEF', 'HIJ', 'LMO'],
multi=True
However, some of the fields I want to filter on contain many options for a search and cannot be hard-coded. I can get the options to work. However, my application will not apply the filter changing when a user changes values. So, I have in my app.layout:
html.Div(dbc.Row([dbc.Col([
html.Label(["System Filter",
dcc.Dropdown(
id='systemFilter',
options='options',
value='All',
multi=True,
)
])
], width = 5)
]))
In my callback, I have tried several different options/combos of Outputs and States to achieve this with no luck. Callback:
#app.callback(
[Output(component_id='outTable', component_property='data'),
Output(component_id='outTable', component_property='columns'),
Output(component_id='commonWords', component_property='children'),
Output(component_id='systemFilter', component_property='options'),
#Output(component_id='systemFilter', component_property='value')
],
[Input(component_id='button', component_property='n_clicks')],
[State('searchId', 'value'),
State('siteFilter', 'value'),
State('detectFilter', 'value'),
State('levelFilter', 'value'),
State('codeFilter', 'value'),
#State(component_id='systemFilter', component_property='value')
])
def tableCreate(n_clicks, searchId, siteFilter, detectFilter, levelFilter, codeFilter):
if n_clicks > 0:
searchFrame = search(searchId)
###################### System Filter #######################################
#print('HERE DLKFSJSLDFKJSLDKJFJLKFDSJLDSKF')
#print(searchFrame['System'].unique())
#global optionsArray
optionsArray = searchFrame['System'].unique()
optionsArray = optionsArray.tolist()
print('Test')
print(optionsArray)
print('Test')
system_filter_options = [{'label': i, 'value': i} for i in optionsArray]
systemFilter = optionsArray
searchFrame = searchFrame[searchFrame['System'].isin(systemFilter)]
# Bunch of other code
searchFrame = searchFrame.drop(columns=['ContentNoStop'])
columns = [{'name': col, 'id': col} for col in searchFrame.columns]
data = searchFrame.to_dict(orient='records')
return data, columns, wordComponent, system_filter_options #, systemFilter
else:
return dash.no_update, dash.no_update, dash.no_update, dash.no_update
Essentially, one of 2 things happens. 1) The values return blank because they are not defined or 2) the options continually override the updated value parameter which I know is caused by the line systemFilter = optionsArray but I cannot seem to figure out a work around for this.
Here is an example image. I would like the results to populate all, but when a user filters by system and only selects a few, all my graphs and tables should update. But everytime, it does not seem to catch that. What am I missing here? How can the dcc.Dropdown selections be applied as a filter dynamically? I cannot and do not want to hard-code all the options
Easier reproduceable example:
I would like the dropdowns options to filter the table. If dog is selected, the table only shows dog.
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
import dash_table
import pandas as pd
from dash.dependencies import Input, Output, State
external_stylesheets = [dbc.themes.BOOTSTRAP]
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
server = app.server
def createTable():
df = pd.DataFrame({'num_legs': [2, 4, 8, 0],
'num_wings': [2, 0, 0, 0],
'system': ['falcon', 'dog', 'spider', 'fish']},
index=[1, 2, 3, 4])
return df
app.layout = html.Div([
html.Button('Submit and Refresh', id='button', n_clicks=0, className='btn btn-primary'),
dbc.Row([dbc.Col([
html.Label(["System Filter",
dcc.Dropdown(
id='systemFilter',
options='options',
value='All',
multi=True,
)
])
], width = 5)
]),
html.Div([dash_table.DataTable(style_cell={
'whiteSpace': 'normal',
'height': 'auto',
'textAlign': 'left'
},
fill_width=False,
id='outTable',
sort_action="native",
sort_mode="multi",
column_selectable="single",
row_deletable=True,
)
],
) ])
#app.callback(
[Output(component_id='outTable', component_property='data'),
Output(component_id='outTable', component_property='columns'),
Output(component_id='systemFilter', component_property='options'),
#Output(component_id='systemFilter', component_property='value')
],
[Input(component_id='button', component_property='n_clicks')],
[
State(component_id='systemFilter', component_property='value')
])
def tableCreate(n_clicks, systemFilter):
if n_clicks > 0:
searchFrame = createTable()
optionsArray = searchFrame['system'].unique()
optionsArray = optionsArray.tolist()
print('Test')
print(optionsArray)
print('Test')
system_filter_options = [{'label': i, 'value': i} for i in optionsArray]
systemFilter = optionsArray
searchFrame = searchFrame[searchFrame['system'].isin(systemFilter)]
columns = [{'name': col, 'id': col} for col in searchFrame.columns]
data = searchFrame.to_dict(orient='records')
return data, columns, system_filter_options#, systemFilter
if __name__ == '__main__':
app.run_server(debug=False)

How do I use Dash Loading with Dash Store?

I am writing a simple Dash page. I get data from external APIs etc. and put this in a dcc.Store. The Graphs then pull the data and plot in callbacks. I am trying to implement the dcc.Loading functionality as the pulling of the data can take some time. However I can't figure out how to trigger the Loading for the Graphs when the work is being done by Store.
Below is an example:
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
import plotly.express as px
import pandas as pd
import time
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div(children=[
html.H1(children='Hello Dash'),
html.Div(children='''
Dash: A web application framework for Python.
'''),
dcc.Dropdown(
id='demo-dropdown',
options=[
{'label': 'New York City', 'value': 'NYC'},
{'label': 'Montreal', 'value': 'MTL'},
{'label': 'San Francisco', 'value': 'SF'}
],
value='NYC'
),
dcc.Loading(
id='loading01',
children=html.Div(id='loading-output')),
# Store certain values
dcc.Store(
id='session',
storage_type='session'),
])
#app.callback(Output('loading-output', 'children'),
[Input('session', 'modified_timestamp')],
[State('session', 'data')])
def loading_graph(ts, store):
if store is None:
raise PreventUpdate
if 'NYC' in store['value']:
v = 1
elif 'SF' in store['value']:
v=2
else:
v=3
return dcc.Graph(
id='example-graph',
figure={
'data': [
{'x': [1, 2, 3], 'y': [4*v, 1*v, 2*v], 'type': 'bar', 'name': 'SF'},
{'x': [1, 2, 3], 'y': [2, 4, 5], 'type': 'bar', 'name': u'Montréal'},
],
'layout': {
'title': 'Dash Data Visualization'
}
}
)
#app.callback(Output('session', 'data'),
[Input('demo-dropdown', 'value')],
[State('session', 'data')])
def storing(value, store):
store = store or {}
store['value'] = value
time.sleep(3)
return store
if __name__ == '__main__':
app.run_server(debug=True)
I guess I was hoping for the spinner to be present whilst Store was fetching things.
Thanks in advance for any help or pointers.
If you want to show a loader when the storing callback is called it also needs to have an output to a Loading components' children property.
You can't have duplicate callback outputs, so you could either combine the callbacks into a single callback. Then you could have a single spinner that is active for as long as the combined callback takes to execute.
Or you could have multiple Loading components: One for each callback function:
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
import time
external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div(
children=[
html.H1(children="Hello Dash"),
html.Div(
children="""
Dash: A web application framework for Python.
"""
),
dcc.Dropdown(
id="demo-dropdown",
options=[
{"label": "New York City", "value": "NYC"},
{"label": "Montreal", "value": "MTL"},
{"label": "San Francisco", "value": "SF"},
],
value="NYC",
),
dcc.Loading(id="loading01", children=html.Div(id="loading-output1")),
dcc.Loading(id="loading02", children=html.Div(id="loading-output2")),
# Store certain values
dcc.Store(id="session", storage_type="session"),
]
)
#app.callback(
Output("loading-output2", "children"),
Input("session", "modified_timestamp"),
State("session", "data"),
prevent_initial_call=True,
)
def loading_graph(ts, store):
if store is None:
raise PreventUpdate
if "NYC" in store["value"]:
v = 1
elif "SF" in store["value"]:
v = 2
else:
v = 3
time.sleep(2)
return dcc.Graph(
id="example-graph",
figure={
"data": [
{
"x": [1, 2, 3],
"y": [4 * v, 1 * v, 2 * v],
"type": "bar",
"name": "SF",
},
{"x": [1, 2, 3], "y": [2, 4, 5], "type": "bar", "name": u"Montréal"},
],
"layout": {"title": "Dash Data Visualization"},
},
)
#app.callback(
Output("session", "data"),
Output("loading-output1", "children"),
Input("demo-dropdown", "value"),
State("session", "data"),
)
def storing(value, store):
time.sleep(2)
store = store or {}
store["value"] = value
return store, ""
if __name__ == "__main__":
app.run_server(debug=True)

How to refresh html map with dropdown menu in dash app?

I am new to python Dash. I am trying to create a dashboard with a dropdown menu and a map. By the selecting a value in the dropdown menu, the map will be updated with a new html map.
Here is the code:
dropdown_list = [{'label': 'MSFT', 'value': 'MSFT'},
{'label': 'IBM', 'value': 'IBM'}]
app.layout = html.Div(
children=[
html.Div(className='row',
children=[
html.Div(className='three columns div-user-controls',
children=[
html.Div(
children=[
dcc.Dropdown(id='selector', options=dropdown_list,
multi=False, value=dropdown_list[0]['value'],
style={'backgroundColor': '#1E1E1E'}
),
],
style={'color': '#1E1E1E'})
]
),
html.Div(className='nine columns div-for-charts bg-grey',
children=[
html.Iframe(id='map', style={'border': 'none'}, width='100%', height='750')
])
])
])
## Callback for timeseries price
#app.callback(Output('map', 'srcDoc'),
[Input('selector', 'value')])
def update_map(dropdown_values):
for vv in dropdown_values:
if vv == 'MSFT':
return open(html_file1, 'r').read()
elif vv == 'IBM':
return open(html_file2, 'r').read()
else:
return dash.no_update
The map, however, does not show up and is not updated. Can anyone give me some hints on this? Thank you in advance.
I have found the issue. In the dropdown menu, since multi=False, there will be only one value in the input of the callback function: dropdown_values. So there is no need to loop through a list and match the value.
Below is the corrected callback function
#app.callback(Output('map', 'srcDoc'),
[Input('selector', 'value')])
def update_map(dropdown_values):
if dropdown_values == 'MSFT':
return open(html_file1, 'r').read()
elif dropdown_values == 'IBM':
return open(html_file2, 'r').read()
else:
return dash.no_update

Graph not updating when dropdown value selected

I cant figure out why my graph isnt updating with the data from the value I have selected. Can somebody please try help.
I've tried to rewrite this many times but I can't seem to get my head around the function which I must write after the app callback and what I should return in this function.
app = dash.Dash()
app.layout = html.Div(children = [
html.H1('Sports PA Dashboard'),
dcc.Dropdown(
id='sport_dropdown',
options=[{'label': i, 'value': i} for i in df.Sport.unique()],
value = df.Sport.unique()),
dcc.Graph(id='age_vs_rank')
])
#app.callback(
dash.dependencies.Output('age_vs_rank', 'figure'),
[dash.dependencies.Input('sport_dropdown', 'value')])
def update_output(selected_sport):
print(selected_sport)
sport = df[df.Sport == selected_sport]
rank = sport['Rank']
age = sport['Age']
dcc.Graph(id='age_vs_rank',
figure={
'data' : [{'x':rank, 'y':age, 'type' : 'bar', 'name' : 'age_vs_rank'}],
'layout' : {
'title' : 'Age vs Rank'
}
})
if __name__ == '__main__':
app.run_server(debug=True)
You can try create div and then fill him with what you want.
Code:
from flask import Flask
import dash
import dash_core_components as dcc
import dash_html_components as html
# if you import in a such way - you can call Input, Output directly
from dash.dependencies import Input, Output
# Create app
app = dash.Dash()
# Specify layout
app.layout = html.Div(children=[
html.H1('Sports PA Dashboard'),
dcc.Dropdown(
id='sport_dropdown',
options=[{'label': i, 'value': i} for i in df.Sport.unique()],
value=df.Sport.unique()),
# Create empty Div container, which you can fill with everything!
html.Div(id='your-plot-here')
])
#app.callback(
# If you stuck to what those values belongs to,
# you can specify them more in details:
# Output(component_id='your_plot_here', component_property='children'),
# instead of:
Output('your_plot_here', 'children'),
[Input('sport_dropdown', 'value')])
def update_output(selected_sport):
"""This function create age_vs_rank graph."""
# selected_sport == value property from sport_dropdown id
print(selected_sport)
sport = df[df.Sport == selected_sport]
rank = sport['Rank']
age = sport['Age']
# return what you want in empty Div container with id 'your_plot_here'
return dcc.Graph(id='age_vs_rank',
figure={'data': [
{'x': rank, 'y': age,
'type': 'bar', 'name': 'age_vs_rank'}
],
'layout': {
'title': 'Age vs Rank'
}
}
)
if __name__ == '__main__':
app.run_server(debug=True)
Try changing the return statement to your update_output function.
So it should look like,
#app.callback(
# If you stuck to what those values belongs to,
# you can specify them more in details:
# Output(component_id='your_plot_here', component_property='children'),
# instead of:
Output('your_plot_here', 'children'),
[Input('sport_dropdown', 'value')])
def update_output(selected_sport):
sport = df[df.Sport == selected_sport]
rank = sport['Rank']
age = sport['Age']
return figure = {'data': #stuff
'layout':#stuff
}
Only add the figure dict because thats the only value thats getting updated.
Sorry if the formatting looks weird, this is my first StackOverflow Answer :)

API defined dataframe (dynamically generated) into Table (Dash_Table_Experiments)

Does anyone know how to dynamically generate a TABLE of data based on the user input using Dash_Table_Experiments (Python code)?
This code is static, however, I would like to find a way to make it dynamic. Thus, when a user enters a ticker symbol the TABLE will automatically update with the pertinent data below the chart.
The dataframe will change because it is generated from an api (i.e. Alpha Vantage), thus it cannot be defined as a static table. See code below.
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_table_experiments as dt
import json
import pandas as pd
from alpha_vantage.timeseries import TimeSeries
print(dcc.__version__) # 0.6.0 or above is required
app = dash.Dash()
ts = TimeSeries(key='', output_format='pandas')
data1, meta_data = ts.get_daily(symbol=inputsymbol, outputsize='full')
date=data1.reset_index(level=['date'])
df=(date.tail(10))
DF_SIMPLE = df
app.config.supress_callback_exceptions = True
app.scripts.config.serve_locally = True
app.layout = html.Div([
dcc.Location(id='url', refresh=False),
html.Div(id='page-content'),
html.Div(dt.DataTable(rows=[{}]), style={'display': 'none'})
])
index_page = html.Div([
html.H1('Page Home'),
html.Br(),
dcc.Link('Go to Home', href='/'),
html.Br(),
dcc.Link('Go to Page 1', href='/page-1'),
])
page_1_layout = html.Div([
html.H1('Page 1'),
html.Br(),
dcc.Link('Go back to home', href='/'),
html.H4('DataTable'),
dt.DataTable(
rows=DF_SIMPLE.to_dict('records'),
# optional - sets the order of columns
#columns=sorted(DF_SIMPLE.columns),
editable=False,
id='editable-table'
),
html.Div([
html.Pre(id='output', className="two columns"),
html.Div(
dcc.Graph(
id='graph',
style={
'overflow-x': 'wordwrap'
}
),
className="ten columns"
)
], className="row"),
])
#app.callback(
dash.dependencies.Output('output', 'children'),
[dash.dependencies.Input('editable-table', 'rows')])
def update_selected_row_indices(rows):
return json.dumps(rows, indent=2)
#app.callback(
dash.dependencies.Output('graph', 'figure'),
[dash.dependencies.Input('editable-table', 'rows')])
def update_figure(rows):
dff = pd.DataFrame(rows)
return {
'data': [{
'x': dff['x'],
'y': dff['y'],
}],
'layout': {
'margin': {'l': 10, 'r': 0, 't': 10, 'b': 20}
}
}
# Update the index
#app.callback(dash.dependencies.Output('page-content', 'children'),
[dash.dependencies.Input('url', 'pathname')])
def display_page(pathname):
if pathname == '/page-1':
return page_1_layout
else:
return index_page
app.css.append_css({
'external_url': 'https://codepen.io/chriddyp/pen/bWLwgP.css'
})
if __name__ == '__main__':
app.run_server(debug=True)

Categories

Resources