python 如何构造/设计一个多页dash应用程序以避免重复的回调错误?

nwo49xxi  于 2023-08-02  发布在  Python
关注(0)|答案(1)|浏览(134)

我正试图将我的单页 Jmeter 板应用程序重构为多页应用程序,以便提供一个“关于”页面,在该页面上我展示了有关 Jmeter 板的一些细节。但是,我遇到了重复的回调错误。注意
1.当我第一次启动应用程序时,根页面加载良好,并按预期工作。
1.如果我刷新页面,我得到重复的回调错误
1.如果我导航到关于页面,我得到重复的回调错误
我在下面提供了一个完整的MWE,它再现了错误。
我的想法
我认为问题在于我通过app = dash.get_app()在页面中获取Dash示例。每当出现重复回调错误时,我在回调树调试器工具中注意到连接增加了一倍。
我想我需要将app传递给实用函数,因为我需要它们来设置回调。我决定把回调函数放在实用函数里面,因为
1.我正在生成许多外观相同的组件,其中图形数据和标题不同。通过这种方式,我可以通过传递相关参数来调用同一个实用函数,从而生成所有这些组件。
1.我认为,当更新图形的回调位于负责生成图形的函数内部时,代码是简单的。

树结构

.
├── main.py
├── pages
│   ├── about.py
│   └── home.py
└── utils.py

字符串

utils.py

import plotly.graph_objects as go
import numpy as np

from dash import Dash, Patch, html, dcc
from dash.dependencies import Input, Output

def sine(xs: np.array) -> np.array:
    return 100 * np.sin(xs)

def make_figure(app: Dash) -> html.Div:
    fig = go.Figure()
    fig.add_trace(go.Scatter(
        x=None,
        y=None,
        name="Randomized Data"
    ))

    fig.update_layout(height=800, template="plotly_dark")

    @app.callback(
        Output("plotly-figure", "figure"),
        Input("dcc-interval", "n_intervals")
    )
    def update(n_clicks: int) -> Patch:
        xs = np.linspace(0, 6*np.pi, 100)
        ys = sine(xs) * np.random.random(size=len(xs))

        patch = Patch()
        patch["data"][0]["x"] = xs
        patch["data"][0]["y"] = ys

        return patch

    return html.Div(children=[
        dcc.Graph(figure=fig, id="plotly-figure")
    ])

def make_random_title(app: Dash) -> html.Div:
    adjectives = [
        "Affable",
        "Affluent",
        "Deprived",
        "Destitute",
        "Elaborate",
        "Evocative",
        "Frivolous",
        "Gritty",
        "Immense"
    ]

    nouns = [
        "Crackdown",
        "Culprit",
        "Discrepancy",
        "Endorsement",
        "Enmity",
        "Gravity",
        "Immunity",
        "Juncture",
        "Legislation",
        "Offspring"
    ]

    @app.callback(
        Output("random-title", "children"),
        Input("dcc-interval", "n_intervals")
    )
    def update(n_intervals: int) -> html.H1:
        return html.H1(f"{np.random.choice(adjectives)} {np.random.choice(nouns)}")
    
    return html.Div(children=[
        f"{np.random.choice(adjectives)} {np.random.choice(nouns)}"
    ], id="random-title")

pages/about.py

import dash

dash.register_page(__name__, path="/about")

from dash import html

def layout() -> html.Div:
    return html.Div(children=[
        html.H1("About Page")
    ])

pages/home.py

import dash
import utils

dash.register_page(__name__, path="/")

app = dash.get_app()

from dash import html

def layout() -> html.Div:
    return html.Div(children=[
        utils.make_random_title(app),
        utils.make_figure(app)
    ])

main.py

import dash
import dash_bootstrap_components as dbc
from dash import Dash, html, dcc

def main() -> Dash:
    app = Dash(__name__, use_pages=True, external_stylesheets=[dbc.themes.SLATE])

    app.layout = html.Div(children=[
        dash.page_container,
        dcc.Interval(id="dcc-interval", n_intervals=0, interval=1000)
    ])

    return app

if __name__ == "__main__":
    app = main()
    app.run(debug=True)

requirements.txt

plotly
dash
numpy
dash-bootstrap-components

mnemlml8

mnemlml81#

这个问题的根源可能是糟糕的代码设计。或者更确切地说,代码设计具有难以预见的后果。我真的很希望dash社区能想出一种正确的方法来构建代码。一种方法,总是将工作没有黑客,这可以简化为简单的应用程序。

要修复重复的回调错误,您需要重构代码,以便将utils.py中的回调移动到相关的页面文件中。这样就不需要向下传递app对象,我认为这是问题的根源。
以下更新的文件内容运行时没有错误(并且还修复了我的生产 Jmeter 板中的相同问题,而不仅仅是MWE中的问题):

utils.py

import plotly.graph_objects as go
import numpy as np

from dash import html, dcc

adjectives = [
    "Affable",
    "Affluent",
    "Deprived",
    "Destitute",
    "Elaborate",
    "Evocative",
    "Frivolous",
    "Gritty",
    "Immense"
]

nouns = [
    "Crackdown",
    "Culprit",
    "Discrepancy",
    "Endorsement",
    "Enmity",
    "Gravity",
    "Immunity",
    "Juncture",
    "Legislation",
    "Offspring"
]

def sine(xs: np.array) -> np.array:
    return 100 * np.sin(xs)

def make_figure() -> html.Div:
    fig = go.Figure()
    fig.add_trace(go.Scatter(
        x=None,
        y=None,
        name="Randomized Data"
    ))

    fig.update_layout(height=800, template="plotly_dark")

    return html.Div(children=[
        dcc.Graph(figure=fig, id="plotly-figure")
    ])

def make_random_title() -> html.Div:
    return html.Div(children=[
        f"{np.random.choice(adjectives)} {np.random.choice(nouns)}"
    ], id="random-title")

字符串

pages/home.py

import dash
import utils

import numpy as np

from dash import html, Patch, callback
from dash.dependencies import Input, Output

dash.register_page(__name__, path="/")

@callback(
    Output("plotly-figure", "figure"),
    Input("dcc-interval", "n_intervals")
)
def update(n_clicks: int) -> Patch:
    xs = np.linspace(0, 6*np.pi, 100)
    ys = utils.sine(xs) * np.random.random(size=len(xs))

    patch = Patch()
    patch["data"][0]["x"] = xs
    patch["data"][0]["y"] = ys

    return patch

@callback(
    Output("random-title", "children"),
    Input("dcc-interval", "n_intervals")
)
def update(n_intervals: int) -> html.H1:
    return html.H1(f"{np.random.choice(utils.adjectives)} {np.random.choice(utils.nouns)}")

def layout() -> html.Div:
    return html.Div(children=[
        utils.make_random_title(),
        utils.make_figure()
    ])

相关问题