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.
Related
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]
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!
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)
I'm trying to test the refresh page logic.
I read the manual:
https://dash.plotly.com/live-updates
And wrote this simple code:
import datetime
import dash
import dash_html_components as html
def get_time():
print("get time...")
return datetime.datetime.now()
def serve_layout():
return html.H1('The time is: ' + str(get_time()))
if __name__ == '__main__':
app = dash.Dash()
app.layout = serve_layout()
app.run_server(debug=True)
I expected to see that every page refresh (F5) I will see the log "get time...",
But I see this log only on startup and not on every refresh.
What am I missing ?
I want to wrote a logic that every page refresh (F5) the function serve_layout (and get_time) will be called.
How can I do it ?
It looks like you are assigning the rendered layout instead of a function, which is needed to update on page refresh. Hence if you replace the line
app.layout = serve_layout()
with
app.layout = serve_layout
it should work as expected.
I get an error when I use the tooltip={'always_visible': True} argument when creating a dash slider in python.
The error only appears when I move the slider.
When I remove the tooltip argument, the code runs fine (but I don't get the tooltip that I need).
# Here's a minimal example that reproduces the error
import dash
import dash_html_components as html
import dash_core_components as dcc
app = dash.Dash(__name__)
app.layout = html.Div([
dcc.Slider(
id='my-slider',
min=0,
max=20,
step=0.5,
value=5,
tooltip={'always_visible': True}
),
html.Div(id='output-container-slider')
])
#app.callback(
dash.dependencies.Output('output-container-slider', 'children'),
[dash.dependencies.Input('my-slider', 'value')])
def update_output(value):
return 'You have selected "{}"'.format(value)
if __name__ == '__main__':
app.run_server(debug=True)
I would expect the tooltip to show the current value of the slider as it moves and the tooltip to persist even I move the mouse away.
Instead, I get this error:
Failed component prop type: Invalid component prop `tooltip` key `visible` supplied to Slider.
Bad object: {
"visible": true
}
Valid keys: [
"always_visible",
"placement"
]
(This error originated from the built-in JavaScript code that runs Dash apps. Click to see the full stack trace or open your browser's console.)
Error: Failed component prop type: Invalid component prop `tooltip` key `visible` supplied to Slider.
Bad object: {
"visible": true
}
Valid keys: [
"always_visible",
"placement"
]
at propTypeErrorHandler (http://127.0.0.1:8050/_dash-component-suites/dash_renderer/dash_renderer.dev.js?v=1.0.1&m=1568039086:44125:11)
at CheckedComponent (http://127.0.0.1:8050/_dash-component-suites/dash_renderer/dash_renderer.dev.js?v=1.0.1&m=1568039086:40498:9)
at renderWithHooks (http://127.0.0.1:8050/_dash-component-suites/dash_renderer/react-dom#16.8.6.js?v=1.0.1&m=1568039086:13073:18)
at mountIndeterminateComponent (http://127.0.0.1:8050/_dash-component-suites/dash_renderer/react-dom#16.8.6.js?v=1.0.1&m=1568039086:15155:13)
at beginWork (http://127.0.0.1:8050/_dash-component-suites/dash_renderer/react-dom#16.8.6.js?v=1.0.1&m=1568039086:15760:16)
at performUnitOfWork (http://127.0.0.1:8050/_dash-component-suites/dash_renderer/react-dom#16.8.6.js?v=1.0.1&m=1568039086:19447:12)
at workLoop (http://127.0.0.1:8050/_dash-component-suites/dash_renderer/react-dom#16.8.6.js?v=1.0.1&m=1568039086:19487:24)
at renderRoot (http://127.0.0.1:8050/_dash-component-suites/dash_renderer/react-dom#16.8.6.js?v=1.0.1&m=1568039086:19570:7)
at performWorkOnRoot (http://127.0.0.1:8050/_dash-component-suites/dash_renderer/react-dom#16.8.6.js?v=1.0.1&m=1568039086:20477:7)
at performWork (http://127.0.0.1:8050/_dash-component-suites/dash_renderer/react-dom#16.8.6.js?v=1.0.1&m=1568039086:20389:7)
Thanks for the help!
Turns out it was a bug in the package and has since been fixed:
https://github.com/plotly/dash-core-components/issues/640
Alternatively, as a workaround for that version, one can start the app using:
dev_tools_props_check=False:
app.run_server(debug=True, dev_tools_props_check=False )