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.]

查看:33
本文介绍了Dash app-为多页App验证动态创建布局[错误:在Dash回调的`State`中使用了不存在的对象。]的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个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范例是如何工作的。

如果有任何进一步的问题,请随时联系,但是..

以下是这些文件的内容:

  1. app.py

import dash

app = dash.Dash(__name__, suppress_callback_exceptions=True)
server = app.server
  1. 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
    ]
)

  1. 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

  1. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆