【问题标题】:Dash multiple independent callbacks破折号多个独立的回调
【发布时间】:2021-02-12 21:01:03
【问题描述】:

我有一个简单的仪表板应用程序,其中包含用作过滤器的折线图和单选按钮。

我创建了 2 个回调 - 第一个用于 url,第二个用于过滤器。但是,当我运行应用程序时,它一直在更新(我想在循环中调用第二个回调)。这两个回调似乎有些相互依赖,但我希望它们是独立的——只有在单选按钮(过滤器)更改时,我才需要运行第二个。

没有第一个回调,一切正常。

@app.callback(
    Output('page-content', 'children'),
    [Input('url', 'pathname')]
)
def display_page(pathname):
    if pathname == '/':
        return '404'
    elif pathname == '/something/my-dashboard':
        return app.layout
    else:
        return '404'

@app.callback(
    Output("main-chart", "figure"), 
    Input("category", "value")
)
def update_graph(category):
    dff = melted[melted["category"] == category]
    fig = create_chart(dff)
    set_style(fig, category)
    return fig

编辑: 添加布局

app.layout = html.Div(
    [   dcc.Location(id='url', refresh=False),
        html.Div(
            [
                html.Div(
                    [
                        html.H1(children="My dashboard"),
                        html.Div([dcc.Graph(id="main-chart", figure=fig)]),
                    ],
                    className="column1",
                ),
                html.Div(
                    [
                        dbc.Label("Filter", style={'fontWeight':'bold'}),
                        dcc.RadioItems(
                            id="category",
                            options=[{"label": i, "value": i} for i in categories],
                            value="Product A",
                            labelStyle={"display": "block"},
                        ),
                    ],
                    className="column2",
                ),
            ],
            className="row",
            id='page-content'
        ),
    ]
)

@BasvanderLinden 建议后的EDIT2:

fig = create_chart(df)
set_style(fig, "Marketing")

app.layout = html.Div(
    [   dcc.Location(id='url', refresh=False),
        html.Div(
            className="row",
            id='page-content'
        ),
    ]
)

dashboard_layout = html.Div(
    [
                html.Div(
                    [
                        html.H1(children="My dashboard"),
                        html.Div([dcc.Graph(id="main-chart", figure=fig)]),
                    ],
                    className="column1",
                ),
                html.Div(
                    [
                        dbc.Label("Category", style={'fontWeight':'bold'}),
                        dcc.RadioItems(
                            id="category",
                            options=[{"label": i, "value": i} for i in categories],
                            value="Marketing",
                            labelStyle={"display": "block"},
                        ),
                    ],
                    className="column2",
                ),
            ],
)


@app.callback(Output("page-content", "children"), [Input("url", "pathname")])
def display_page(pathname):
    print(pathname)
    if pathname == "/":
        return "404"
    elif pathname == "/my-dashboard":
        return dashboard_layout
    else:
        return "404"

@app.callback(
    Output("main-chart", "figure"), 
    Input("category", "value")
)
def update_graph(category):
    dff = df[df["Category"] == category]
    fig = create_chart(dff)
    set_style(fig, category)
    return fig


if __name__ == "__main__":
    app.run_server(debug=True, host="0.0.0.0")

【问题讨论】:

  • 能否也包括您的布局?这听起来类似于问题here。您是否有多个Location 组件或具有相同ids 的其他组件?
  • @BasvanderLinden 添加了

标签: python plotly-dash


【解决方案1】:

问题实际上出在您的第一个回调中:

@app.callback(
    Output('page-content', 'children'),
    [Input('url', 'pathname')]
)
def display_page(pathname):
    if pathname == '/':
        return '404'
    elif pathname == '/something/my-dashboard':
        return app.layout
    else:
        return '404'

问题是你在这里返回app.layout

这里发生的事情真的很有趣。由于app.layout 覆盖了您的整个布局,这意味着它将包含ID 值为urlLocation 组件。因此,由于app.layout 附加到page-content div,这意味着在将app.layout 附加到page-content div 之后,现在有两个Location 组件具有相同的ID (url)。使用 id url 注册了一个新的 Location 组件这一事实导致再次触发回调。这一切都会导致第一个回调被递归触发并将元素附加到 dom。如果您检查浏览器中的元素,您会看到这种情况。

因此,解决方案是不在您的回调中返回app.layout,而是抽象您的布局的一部分并在回调中返回它。这是一个在其定义中不包含 Location 组件的组件。

因此,您可以将仪表板抽象为它自己的组件,如下所示:

dashboard_layout = html.Div(
    [
        html.Div(
            [
                html.H1(children="My dashboard"),
                html.Div([dcc.Graph(id="main-chart", figure=fig)]),
            ],
            className="column1",
        ),
        html.Div(
            [
                dbc.Label("Filter", style={"fontWeight": "bold"}),
                dcc.RadioItems(
                    id="category",
                    options=[{"label": i, "value": i} for i in categories],
                    value="Product A",
                    labelStyle={"display": "block"},
                ),
            ],
            className="column2",
        ),
    ]
)

app.layout = html.Div(
    [
        dcc.Location(id="url", refresh=False),
        html.Div(
            className="row",
            id="page-content",
        ),
    ]
)

@app.callback(Output("page-content", "children"), [Input("url", "pathname")])
def display_page(pathname):
    print(pathname)
    if pathname == "/":
        return "404"
    elif pathname == "/something/my-dashboard":
        return dashboard_layout
    else:
        return "404"

【讨论】:

  • 我不能说我完全理解,但我尝试了你的代码,加载页面时出现 2 个错误ID not found in layout。我将dashboard_layout 添加到 app.layout 第二个 Div 中,它被加载了,但又一次 - 它卡在了某个循环中 - 一直在加载图表并将其向右移动。
  • id 警告在这里并不是真正的问题,因为当您不在仪表板页面上时,回调中使用的ids 还不存在。您可以在仪表板应用程序初始化中取消它:app = dash.Dash(__name__, suppress_callback_exceptions=True)
  • 也许我在这里误解了您的部分评论,但您不应该直接将dashboard_layout 添加到app.layoutdashboard_layout 应该只附加在回调中。
  • 实际上是浏览器中显示的破折号错误。其中有两个。一种是:Attempting to connect a callback Output item to component: "main-chart" but no components with that id exist in the layout. If you are assigning callbacks to components that are generated by other callbacks (and therefore not in the initial layout), you can suppress this exception by setting `suppress_callback_exceptions=True`. This ID was used in the callback(s) for Output(s): main-chart.figure 什么都不显示(无内容)
  • 就像我在上一条评论中所说,您可以通过将suppress_callback_exceptions 设置为True 来抑制dash 应用程序初始化中的那些。查看错误消息的内容,您会发现它与我说的完全一样。
猜你喜欢
  • 2021-05-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-19
  • 2020-11-09
  • 2021-10-14
  • 1970-01-01
  • 2021-10-08
  • 1970-01-01
相关资源
最近更新 更多