dash icon indicating copy to clipboard operation
dash copied to clipboard

[BUG] dcc.Loading appears/disappears if used with app.long_callback

Open uns1 opened this issue 4 years ago • 6 comments

Describe your context

dash                      2.0.0
dash-bootstrap-components 0.13.0
dash-core-components      2.0.0
dash-extensions           0.0.60
dash-html-components      2.0.0
dash-table                5.0.0
- OS: Windows 10
- Browser Chrome
- Version 93.0.4577.82 (Official Build) (64-bit)

Describe the bug

dcc.Loading is "jittery" i.e. appears/disappears when it has children that are called from @app.long_callback. This was initially noticed when using dbc.Spinner (dash-bootstrap-components) but same holds for dcc.Loading as mentioned in reply to the x-ref issue on DBC

X-REF https://github.com/facultyai/dash-bootstrap-components/issues/706

Expected behavior

dcc.Loading should not appear/disappear or be "jittery" when used with @app.long_callback

Code

from app import app
import dash_bootstrap_components as dbc
from dash import html
from dash.dependencies import Input, Output
from dash.exceptions import PreventUpdate
from dash.long_callback import DiskcacheLongCallbackManager
import diskcache
from time import sleep

cache = diskcache.Cache("./cache")
long_callback_manager = DiskcacheLongCallbackManager(cache)

layout = dbc.Container(
    [
        html.Hr(),
        dbc.Row([
                dbc.Col([dbc.Spinner([
                    html.H1('TEST', id="raw-data-store"),
                ],
                    id='main-spinner',
                    size='lg',
                    fullscreen=True,
                    color='primary',
                    fullscreen_style={'backgroundColor': 'transparent'},
                    spinner_style={"width": "10rem", "height": "10rem"})]),
                dbc.Col([
                    dbc.Button("TEST BUTTON",
                               id='get-data-button',
                               color="primary",
                               block=True,
                               disabled=False,
                               outline=True,
                               size='lg',
                               style={'marginTop': '22px'},
                               ),
                ]),
                ]),
    ],
    fluid=True,
)


"""
LONG CALLBACK
"""


@app.long_callback(
    output=[Output("raw-data-store", "children")],
    inputs=[Input("get-data-button", "n_clicks")],
    manager=long_callback_manager,
    prevent_initial_call=True)
def get_data_from_sql(n_clicks):
    """
    Callback to show jittery spinner behavior when used with @app.long_callback
    """
    if n_clicks:
        print('Button Clicked - Going to Sleep')
        sleep(10)
        return ['DONT BE JITTERY SPINNER']

    raise PreventUpdate

Uses dbc.Spinner but same holds for dcc.Loading

uns1 avatar Sep 22 '21 17:09 uns1

Any ideas when this is will be fixed? I am also having issues with this, it seems to happen as the callback is constantly being called so the loading thinks it's a refresh each time.

mayurpande avatar Feb 14 '22 11:02 mayurpande

I am having the same issue

devopsquantance avatar Apr 19 '22 13:04 devopsquantance

@devopsquantance @mayurpande @uns1 Maybe you can try this code. I don't expect if this meets your expectation, but hopefully, it will help. i just modified from official web here

import time
import dash
from dash import html
from dash.long_callback import DiskcacheLongCallbackManager
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc


import diskcache
cache = diskcache.Cache("./cache")
long_callback_manager = DiskcacheLongCallbackManager(cache)

app = dash.Dash(__name__,
                long_callback_manager=long_callback_manager,
                external_stylesheets=[dbc.themes.DARKLY])

app.layout = html.Div(
    [
        html.Div(
            [
                html.P(id="paragraph_id", children=["Button not clicked"], style={'margin': '5px'}),
                html.Div([
                    html.P(id="paragraph_id2", children=["Job Status : Not Yet Started"], style={'margin': '5px'}),
                    dbc.Spinner(id="progress_bar", spinner_style={'visibility': 'hidden'})]),
            ]
        ),
        html.Button(id="button_id", children="Run Job!"),
        html.Button(id="cancel_button_id", children="Cancel Running Job!"),
    ]
)


@app.long_callback(
    output=Output("paragraph_id", "children"),
    inputs=Input("button_id", "n_clicks"),
    running=[
        (Output("button_id", "disabled"), True, False),
        (Output("cancel_button_id", "disabled"), False, True),
        (
            Output("paragraph_id", "style"),
            {"visibility": "hidden", "margin": "5px"},
            {"visibility": "visible", "margin": "5px"},
        ),
        (Output("paragraph_id2", "children"), 'Job Status : on progress', 'Job Status : Done'),
        (
            Output("progress_bar", "spinner_style"),
            {"visibility": "visible"},
            {"visibility": "hidden"},
        ),
    ],
    cancel=[Input("cancel_button_id", "n_clicks")],
    progress=[Output("progress_bar", "children")],
    prevent_initial_call=True
)
def callback(set_progress, n_clicks):
    print(n_clicks)
    time.sleep(3)
    return [f"Clicked {n_clicks} times"]


if __name__ == "__main__":
    app.run_server(debug=True, port=8054)

hilmandei avatar Apr 19 '22 16:04 hilmandei

Using progress only works if you know when the operation will complete. The loading component is better for use cases where task progress cannot be measured. It would be really nice to get this fixed. In my case it isn't even jittery, I just can't even see loading component at all, the screen just flashes a couple times and the results show up once they come back.

FyZyX avatar May 11 '22 19:05 FyZyX

Has anyone found a way to have a dcc.Loading or dbc.Spinner component work with a long_callback? I have an app that has a long running background process (approximately 1.5 min) and it would be very beneficial to have a loading spinner running on the page while this happens. Almost crucial really.

I am having the same issue as @lucasmlofaro where I cannot even get the spinner to activate at all. I have another spinner on the page for a much faster process that works just fine. But I cannot get a dcc.loading or dbc.spinner component to work with the long_callback.

WaleedAlfaris avatar Jun 01 '22 10:06 WaleedAlfaris

Not sure if this is the solution you are looking for, but I have discovered a method that works using the dbc.Spinner in conjunction with the running keyword argument of the long_callback.

First, create a div where you want the spinner to appear, give it a unique id name and set its children equal to None. For me, I used a dbc.Row(dbc.Col()) instead of a div, but it should work the same. Then you will need to set the running keyword argument of the long_callback to output a dbc.Spinner. The following code is currently working for me. I have removed a lot of code, so ignore the inputs, ouputs and lack of layout components. Pay attention to the running kwarg.

app.layout = dbc.Container(dbc.Row(dbc.Col(id='fs_spinner', children=None)))

@app.long_callback(
    output=(
        Output("full_search_predictions", "data"),
        Output("full_results_query", "data"),
    ),
    inputs=[
        Input("submit_button", "n_clicks"),
        Input("search_query", "value"),
    ],
    running=[
        (Output('fs_spinner', 'children'), dbc.Spinner(size='md'), None)
    ],
    interval=20000,
    prevent_initial_call=True,
)

Here is what is happening. When the the long_callback begins, it returns a dbc.Spinner() to the children component of the fs_spinner. Once the callback is complete, it then returns None to the children component of the fs_spinner, which removes the spinner from the screen.

Hope this helps!

WaleedAlfaris avatar Jun 01 '22 11:06 WaleedAlfaris