How to display message during long running callback

Hi,
I’m writing a pretty simple Dash application. One of my callbacks could take a minute or two to complete while it executes some code. I have included a dcc.loading element so that I get a nice spinner while my callback is running. However, I’d like to also display a little text message, perhaps in a modal, that says something like “Please be patient - this may take a few minutes.” I’d like the message to appear when the callback starts running and disappear when it is finished.

However, I can’t quite seem to figure out how to do that. I tried using two call backs:

  1. My long running callback called run_app. This callback takes as input a button pressed and outputs to two elements, one an invisible div that is a child of my dcc.loading element and the second another invisible div that will be used to indicate that the run is complete.
  2. The callback that will display my warning message called toggle_run_modal. It takes as input the button pressed and the hidden div that indicates the run is complete (the output of the first callback). This callback will check to see who triggered it, the button (run started) or the hidden div (run completed) and either show the warning or hide the warning respectively.

The problem is that Dash is smart and will see that the input of the second depends on the output of the first, and so the second callback doesn’t appear to get called until after my run is complete.

Does anyone have any suggestions on how best to just display a little message while my long callback is executing?

My simple test code is below:

import dash
import dash_bootstrap_components as dbc
import dash_core_components as dcc
from dash_extensions.enrich import DashProxy, EnrichedOutput, GroupTransform, Input, Output, ServersideOutput, ServersideOutputTransform, State
import dash_html_components as html
import time

EXTERNAL_STYLESHEETS = [dbc.themes.BOOTSTRAP]

app = DashProxy(
    __name__,
    external_stylesheets=EXTERNAL_STYLESHEETS,
    suppress_callback_exceptions=True,
    #prevent_initial_callbacks=True,
    transforms=[
        GroupTransform(),
        ServersideOutputTransform()
    ],

)

server = app.server

def get_modal():
    return dbc.Modal(
        id="run-warning",
        children=[
            dbc.ModalBody(
                dcc.Markdown(
                    """
                    ## Warning
                    **Note**: Running this can take a long time!
                    """
                )
            )
        ],
        is_open=False,
    )

input_content = html.Div(
    [
        html.H1("My application"),
        html.Hr(),
        html.Div(id="run-done"),
        dbc.Button(
            "Run application",
            color="primary",
            block=False,
            id="run-button",
            disabled=False,
            className="mb-3",
        ),
        get_modal(),
        dcc.Loading(id="running-app-spinner", children=[html.Div(id="running-app-div")])
    ],
    id="input",
)

app.layout = html.Div([dcc.Location(id="url"), input_content])


@app.callback(
    Output("run-warning", "is_open"),
    Input("run-button", "n_clicks"),
    Input("run-done", "children"),
)
def toggle_run_modal(n_clicks, run_done):
    ctx = dash.callback_context
    if ctx.triggered:
        trigger_id = ctx.triggered[0]["prop_id"].split(".")[0]
    else:
        return False
    print("************")
    print(trigger_id)

    if trigger_id == "run-button" and n_clicks > 0:
        return True

    return False


@app.callback(
    Output("running-app-div", "children"),
    Output("run-done", "children"),
    Input("run-button", "n_clicks"),
)
def run_app(n_clicks):
    time.sleep(5)
    return [None, None]
    return [None, False]


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