[SOLVED] Unclickable button while heavy processing

Hi,
I’m doing a dash and I wan’t to block multiple Callback when people press a button multiple times. It would only be allowed to send a callback again when the process from the first button press end.

Here is a simplified toy example:

import dash
import dash_html_components as html
import dash_core_components as dcc
import time
external_stylesheets = [‘https://codepen.io/chriddyp/pen/bWLwgP.css’]

app = dash.Dash(name, external_stylesheets=external_stylesheets)

app.layout = html.Div([
html.Div(dcc.Input(id=‘input-box’, type=‘text’)),
html.Button(‘Submit’, id=‘button’),
html.Div(id=‘container-button-basic’,
children=‘Enter a value and press submit’)
])

@app.callback(
dash.dependencies.Output(‘container-button-basic’, ‘children’),
[dash.dependencies.Input(‘button’, ‘n_clicks’)],
[dash.dependencies.State(‘input-box’, ‘value’)])
def update_output(n_clicks, value):

print("HEAVY PROCESS {}".format(value))
time.sleep(5)
print("DEPLOY HEAVY PROCESS {}".format(value))
return 'The input value was "{}" and the button has been clicked {} times'.format(
    value,
    n_clicks
)

if name == ‘main’:
app.run_server(debug=True)

Try wrapping your button in a dcc.Loading: https://dash.plot.ly/dash-core-components/loading_component

I think that mostly achieves what you want.

@Damian Sorry, but it didn’t stop multiple clicks.

NEW EXAMPLE:

import dash
import dash_html_components as html
import dash_core_components as dcc
import time
external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

app.layout = html.Div([
html.Div(dcc.Input(id="input-box", type="text")),
html.Button("Submit", id="button"),
dcc.Loading(
    id="loading-2",
    children=[html.Div([html.Div(id="loading-output-2")])],
    type="circle"),
html.Div(id="container-button-basic",
children="Enter a value and press submit")
])

@app.callback(
[dash.dependencies.Output("container-button-basic", "children"),
 dash.dependencies.Output("loading-output-2","children")],
[dash.dependencies.Input("button", "n_clicks")],
[dash.dependencies.State("input-box", "value")])
def update_output(n_clicks, value):
    print("HEAVY PROCESS {}".format(value))
    time.sleep(5)
    print("DEPLOY HEAVY PROCESS {}".format(value))
    return 'The input value was "{}" and the button has been clicked {} times'.format(
        value,
        n_clicks),""


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

Almost there but the button needs to be inside the Loading component, I’ve rewritten your code a little bit to make it work how I think you want, give it a try:

import dash
import dash_html_components as html
import dash_core_components as dcc
import time
external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

app.layout = html.Div([
    html.Div(dcc.Input(id="input-box", type="text")),
    html.Div(
        style={'max-width': '110px'},
        children=[
            dcc.Loading(
                id="loading-2",
                children=[html.Button("Submit", id="button"),
                          html.Div(id="loading-output-2")],
                type="circle"),
        ]
    ),
    html.Div(id="container-button-basic",
             children="Enter a value and press submit")
])


@app.callback(
    [dash.dependencies.Output("container-button-basic", "children"),
     dash.dependencies.Output("loading-output-2", "children")],
    [dash.dependencies.Input("button", "n_clicks")],
    [dash.dependencies.State("input-box", "value")])
def update_output(n_clicks, value):
    if n_clicks is None:
        return '', ''
    print("HEAVY PROCESS {}".format(value))
    time.sleep(5)
    print("DEPLOY HEAVY PROCESS {}".format(value))
    return 'The input value was "{}" and the button has been clicked {} times'.format(
        value,
        n_clicks), ''


if __name__ == '__main__':
    app.run_server(debug=False)
1 Like

Awesome, I thought It couldn’t make the button “Disapear”.
thanks man!

Is there a way to omit the dash.dependencies.Output(“loading-output-2”, “children”), so I don’t have to give an empty string as a return?

Thanks @Damian !

You could make it a 2nd callback instead, but then it won’t be tied to the time it takes to complete your heavy loading. But other than that I don’t know.

It’s very new component just announced last month, so I’ve only used it a couple times and am still learning: 📣 Loading states and Loading component

1 Like