在回调执行期间禁用 Plotly Dash 组件,然后在回调完成后重新启用它

Disable a Plotly Dash component during callback execution, then re-enabling it after the callback has finished

提问人:disguisedtoast 提问时间:10/20/2023 更新时间:10/21/2023 访问量:69

问:

我有 Plotly Dash 组件,需要在触发回调后禁用。回调运行完毕后,需要启用相同的组件。我尝试了以下方法。虽然组件被禁用,但在完成回调函数后不会启用。


from dash.exceptions import PreventUpdate
from dash_extensions.enrich import (
    Output,
    DashProxy,
    html,
    Input,
    MultiplexerTransform,
)
from time import sleep

app = DashProxy(__name__, transforms=[MultiplexerTransform()])

app.layout = html.Div(
    [
        html.Button(id="button", children="input-button", style={"color": "#FF0000"}),
        html.Div(id="trigger", children=None, style={"display": "none"})
    ],
)

@app.callback(
    [
        Output("button", "disabled"),
        Output("button", "style"),
        Output("trigger", "children"),
    ],
    [
        Input("trigger", "children")
    ],
    prevent_initial_call=True
)
def enable_components_after_export(trigger):
    if trigger == 1:
        return [
            False, {"color": "#FF0000"}, 0
        ]

    raise PreventUpdate


@app.callback(
    [
        Output("button", "disabled"),
        Output("button", "style")
    ],
    [
        Input("button", "n_clicks"),
    ],
    prevent_initial_call=True
)
def disable_components_on_export_button_click(button):
    if (button is not None and button > 0):
        return [
            True, {"color": "#808080"}
        ]

    raise PreventUpdate

@app.callback(
    [
        Output("trigger", "children")
    ],
    [
        Input("button", "n_clicks"),
    ],
    prevent_initial_call=True
)
def callback_function(button):
    if button is not None and button > 0:
        sleep(5)
        return [1]
    
    raise PreventUpdate

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

在回调期间禁用组件,然后在完成回调后重新启用组件的正确方法是什么?另外,我记得 Dash 不支持多个回调输出,这就是使用 DashProxy 和 MultiplexerTransform 的原因,尽管我不确定这是否导致了我的问题。

包版本

dash                      2.12.1                   pypi_0    pypi       
dash-bootstrap-components 1.5.0                    pypi_0    pypi       
dash-core-components      2.0.0                    pypi_0    pypi       
dash-extensions           0.1.11                   pypi_0    pypi       
dash-html-components      2.0.0                    pypi_0    pypi       
dash-renderer             1.9.1                    pypi_0    pypi 
python 用户界面 回调 plotly-dash

评论


答:

0赞 EricLavault 10/21/2023 #1

我不确定使用这里是否应该解决这个问题,但无论如何,由于 2.9 版 Dash 允许重复的回调输出:多个回调可以针对相同的输出,唯一的事情就是在回调输出中指定,例如。DashProxyallow_duplicate=True

@app.callback(
    [
        Output("button", "disabled", allow_duplicate=True),
        Output("button", "style", allow_duplicate=True),
        Output("trigger", "children", allow_duplicate=True),
    ],
    [
        Input("trigger", "children")
    ],
    prevent_initial_call=True
)

请注意,您也可以使用简单的后台回调(即一个回调而不是三个回)来实现此目的。

您可以通过设置回调来配置回调以在后台运行。使用您配置的后端管理器来运行回调逻辑的回调。您可以将此管理器实例作为关键字参数提供给应用构造函数,或作为装饰器的参数。background=Truebackground=Truedash.Dashbackground_callback_managermanager@dash.callback

这个想法是使用允许根据回调状态设置组件属性的参数(即在回调运行时禁用按钮):running

@app.callback(
    Output("trigger", "children"),
    Input("button", "n_clicks"),
    background=True,
    running=[
        (Output("button", "disabled"), True, False),
        (Output("button", "style"), {"color": "#808080"}, {"color": "#FF0000"}),
    ],
    prevent_initial_call=True
)
def callback_function(button):
    if button is not None and button > 0:
        time.sleep(5)
        return [1]
    
    raise PreventUpdate
0赞 Waleed Malik 10/21/2023 #2

使用Dash v2.9+,您可以有多个回调来针对相同的输出。下面是一个完整的工作示例,说明如何在触发回调函数后禁用该按钮:

from dash import Dash, html, Input, Output, no_update
from time import sleep

app = Dash(__name__)

app.layout = html.Div(
    [
        html.Button(id="button", children="input-button", n_clicks=0, style={"color": "#FF0000"}),
        html.Div(id="trigger", children=None)
    ],
)

@app.callback(
    Output("button", "disabled", allow_duplicate=True),
    Input("button", "n_clicks"),
    prevent_initial_call=True,
)
def disable_btn(n):
    if n:
        return True
    return no_update


@app.callback(
    Output("trigger", "children"),
    Output("button", "disabled"),
    Input("button", "n_clicks"),
    prevent_initial_call=True,
)
def update_content(n):
    if n:
        sleep(5)
        return 1, False
    return no_update

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