Bring Drag & Drop to Dash with Dashboard Engine. 💫 Learn how at our next webinar!

Getting a button to change state to disabled while calc is running

I have a Dash app with a button which triggers a calculation.

The calculation itself takes about 20-30 seconds and I would like the button to change state to disabled and a spinner while it’s calculating.

I’ve tried to use the following approach with a ‘trigger’ div which stores the state of calculation, but because of the multiple callback restriction in Dash I haven’t been able to turn it back on when it finishes calculating.

I really cannot think of a way to get this to work with the 1-callback output restriction.

###LAYOUT WITH BUTTON AND CHART
app.layout = html.Div([
    html.Button(id='submit-button-state-go-run-query',disabled=False),
    html.Div(id='graph'),
    html.Div(id='trigger',children=0, style=dict(display='none'))
])


###ENABLE-DISABLE THE BUTTON
@app.callback(
    Output('trigger','children'),
    Input('submit-button-state-go-run-query', 'n_clicks'),
             )
def disbutton(n_clicks):
    if n_clicks > 0:
        return 1
    else:
        return 0
######################


###ENABLE-DISABLE THE BUTTON
@app.callback(
    Output('submit-button-state-go-run-query', 'disabled'),
    [Input('submit-button-state-go-run-query', 'n_clicks'),
     Input('trigger', 'children')])
def trigger_function(n_clicks, trigger):
    context = dash.callback_context.triggered[0]['prop_id'].split('.')[0]
    context_value = dash.callback_context.triggered[0]['value']
    if context == 'submit-button-state-go-run-query':
        if n_clicks > 0:
            return True
        else:
            return False
    else:
        return False
######################


###MAIN FUNCTION
@app.callback(
    Output('graph','children'),
     #Output('trigger','children'), #tried getting the trigger div value to change when calc finished - but ran into 1-output restriction
    [Input('button','n_clicks')])
def update_element(n_clicks):
	#20-SECOND CALCULATION HERE
    return fig0 #, 1

UPDATE: tried declaring a global variable to hold the state of calculation - the spinner now appears, but could not get the disabled state to change

computing = [False]

#CALLBACKS FOR QUERY
@app.callback(
    Output('graph0', 'figure'),

    Output("submit-button-state-go-run-query",'disabled'),
    Output("spinner",'children'),

    Input('submit-button-state-go-run-query', 'n_clicks'),
    State('EVENT_ID_state', 'value'), #
    State('MASTER_TICKER_STR_state', 'value'),
    State("full-input-boxes", "children"),
             )
def display_value0(n_clicks,event_id,master_ticker_str, children):

    if computing[0] == True:
        ...
    else:
        computing[0] = True
		
		#LONG CALC HERE
		
        computing[0] = False
    return fig_pg_0, False, []

Hi!

Have you considered changing your application to use a long callback?

thank you @jlfsjunior ! didn’t know about it before

I tried it (the exact same construction as in the example) and it is just always stuck on calculating even before I click the button. Could it be due to mixing regular callbacks and long callbacks in one application? The example from tutorial works for me when I run it as a standalone app, but the exact same code is always calculating when I run it in my app.

It shouldn’t be a problem to have normal callbacks with the long ones, but this is a very new functionality and there might be some bugs to be discovered.

I haven’t tried the long callback in my applications, just went through the examples, so I am not a good choice for help on that. There are some announcement threads where folks are discussing about it and you might find the info you need. I intend to try it in one of my prod apps for a similar use case than yours, but this is not a priority in my work… Sorry I can’t help you further. :slightly_frowning_face: