Dash plotly update label in between calculation

I’m new to dash plotly and using this for some of my personal projects. Now I have a long calculation running and I need to update some status bar like label with progress with the calculation. Something like this

app.layout = html.Div([
    html.Div([
        html.Button("Start calculation",n_clicks=0,id="btn")
        html.Label('Calculation 1 complete', style={'display':"none"}, id='cal1'),
        html.Label('Calculation 2 complete', style={'display':"none"}, id='cal1'),
        html.Label('Calculation 3 complete', style={'display':"none"}, id='cal1'),
])
])


@app.callback(
    [Output('cal1', 'style'),
    Output('cal2', 'style'),
    Output('cal3', 'style')],
    [Input('btn', 'n_clicks')])
def calculation():
    # long calulation 1
    #> make label id cal1 visible

    # long calulation 2
    #> make label id cal2 visible

    # long calulation 3
    #> make label id cal3 visible

    # calculation complete

But I don’t know how to achieve this. I can only have callbacks that have specific input that starts the function and at the end of the function update some specific output. Unlike pure javascript I cant just update some html anytime inside the function. Can anyone help me to solve this

Hi @koushikphy the documentation on loading states has what you need I think.

That Loading component feels pretty limited in usecase. Also how do I change the spinner to some simple text and how do I place that text inside some other element, say, first celll of a table?

One option is to write the status to a server side cache (e.g. a file or a memory cache) and read the status in another callback, which is triggered by an Interval component. While being a simple solution, it requires some book keeping, so i have written a few helper objects to make it easier. Here is a small example,

import time
import dash
import dash_core_components as dcc
import dash_html_components as html

from dash.dependencies import Input, Output
from dash_extensions.callback import CallbackPlumber, DiskPipeFactory

# Create example app.
app = dash.Dash(__name__, prevent_initial_callbacks=True)
app.layout = html.Div(
    [
        html.Button('Start', id='start', n_clicks=0), html.Div("Not running", id='progress'),
        html.Div(id='result'), dcc.Interval(id='trigger', interval=1000),
    ]
)
# Create CallbackPlumber, use a folder on disk (server side) to save messages in transit.
cp = CallbackPlumber(DiskPipeFactory("some_folder"))

@cp.piped_callback(Output('result', 'children'), [Input('start', 'n_clicks')])
def start_job(pipe, *args):
    n = 100
    for i in range(n):
        pipe.send("progress", float(i)/float(n)*100)  # send progress updates to the client
        time.sleep(0.1)  # sleep to emulate a long job
    return "The result!"

@app.callback(Output('progress', 'children'), [Input('trigger', 'n_intervals')])
def update_progress(*args):
    progress = cp.receive("start_job", "progress")  # get latest progress value from "start_job"
    if progress is not None:
        return "Running (progress = {:.0f}%)".format(progress)
    return "Not running"

cp.register(app)  # this call attaches the piped callback

if __name__ == '__main__':
    app.run_server()

If you would like to try it out, you wil need to install dash-extensions,

pip install dash-extensions==0.0.20