破折号回调防止其他回调

Posted

技术标签:

【中文标题】破折号回调防止其他回调【英文标题】:Dash callback preventing other callbacks 【发布时间】:2021-10-19 13:22:08 【问题描述】:

我对 Dash 比较陌生,并且认为我对回调非常了解。但是,我现在处于一种情况,似乎我需要在一个回调中包含所有回调,因为我的程序在调用该回调时工作正常。

当我有多个回调时,它们单独工作正常;这意味着如果我注释掉一个回调,则回调将按需要工作。我可以使用 context.triggered 获得正确的功能,但我不想在一个大规模回调中包含所有内容,我觉得这是我理解的一个问题。我尝试在回调中限制一个输入,但这仍然不起作用。我是否将整个应用布局传递给回调?

如果我是,我如何限制在 id 之外传递的内容?如何 可以回调吗?

一个改编的工作示例如下:

import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import dash_table
from dash.dependencies import Input, Output, State
import plotly.express as px
from dash import callback_context, no_update
import webbrowser
from flask import request
chrome_path = 'C:/Program Files (x86)/Google/Chrome/Application/chrome.exe %s'
df = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/Mining-BTC-180.csv").drop('Unnamed: 0',axis=1)
selected_cols = ['Number-transactions', 'Output-volume(BTC)',
       'Market-price', 'Hash-rate', 'Cost-per-trans-USD', 'Mining-revenue-USD',
       'Transaction-fees-BTC']

class Parameter_Viewer():
    def __init__(self, df, **kwargs):

        revenue_plot = px.scatter(df, x="Date", y="Mining-revenue-USD",title='Mining-revenue-USD')
        Hash_plot = px.scatter(df, x="Date", y="Hash-rate",title='Hash-rate')
        parameter_table = dash_table.DataTable(
            id='db-table',
            columns=["name": i, "id": i for i in selected_cols],
            data=df.to_dict('records'),
            #page_action="native",
            page_action='none',
            style_table='height': '300px', 'overflowY': 'auto',
            editable=True
        )
        app = dash.Dash()
        app.layout = html.Div(
            children=[
                #first row
                html.Div(
                    children=[
                        html.Div(children=[
                        html.H1("Parameter", className="menu-title"),
                        dcc.Dropdown(
                            id="parameter-filter",
                            options=[
                                "label": parameter, "value": parameter for parameter in df.columns
                            ],
                            value="Mining-revenue-USD",
                            clearable=False,
                            className="dropdown",
                        ),]),
                        html.Div(children=[
                        html.H1("Data Type", className="menu-title"),
                        dcc.Dropdown(
                            id="data-selector",
                            options=[
                                "label": data_col, "value": data_col for data_col in selected_cols
                            ],
                            value="Hash-rate",
                            clearable=False,
                            className="dropdown",
                        ),]),
                        html.Button("Close Viewer", id="Close_Btn")
                    ]
                ),
        html.Div(children=[
                html.H1(children="Database Analytics",),
                parameter_table]),
        html.Div(children=[
                html.Div(
                dcc.Graph(id='param-plot',figure=revenue_plot),
                style="width":'50%', "margin": 0, 'display': 'inline-block',className="six columns"),
                html.Div(
                dcc.Graph(id='param-plot2',figure=Hash_plot),
                style="width":'50%', "margin": 0, 'display': 'inline-block',className="six columns")],className="row")
        ])
        @app.callback(
            [Output("db-table", "data"), Output("param-plot", "figure"), Output("param-plot2", "figure")],
            [Input("parameter-filter", "value"),Input("data-selector", "value"),Input('db-table', 'data')])#,prevent_initial_call=True)
        def update_charts(parameter,data_type,table_data):
            changed_inputs = [x["prop_id"]for x in callback_context.triggered]
            if changed_inputs[0] == 'parameter-filter.value':
                df = pd.DataFrame.from_dict(table_data)
                ua_plot = px.scatter(df, x="Date", y=data_type)
                aa_plot = px.scatter(df, x="Date", y=parameter)
                return table_data, ua_plot, aa_plot
            elif changed_inputs[0] == 'db-table.data':
                df = pd.DataFrame.from_dict(table_data)
                ua_plot = px.scatter(df, x="Date", y=data_type)
                aa_plot = px.scatter(df, x="Date", y=parameter)
                return no_update, ua_plot, aa_plot
            else:
                return no_update,no_update,no_update
        @app.callback(Output("db-table", "data"),Input("Close_Btn", "n_clicks"),prevent_initial_call=True)
        def close_browser(n_clicks):
            print('In close callback\n')
            if n_clicks>0:
                self.shutdown()
            return no_update

        host='127.0.0.1'
        port='8050'
        url = 'http://:/'.format(host,port)
        webbrowser.get(chrome_path).open(url)
        app.run_server(debug=False)
    def shutdown(self):
            func = request.environ.get('werkzeug.server.shutdown')
            if func is None:
                raise RuntimeError('Not running with the Werkzeug Server')
            func()


Parameter_Viewer(df)```


【问题讨论】:

将第一个回调分解为以下也不起作用:``` @app.callback( [Output("db-table", "data"), Output("param-plot" , "figure"), Output("param-plot2", "figure")], [Input("db-table", "value")],[State("data-selector", "value"),State ('参数过滤器', '数据')],prevent_initial_call=True) ''' 【参考方案1】:

考虑你的回调可能是什么样子的基本情况然后扩展它们会更容易。根据我的经验,我通常构造回调的主要原因是处理由特定输入引起的特定输出

例如

如果输出 O1 是输入 I1 的效果,那么我会进行回调 C1(I1 → O1)。

如果输出 O1 是输入 I1 和 I2 的影响,那么我进行回调 C1([I1, I2] → O1)

如果您碰巧有多个输出是输入的效果,那么您可以将这些输出合并到同一个回调中。

例如

输出O1是输入I1的效果输出O2是输入I1的效果,然后我做一个回调C1(I1→[O1,O2])

另外,请记住,Dash 不允许多个回调具有相同的输出组件(例如,特定组件属性可以与一个且只有一个回调关联)。在这种情况下,如果您有一个特定的输出需要与另一个同时更新,但不一定是同一输入的效果,那么您仍然应该将输出组合到单个回调中。

例如

如果输出O1是输入I1的效果并且输出O2必须用O1更新,那么我做一个回调C1(I1 → [O1, O2])

我不确定为什么在评论中您尝试使用 State() 而不是 Input(),因为 State() 用于在仍必须将属性读取为输入的情况下,但不一定必须是触发。

所以使用上面的逻辑,例如,

如果输出 O1 是输入 I1 的效果并且不是输入(我们现在称为状态)S1 的效果,但是 仍然需要 S1,然后我做一个回调 C1([I1, S1] → [O1])

只要回调不试图一次做太多不连贯的事情,有很多输入和输出的大回调是正常的(否则当你回去查看你的代码时,你会花费很多时间试图记住你确实相信我的事情:))。

【讨论】:

【参考方案2】:

如果你像我一样是初学者。您可能正在寻找最有价值的信息,就我而言,Daniel Al Mouiee 的回答:

Dash 不允许多个回调具有相同的输出组件

【讨论】:

以上是关于破折号回调防止其他回调的主要内容,如果未能解决你的问题,请参考以下文章

链式回调中的输入 - 破折号

破折号巧妙地克服了重复的回调

使用破折号创建仪表板时出现回调错误

回调模式其他模式

具有日期时间和散点图交互的绘图破折号范围滑块

响应式 WCF 客户端的双工回调或客户端线程