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
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,