@itayma wrote:
Hi,
In a dashboard IтАЩm building I have one heavy callback that runs a series of operations and IтАЩd like to give the user some indication of the stage that is currently running.
I know we donтАЩt have asynchronous notifications yet, so I came up with a temporary solution that works but feels a bit patchy. IтАЩd love to get some feedback, especially if you see problems with such a solution or have a better one.
The idea is essentially to create a callback that calls itself as long as the process lasts, running different functions and producing matching outputs. IтАЩm using different levels in the html to bypass the restriction on dependency cycles.
HereтАЩs the code:
import time import dash import dash_core_components as dcc import dash_html_components as html from dash.dependencies import Input, Output from dash.exceptions import PreventUpdate app = dash.Dash(__name__) server = app.server app.layout = html.Div(children=[ html.Button('Run', id='run_button'), html.Div( id='output_container', children=[ html.Div(children='', id='output_text'), html.Div(id='process_stage', children=-1, style={'display': 'none'}) ] )] ) def dummy(): return def step1(): time.sleep(1) def step2(): time.sleep(1.5) def step3(): time.sleep(2) process_plan = [ [dummy, ''], [step1, 'Running step 1...'], [step2, 'Running step 2...'], [step3, 'Running step 3...'], [dummy, 'Process finished.'] ] @app.callback([Output('process_stage', 'children'), Output('run_button', 'disabled')], [Input('run_button', 'n_clicks')]) # start the process by setting the stage to 0 def button_clicked(click): if not click: return -1, False else: return 0, True # this callback triggers itself by returning the div container as output and getting triggered by one of its children as input @app.callback(Output('output_container', 'children'), [Input('process_stage', 'children')]) # the process runs as long as the stage (stored in a hidden div) is between 0 and the number of stages in the plan def run_process(stage): if stage == -1 or stage == len(process_plan)-1: raise PreventUpdate process_plan[stage][0]() # the callback returns the next stage's output and runs the current stage's function, so output matches what's currently running return [html.Div(children=process_plan[stage+1][1], id='output_text'), html.Div(id='process_stage', children=stage+1, style={'display': 'none'})] if __name__ == '__main__': app.run_server(debug=True)
Posts: 1
Participants: 1