The application I’m developing loads data in from a third-party database, and I’m looking to setup a loading icon that displays whilst data is downloaded. However, this requires using a function that both returns in a regular, Pythonic way (once data has been downloaded), and outputs a loading callback. Is there any way to do both simultaneously, or a better way to handle this?
Hello @matt-buckley,
Do you have any code that you can post in order to expound on the second part?
You can display a loading spinner with this:
https://dash.plotly.com/dash-core-components/loading
This works by updating children of the spinner in the callback, so it will display any time you are trying to updating that specific element.
Sure, just as a pseudo-code example what I’m looking for is a simple function like this
def import_data(key):
return import_data_from_database(key)
that, until it returns, results in a loading icon being displayed in a particular area of the screen. However, in order to use the Loading spinner, the function must look like this:
@app.callback(
Output('loading-icon', 'children')
)
def import_data(key):
return ?
I guess my question is what to return here, as I wish to return the data to the function that called import_data() (as above), but any return output will instead be used as a callback, as far as I can see?
For the import_data, you can make your callback have two outputs.
The first is where you want the data to be displayed, and the second would be the (‘loading-icon’, ‘children’). You can return whatever you wish, this will make it update and no longer be there.
@app.callback(
Output(yourdatalocation, yourdatalocationprop),
Output('loading-icon', 'children'),
Input(yourtrigger, yourtriggerprop)
)
def import_data(key):
return import_data_from_database(key), ''
The challenge I have is the data will not be displayed directly on the dashboard, but put through further processing before being displayed in a graph. Given the dataset is too large to be stored in a dcc.Store component, is there any other way to return it to a function?
You dont need to return the data, if you are going to be managing the data on the backend, just return to the loading-icon children or ‘’ to let the user know that the data has been loaded successfully.
Is the variable (data) being stored as something on the server side?
Yes it will be stored as something like a DataFrame, and processed on the server side too which is the challenge, before being displayed as a graph.
Here, try this:
import time
import dash
from dash import html, dcc
from dash.long_callback import DiskcacheLongCallbackManager
from dash.dependencies import Input, Output
## Diskcache
import diskcache
cache = diskcache.Cache("./cache")
long_callback_manager = DiskcacheLongCallbackManager(cache)
app = dash.Dash(__name__, long_callback_manager=long_callback_manager)
app.layout = html.Div(
[dcc.Loading(id="progress_spinner"),
html.Div(
[
html.P(id="paragraph_id", children=["Button not clicked"]),
html.Div(id="progress_bar", style={"display": "none"},),
]
),
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"),
Output("progress_spinner", "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"},
{"visibility": "visible"},
),
(
Output("progress_bar", "style"),
{"display": "block"},
{"display": "none"},
),
],
cancel=[Input("cancel_button_id", "n_clicks")],
progress=[Output("progress_bar", "children")],
prevent_initial_call=True
)
def callback(set_progress, n_clicks):
total = 10
set_progress('Loading Data')
for i in range(total):
time.sleep(0.5)
set_progress('Successfully Loaded Data, performing additional crunching')
time.sleep(5)
return [f"Clicked {n_clicks} times", '']
if __name__ == "__main__":
app.run_server(debug=True,port=8054)
It uses the progress to update what has happened behind the scenes. This is just rudimentary, but it should give you some idea of how you could possibly solve your issue.
edit#2 added the use of a load in order to add some movement.