Dash app-为多页App验证动态创建布局[错误:在Dash回调的`State`中使用了不存在的对象。] [英] Dash app - Dynamically Create a Layout for Multi-Page App Validation [Error: A nonexistent object was used in an `State` of a Dash callback.]
问题描述
我有一个Dash应用程序,我试图加载几个附加的输入单元格,这些单元格依赖于前一个单元格的输入
即
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)
代码是这样的-布局文件(我遵循了DASH多页应用程序模板)文件包含附加输入的主布局和子布局(Layout_Query_Paroloic和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'),
])
回调文件声明了两个回调-一个用于主页,另一个用于显示其他输入,这取决于对查询-输入-1-状态的输入。
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
应用程序入口页面如下(声明应用程序布局):
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)
但是,我收到一个不存在的对象错误:
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`.
我已经声明了验证布局,并尝试通过直接进入引擎盖下的烧瓶来关闭错误,但是这两个措施似乎没有帮助。我也尝试在声明中移动以进行验证,但这没有什么不同。接受input-X-state
的回调无法识别输入集(X,Y)和(XX,YY,Z),图表永远不会更新。
我如何修复此问题?
推荐答案
好的,所以我假设您指的是URL Routing and Multiple Apps的以下Dash文档;并且已经(我个人也会)决定对他们推荐的文件结构布局使用第二个选项,他们将其称为‘平面’,如下所示:
.
|-- assets
| `-- custom.css
|-- app.py
|-- callbacks.py
|-- index.py
`-- layout.py
1 directory, 5 files
其中,index.py
文件是应用程序文件的实际条目(即,动态控制URL的文件,从而通过使用专门为此目的构建的dcc.Location
对象(多URL短划线应用程序))的回调函数显示哪个‘布局’(或实质上是应用程序)。
您收到该错误的原因是因为您正在加载的布局未声明上述Inputs
。
附注:我不确定您在这里想要的是一个多应用程序/URL Dash应用程序,或者,这是最好的方法(同样,因为我不是100%地支持您的整个程序要解决的问题),但是,正如他们所说,有不止一种方法可以给猫剥皮。这种方法可以完美地满足您给定的需求;特别是如果您了解我更改了什么,以及这种基于URL更改页面的Dash范例是如何工作的。
如果有任何进一步的问题,请随时联系,但是..
以下是这些文件的内容:
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)
显然,您需要完成/修复它们,才能准确地执行您想要的操作。例如,需要注意的一件事是:如果要在回调文件中引用布局,则需要将其导入(即import layouts
→然后将其称为layouts.__name_of_layout__
,或者直接导入from layouts import _____
)。对于任何可能被触发的单个回调,如果/当它被触发时,它的所有输入(包括状态)都必须以任何有意义的当前布局出现!希望如此。🙂
编辑:唉,由于有了新的dash.Dash()
应用程序属性"validation_layout
",更新版本的Dash不再受制于仅加粗和斜体的要求,它允许您基本上将所有页面/布局分配给";valify_layout";(从而包含您可能使用的所有组件ID),而不必担心回调被绑定到特定布局
这篇关于Dash app-为多页App验证动态创建布局[错误:在Dash回调的`State`中使用了不存在的对象。]的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!