Dash 多页面应用无法检测到不同页面布局中的回调输出 ID

Dash multi-page app failing to detect callback output IDs in different page layouts

提问人:Alex M 提问时间:9/24/2023 更新时间:9/24/2023 访问量:77

问:

我正在尝试将我的多选项卡仪表板重新配置为多页应用程序,但遇到回调输出错误的问题。

以下是我如何模块化以跟踪应用程序的组件:

文件夹结构

- app.py 
- assets
- data_processing_scripts
- app_scripts
    - app_pages       
       |-- __init__.py
       |-- home.py
       |-- data_upload.py
       |-- visualisation.py
       |-- about.py
    - app_tabs
        - data_upload_tabs
           |-- software_1_tables_upload.py
        - data_visualisation_tabs
           |-- software_1_data_visualisation.py
    - app_data_store       
       |-- data_store_software_1.py
    - callbacks
       |-- all_callbacks.py
       |-- software_1_data_callbacks.py
    - navigation_bar
       |-- nav_bar.py

我使用 dash-uploader 库将多个表作为压缩文件上传到页面中。在这里,键入 dash-uploader 事件的初始回调会创建一系列输出,这些输出用作处理数据处理和最终绘图生成的其他回调的输入。然后,应将这些绘图和表格提供给应用程序,以便在可视化页面的各个子选项卡中显示。data_upload

页面只是定义页面总体布局的包装器。我从文件夹中的模块导入函数,这些模块返回布局的内容,即上传或可视化页面。这些布局引用各种回调输出 ID。html.Div()app_tabs

我对回调遵循了相同的逻辑。 从 导入回调。和被导入到其中,用于实例化应用。all_callbacks.pysoftware_1_data_callbacks.pypagesall_callbacksapp.py

我使用各种对象,这些对象将各种表作为回调的输入传递。在这里,我将它们包装在一个函数中,但不是将它们放在布局上的隐藏 div 中,而是将它们导入到 .dcc.Storedata_store_software_1.pydata_uploadapp.py

通过这种方式,我在 中引用了应用程序的所有必要部分。这种模块化适用于具有选项卡和嵌套子选项卡的单页应用。app.py

是的,如果你在介绍之后还没有关闭,这是我的设置方式:app.py

# =============================================================================

import dash_uploader as du
import dash_bootstrap_components as dbc
import dash
from dash import html

# =============================================================================

from app_scripts.navigation_bar.nav_bar import create_navbar
from app_scripts.callbacks.all_callbacks import all_callbacks
from app_scripts.app_data_store.maxquant_data_store import data_store_software_1

# =============================================================================

#### Define app.
app = dash.Dash(__name__, 
                suppress_callback_exceptions = True,
                prevent_initial_callbacks = True,
                pages_folder = './app_scripts/app_pages',
                use_pages = True,
                external_stylesheets = dbc.themes.BOOTSTRAP)

#### Define server.
server = app.server

# --------------------------------------------------------------------------- #

#### Configure uploader.
du.configure_upload(app, 
                    r'C:\tmp\data_uploads', 
                    use_upload_id = True)

# --------------------------------------------------------------------------- #

#### Generate navigation bar.
nav_bar = create_navbar()

# --------------------------------------------------------------------------- #

#### Generate layout.
app.layout = html.Div(
    style = {'background': '#f0f0f0'},
    children = [
        data_store_software_1(),
        nav_bar, 
        dash.page_container
        ]
    )

# --------------------------------------------------------------------------- #

#### Specify callbacks.
all_callbacks(app)

# --------------------------------------------------------------------------- #

#### Instantiate app.

if __name__ == '__main__':
    app.run_server(debug = True, 
                   dev_tools_hot_reload = False)
    
# --------------------------------------------------------------------------- #

什么有效:

该应用程序完全按照我的预期呈现。页面路由正确,各个页面和子选项卡的布局都显示正确无误。

什么不起作用:

回调输出 ID 错误:

A nonexistent object was used in an `Output` of a Dash ...

根据我导航到的页面,错误引用另一个页面布局中的回调输出 ID,反之亦然。我可以从堆栈跟踪中看到哪些 ID 在我单击的页面布局中:

The string ids in the current layout are: [list of ID's]

糟糕的解决方案

我必须同时调用页面布局。在这种情况下,我还必须包含每个回调输出 ID 的对象,包括绘图(各种绘图图形、绘图 png、pandas 样式表)。最终效果是,绘图在页面中的渲染速度非常慢,以至于无法使用,有时还会由于达到内存限制而引发浏览器错误。此外,单击并返回可视化页面,再次启动绘图渲染过程...慢慢。在我的标准 1 页方法中,只有表和字符串变量。data_store_software_1data_uploadvisualisationdcc.Store(id = 'some_callback_out')visualisationdcc.Store()

我试过了什么

1 - 从这里开始的最小示例 - https://github.com/AnnMarieW/dash-multi-page-app-demos/tree/main/multi_page_store

我使用这种方法时,将 直接传递给 中的应用程序布局。这会导致上述错误。data_store_software_1app.py

2 - 尝试验证 - https://dash.plotly.com/urls#dynamically-create-a-layout-for-multi-page-app-validation

在本例中,布局如下所示:app.py

app_contents = html.Div(
    style = {'background': '#f0f0f0'},
    children = [
        nav_bar, 
        dash.page_container]
    )

app.layout = app_contents

app.validation_layout = html.Div(children = [
    data_store_software_1(),
    app_contents]
    )

这里的错误仍然是:

A nonexistent object was used in an `Output` of a Dash ...

只不过它只是dash_uploader的第一个回调输出 ID。如果我随后将此 ID 添加到它,只需指定另一个 ID 等,因此再次回到方块 1。data_store_software_1()

3 - 可能是我遇到的问题的最接近的例子:

https://community.plotly.com/t/a-nonexistent-object-was-used-in-an-output-of-a-dash-callback/60897/18

在这里,我无法弄清楚如何按照建议拆分我的回调。仅将存储用于特定布局也会导致各种输出 ID 错误。

问题

显然,我看不到树木的树林,或者也许不能像我想象的那样完成。

我怎样才能解决这个问题?

非常感谢您抽出时间,很抱歉冗长的解释。我还想生成一个最小工作的例子,使用我的总体方法复制这个错误。最后,我认为描述性方法可能会更好,因为我不知道如何在这种情况下重新创建一个独立的 dash-uploader 实例。

python-3.x 回调 plotly-dash 多页应用程序

评论

0赞 Dmitry 9/25/2023
面对类似的问题,我最终将所有这些传递参数放入页面的 .有兴趣看看其他解决方案。url
0赞 Dmitry 9/25/2023
澄清更新:有一个作为主要触发器,其中包含其他所有必需的状态。Input('url', 'href')
0赞 Alex M 9/25/2023
嗨,德米特里,感谢您的回复!为了进一步澄清您的第一点,您的意思是像本例中那样路由页面导航 - stackoverflow.com/questions/59903698/...。关于设置状态,这是否适用于回调中可能充当触发器的每个输入,即 .我不知道的是是否可以将回调设置为.@callback([Output('table_2', 'data'), Output('table_3', 'data')], [State('ID_string', 'data'), State('table_1', 'data')])OutputState
0赞 Alex M 9/25/2023
还有一个想法 - 如果页面路由如上面的链接所示,我希望我可以直接在路由回调之后导入我的回调,就像我的问题一样 - 这是正确的吗?
0赞 Dmitry 9/26/2023
关于回调:必须可以只导入每个模块或包,并定义回调处理程序。 装饰器将在导入时执行。棘手的部分是确保它们在单个页面中的组件上运行。即:- 仅限当前页面,- 同一页面或全局存储,- 同一页面或全局存储,或位置。callbackInputStateOutput

答: 暂无答案