I have a Dash application where I'm trying to load a few additional input cells which depend on the input of an earlier cell
I.e.
if input (query-input-1-state) = MOVE --> additional set0 of inputs load (input-X-state,input-Y-state)
if input (query-input-1-state) = PARABOLIC --> additional set1 of inputs load for further submission (input-XX-state,input-YY-state)
The code is this - the layouts file (I have followed the dash multipage app template) file contains the main layout and the sub-layouts for additional inputs (layout_query_parabolic and layout_query_move).
layouts.py
layout_menu = html.Div([
dcc.Link('Run Query', href='/apps/query'),html.Br(),
dcc.Link('Optimise', href='/apps/optimise'),html.Br(),
])
#Set 0 of input boxes
layout_query_move = html.Div([
dcc.Input(id='input-X-state', type='number', value=2),html.Br(),
dcc.Input(id='input-Y-state', type='number', value=3),html.Br(),
])
#Set 1 of input boxes
layout_query_parabolic = html.Div([
dcc.Input(id='input-XX-state', type='number', value=6),html.Br(),
dcc.Input(id='input-YY-state', type='number', value=7),html.Br(),
dcc.Input(id='input-Z-state', type='number', value=8),html.Br(),
])
layout_query_menu = html.Div([
dcc.Link('Go to Main', href='/apps/'),html.Br(),
html.H3('Enter settings for Move'),
dbc.Label("Ticker: ", size="md"),dcc.Input(id='query-input-0-state', type='text', value='QQQ'),
dbc.Label("Event: ", size="md"),dcc.Input(id='query-input-1-state', type='text', value='MOVE'),
html.Div(id='full-input-boxes'),
html.Button(id='submit-button-state2', n_clicks=0, children='Show all inputs'),
html.Button(id='submit-button-state', n_clicks=0, children='Go!'),
dcc.Graph(id='graph-with-slider'),
])
The callbacks file declares 2 callbacks - one for the main page - and the other to show additional inputs which depends on the input into query-input-1-state.
callbacks.py
#Validation layout to 'declare' all the input values
app.validation_layout = html.Div([
layout_query_move,
layout_query_parabolic,
layout_menu,
layout_query_menu,
layout_optimise,
dcc.Input(id='input-X-state', type='number', value=2), #set 0 of inputs
dcc.Input(id='input-Y-state', type='number', value=3), #set 0 of inputs
dcc.Input(id='input-XX-state', type='number', value=6), #set 1 of inputs
dcc.Input(id='input-YY-state', type='number', value=7), #set 1 of inputs
dcc.Input(id='input-Z-state', type='number', value=8), #set 1 of inputs
])
flask.has_request_context() == False
@app.callback(
Output('graph-with-slider', 'figure'),
Input('submit-button-state', 'n_clicks'),
State('query-input-0-state', 'value'),
State('query-input-1-state', 'value'),
State('input-X-state', 'value'),
State('input-Y-state', 'value'),
State('input-XX-state', 'value'),
State('input-YY-state', 'value'),
State('input-Z-state', 'value'),
)
def display_value0(n_clicks,v0,v1, v2,v3,v4,v5,v6):
d = {'x': [v2, v2], 'y': [v2, v2]}
df = pd.DataFrame(data=d)
filtered_df = df
fig = px.scatter(filtered_df, x="x", y="y")
fig.update_layout(transition_duration=500)
return fig
@app.callback(
Output('full-input-boxes', 'children'),
Input('submit-button-state2', 'n_clicks'),
State('query-input-1-state', 'value'),
)
def ask_for_more_inputs(n_clicks,event_id): #,asset_str,event_str
if not n_clicks: raise dash.exceptions.PreventUpdate
if event_id == 'MOVE': return layout_query_move
if event_id == 'PARABOLIC': return layout_query_parabolic
The app entry page is this (declares the app layout):
index.py #App Entry page
app.layout = html.Div([
dcc.Location(id='url', refresh=False),
html.Div(id='page-content'),
])
@app.callback(
Output('page-content', 'children'),
Input('url', 'pathname')
)
def display_page(pathname):
if pathname == '/apps/':
return layout_menu
elif pathname == '/apps/query':
return layout_query_menu
elif pathname == '/apps/optimise':
return layout_optimise
elif pathname == '/apps/move':
return layout_query_move
if __name__ == '__main__':
app.run_server(debug=True)
I however get an nonexistent object error with:
A nonexistent object was used in an `State` of a Dash callback. The id of this object is
`input-X-state` and the property is `value`.
I have declared the validation layout and have tried turning off the errors through going directly into flask under the hood, however these two measures don't seem to have helped. I have also tried moving around the declaration for the validation but that doesn't make any difference. The callback which takes input-X-state
doesn't recognise both sets of inputs (X,Y) and (XX,YY,Z) and the chart never updates.
How can I fix this?
OK, so I am assuming you are referring to the following Dash docs for URL Routing and Multiple Apps; and have (as I personally would too) decided to go with the second option for their recommended file structure layouts which they refer to as 'flat', and looks like this:
.
|-- assets
| `-- custom.css
|-- app.py
|-- callbacks.py
|-- index.py
`-- layout.py
1 directory, 5 files
where it is the index.py
file which is the actual "entry" to the app file (i.e., the file which controls dynamically the URL and thus which 'layout' (or essentially, app) is being displayed, via a callback function utilizing the dcc.Location
object built specifically for this purpose (multi-URL Dash apps)).
I'll be honest it's not entirely clear to me what exactly you are trying to do, but, that may not be necessary. It looks pretty complicated/involved/esoteric. BUT, what I've done is re-arranged your files so that they run without error, and hopefully this will help guide you on the right track.
The reason you were getting that error, is because you were loading a layout which did not have the mentioned Inputs
declared.
Side note: I am not positive what you even want here (/need) is a multi-app/URL Dash app, or, that this is the best approach (again because I'm not 100% on what your overall program is intended to solve) but, there is more than one way to skin a cat, as they say. This approach could work out perfectly, for your given needs; especially if you understand what it is I've changed and how this Dash paradigm with altering the page based on the URL works.
Feel free to lmk if any questions further, but..
Here's the contents of the files:
app.py
import dash
app = dash.Dash(__name__, suppress_callback_exceptions=True)
server = app.server
layouts.py
from dash import dcc
from dash import html
import dash_bootstrap_components as dbc
layout_menu = html.Div(
[
dcc.Link("Run Query", href="/apps/query"),
html.Br(),
dcc.Link("Optimise", href="/apps/optimise"),
html.Br(),
]
)
# Set 0 of input boxes
layout_query_move = html.Div(
[
dcc.Input(id="input-X-state", type="number", value=2),
html.Br(),
dcc.Input(id="input-Y-state", type="number", value=3),
html.Br(),
]
)
# Set 1 of input boxes
layout_query_parabolic = html.Div(
[
dcc.Input(id="input-XX-state", type="number", value=6),
html.Br(),
dcc.Input(id="input-YY-state", type="number", value=7),
html.Br(),
dcc.Input(id="input-Z-state", type="number", value=8),
html.Br(),
]
)
layout_query_menu = html.Div(
[
dcc.Link("Go to Main", href="/apps/"),
html.Br(),
html.H3("Enter settings for Move"),
dbc.Label("Ticker: ", size="md"),
dcc.Input(id="query-input-0-state", type="text", value="QQQ"),
dbc.Label("Event: ", size="md"),
dcc.Input(id="query-input-1-state", type="text", value="MOVE"),
html.Div(id="full-input-boxes"),
html.Button(
id="submit-button-state2", n_clicks=0, children="Show all inputs"
),
html.Button(id="submit-button-state", n_clicks=0, children="Go!"),
dcc.Graph(id="graph-with-slider"),
dcc.Input(
id="input-X-state", type="number", value=2
), # set 0 of inputs
dcc.Input(
id="input-Y-state", type="number", value=3
), # set 0 of inputs
dcc.Input(
id="input-XX-state", type="number", value=6
), # set 1 of inputs
dcc.Input(
id="input-YY-state", type="number", value=7
), # set 1 of inputs
dcc.Input(
id="input-Z-state", type="number", value=8
), # set 1 of inputs
]
)
# Validation layout to 'declare' all the input values
layout_optimise = html.Div(
[
dcc.Input(
id="input-X-state", type="number", value=2
), # set 0 of inputs
dcc.Input(
id="input-Y-state", type="number", value=3
), # set 0 of inputs
dcc.Input(
id="input-XX-state", type="number", value=6
), # set 1 of inputs
dcc.Input(
id="input-YY-state", type="number", value=7
), # set 1 of inputs
dcc.Input(
id="input-Z-state", type="number", value=8
), # set 1 of inputs
]
)
callbacks.py
import dash
import layouts
import pandas as pd
import plotly.express as px
from app import app
from dash import dcc
from dash import html
from dash.dependencies import Input
from dash.dependencies import Output
from dash.dependencies import State
@app.callback(
Output("graph-with-slider", "figure"),
Input("submit-button-state", "n_clicks"),
State("query-input-0-state", "value"),
State("query-input-1-state", "value"),
State("input-X-state", "value"),
State("input-Y-state", "value"),
State("input-XX-state", "value"),
State("input-YY-state", "value"),
State("input-Z-state", "value"),
)
def display_value0(n_clicks, v0, v1, v2, v3, v4, v5, v6):
d = {"x": [v2, v2], "y": [v2, v2]}
df = pd.DataFrame(data=d)
filtered_df = df
fig = px.scatter(filtered_df, x="x", y="y")
fig.update_layout(transition_duration=500)
return fig
@app.callback(
Output("full-input-boxes", "children"),
Input("submit-button-state2", "n_clicks"),
State("query-input-1-state", "value"),
)
def ask_for_more_inputs(n_clicks, event_id): # ,asset_str,event_str
if not n_clicks:
raise dash.exceptions.PreventUpdate
if event_id == "MOVE":
return layouts.layout_query_move
if event_id == "PARABOLIC":
return layouts.layout_query_parabolic
index.py
from dash import dcc
from dash import html
from dash.dependencies import Input, Output
from app import app
from layouts import (
layout_menu,
layout_query_menu,
layout_optimise,
layout_query_move,
)
import callbacks
app.layout = html.Div(
[dcc.Location(id="url", refresh=False), html.Div(id="page-content")]
)
@app.callback(Output("page-content", "children"), Input("url", "pathname"))
def display_page(pathname):
if pathname == "/apps/":
return layout_menu
elif pathname == "/apps/query":
return layout_query_menu
elif pathname == "/apps/optimise":
return layout_menu
elif pathname == "/apps/move":
return layout_query_move
else:
return "404"
if __name__ == "__main__":
app.run_server(debug=True, dev_tools_hot_reload=True)
Obviously you're going to need to finish/fix them to do what it is you want, exactly. One thing to notice, for example: if you're going to reference a layout in the callbacks file, it needs to be imported (i.e., import layouts
→ then refer to it as layouts.__name_of_layout__
, or, import it directly from layouts import _____
). And for any single callback that may be triggered, if/when it is triggered, all of its inputs (including states) must all be present in whatever the current "layout" being served is! Hope that makes sense. 🙂
EDIT: Alas, newer version of Dash are no longer hindered by the requirement just bolded and italicized thanks to a new dash.Dash()
app attribute "validation_layout
", which allows you to essentially assign all of your pages/layouts to the "validation_layout" (thus containing all components ids you might use) and not have to worry about callbacks being tied down to specific layouts