What is the flow of Plotly dash program (python) - python

Actually I am new with python dash library and learning Plotly dash. Where I am bit confused that how this program flows when we run it?
(Like in java program first main method runs and then functions one by one in main method gets run...)
Can anyone just explain or will share the flow diagram?

The Plotly Dash workflow -
a) First create an app.
app = dash.Dash(__name__, requests_pathname_prefix = '/app',
external_stylesheets = external_stylesheet)
b) Secondly create the app layout.
app.layout = html.Div([
html.Div([....])
])
c) Thirdly create the callbacks
#app.callback(Output(component_id = 'id' , component_property = 'data'),
[Input(component_id = 'id', component_property = 'value'])
d) Fourthly, run the app.
app.run_server(debug = True)

Related

Dash python: server-side vs client-side callback

I am experimenting with front-end work and web-based apps and I am wondering if the following the best approach to avoid performance issues.
Part of this doubt comes from not knowing exactly if it would be better in this case to have a server-side or client-side callback (I haven't fully grasped when it is recommended to implement each).
Here are the details:
I have a computationally intense process that runs using MyClass.myFunction().
This involves a series of sub-functions that fetch data from a database, do calculations/manipulations and then load the data up on screen.
Currently I have:
A button that fires the fetch and data manipulation.
A container to store the retrieved data when the button is clicked.
A dash.DataTable to visualize that refreshes when the button is
clicked and that visualizes the output of MyClass.myFunction().
Users will then be able to modify the displayed data and send the results to a database (have not implemented the button yet).
Extra info: MyClass.myFunction() requires some input (input_parameters) this is a yaml configuration file (stored as dictionary) containing dates, strings and doubles, to allow the function to retrieve the appropriate values.
They are displayed in a different page to allow users to modify them and then preserved for the session.
import dash
from dash import Dash, html, dcc, dash_table, Input, Output, State, callback, ctx
from dash.exceptions import PreventUpdate
import pandas as pd
from mymodule import SomeClass
dash.register_page(__name__, name="My Page")
MyClass= SomeClass()
number_clicks_fetch = 0
layout = html.Div(
children=[
html.Div(children=[
html.H1(children='My title'),
dash_table.DataTable(
# data= #this will be populated via callback
# columns= #this will be populated via callback
persistence=True,
persisted_props =['columns.name', 'data'],
persistence_type='session',
virtualization=True,
id='data_table'
)
], style={'width': '100%,'}),
#store fetched data for the remainder of the session
dcc.Store(id='data_storage', storage_type='session'),
html.Button('Fetch data', id='fetch-data-button', n_clicks=number_clicks_fetch)
])
# if button clicked, populate table
#callback(Output('data_storage', 'data'),
Input('fetch-data-button', 'n_clicks'),
Input('input_parameters', 'data'),
)
def fetch_data(clicks, input_parameters):
global MyClass
if clicks == 0:
raise PreventUpdate
else:
mydata = MyClass.myFunction(input_parameters)
return mydata.to_dict('records')
# if storage is updated, update table
#callback([Output('data_table','data'),
Output('data_table','columns')],
Input('fetch-data-button', 'n_clicks'),
State('data_storage', 'data'))
def refresh_table(n_clicks, data):
if n_clicks == 0:
raise PreventUpdate
else:
columns = [{'id': c, 'name': c, "deletable": False, "selectable": True} for c in data[0].keys()],
# print(data)
return [data, columns]

Spinner removed when using long_callback in Dash app

My app works fine when I use the regular Dash callback, but I am currently forced to use long_callback. In this case, my spinner (dcc.Loading) disappears almost instantly - although, ironically, this seems to be a really useful place for a spinner. I have tried to create a minimal example below.
Am I misusing the spinners or is this a bug?
import dash
from dash import dcc
from dash import html
from dash.dependencies import Input, Output
from dash.long_callback import DiskcacheLongCallbackManager
import diskcache
import time
cache = diskcache.Cache('./cache')
long_callback_manager = DiskcacheLongCallbackManager(cache)
app = dash.Dash(
__name__,
long_callback_manager=long_callback_manager,
)
app.layout = html.Div(children=[
dcc.Slider(id='my_slider', min=1, max=10, step=1, value=5),
dcc.Loading(
html.Div(id='my_div', children=['Start']),
),
])
#app.long_callback(
Output('my_div', 'children'),
Input('my_slider', 'value'),
interval=2000,
)
def update_div(slider_value):
time.sleep(5)
return f'Done loading: {slider_value}'
if __name__ == '__main__':
app.run_server(debug=True)
I have tried to set the loading_state/is_loading property in the running argument of the long_callback, but I haven't succeeded. Maybe it's not a "setable" property?
Not sure if this is the solution you are looking for, but I have discovered a method that works with dash bootstrap components 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!

How to abstract dash plotly component into another file with a callback?

Currently, I have my application python file separated with the specific component in the Django framework.
I have seen a lot of examples writing each component with callback in the same file with the app python file. I am wondering how to write the update_output_div() function in a file with callback and let the app call it? Thank you so much!
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div([
...
])
#app.callback(
Output(component_id='my-output', component_property='children'),
Input(component_id='my-input', component_property='value')
)
def update_output_div(input_value):
return 'Output: {}'.format(input_value)
if __name__ == '__main__':
app.run_server(debug=True)
You could create a function that takes your app as a parameter and put your decorated function inside this function:
def init_callback(app):
#app.callback(
Output(component_id="my-output", component_property="children"),
Input(component_id="my-input", component_property="value"),
)
def update_output_div(input_value):
return "Output: {}".format(input_value)
Then you could call the function like this after defining app.layout like this:
init_callback(app)

Update application state outside of callback in plotly dash

I'm writing a plotly Dash application where I don't initially have database access, so I need to wait for the database connection before I can set the initial application state.
Thus, I want to run a function that will set the application state later on, but the only way to set state seems to be with #app.callback() decorators, but the problem is they require a property or state variable to watch before firing, but in my case I'm not watching part of the Dash app, I'm watching something external.
How can I do this in Dash?
For example:
app = Dash(routes_pathname_prefix='/dash/trend/')
app.layout = html.Div(children=[
dcc.Dropdown(
options=get_field_options(),
id='field_select',
multi=True,
)
])
#app.callback(
dash.dependencies.Output('field_select', 'options'),
[
# What do I put here as an input??
]
)
def update_fields(href):
return get_field_options()
You can use plotly dash store component to keep some data needed for event.
dcc.Store(id='local', storage_type='local'),
Dash Store component
Use a hidden html.Div() to store your information. This can be empty:
html.Div(id='application-state', style={'display': 'none'})
So your code should look like this:
import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output
app = Dash(routes_pathname_prefix='/dash/trend/')
app.layout = html.Div(children=[
dcc.Dropdown(
options=get_field_options(),
id='field_select',
multi=True,
),
html.Div(id='application-state', style={'display': 'none'})
])
#app.callback(
[Output('field_select', 'options')],
[Input('application-state', 'children')])
def update_fields(href):
if href =! None:
return get_field_options()
else:
return []
Your external part should update the respective hidden div. You can change this to any other component that is able to store the information you want to transfer to the dash visualization. You find the best sources and tricks for this problem domain here.
The if else part for returning a valid object is a bit tricky. Please let me know if there is a callback output error like Expected 1 output but received None or something similar.

Flask-Babel not working on the login page

I have a Flask/Dash app that uses the Flask-Babel module. The translation works like a charm after logging in and browsing through pages. However, I cannot make it work for the login page which will always be displayed in English. The messages.po and messages.mo both contain the translation strings I prepared for the login page, so the compilation part seems to work fine.
Here's a snippet from my app.py (with a hard-coded choice of Spanish):
import dash
from dash.dependencies import Input, Output
from flask import Flask, request
from flask_babel import Babel
# some more imports...
# (...)
def main():
update_dataframes()
app = dash.Dash(
"MyApp",
url_base_pathname='/summary',
static_folder="static",
sharing=True,
csrf_protect=False
)
# Hook Flask-Babel to the app
babel = Babel(app.server)
#babel.localeselector
def get_locale():
# return request.accept_languages.best_match(context.config['LANGUAGES'].keys())
return 'es'
# App layout
app.layout = build_app_layout(context)
# Setup callbacks
setup_callbacks(app)
setup_login(app, app.server, context.config)
# Start Dash/Flask app
app.server.run(
port=context.config['DEPLOY']['SERVER_PORT'],
debug=context.config['DEPLOY']['SERVER_DEBUG'],
threaded=context.config['DEPLOY']['SERVER_THREADED']
)
# Interval tread to update all dataframes
ThreadPoolExecutor(max_workers=1).submit(update_dataframes_thread)
if __name__ == '__main__':
main()
Below, a part of the setup_login(...) method called above. I'd like to notice that app.server is passed to it from the code above, after Flask-Babel has been hooked to the app (don't really know if that matters much):
from dash_flask_login import FlaskLoginAuth
from flask_login import LoginManager, UserMixin, login_user, logout_user
# (...)
login_app = Dash(
name='login-app',
url_base_pathname='/login',
server=app.server
)
What I tried: hooking the Flask-Babel again for the login_app Dash() instance, but that didn't work (anyways it's still the same app.server).
I've come across this SO question with a similar problem, but it seems to be specific to Flask-Security module (not my case).
What am I missing to make the login page translated?
Although I haven't found a direct reason why a combination of Dash and Flask-Login don't work with Flask-Babel on the login page, I solved the problem with a workaround - I'm dynamically updating generated HTML component through Dash's callback decorator just after loading the page. The function simply substitutes the original English some_string with gettext(some_string) tag which is detected properly in the callbacks. This way the page loads in English and immediately gets translated, as the callbacks come to action. Minimal example:
app.layout = html.Div(
[
html.H1('Hello World!', id='h1'),
html.Div(html.A('login', href='/login')
]
)
# Display the message translated to another language
#app.callback(
Output('h1', 'children'),
[Input('url', 'search')]
)
def translate_message(children):
return gettext('Hello World!')

Categories