Hello everyone, happy monday
Iâd to share with you a really cool dash-core-components
prerelease: the ability to preserve UI state in the dcc.Graph
between callbacks.
Right now, if youâre updating the figure
property of a dcc.Graph
component, the graphâs âviewâ will get reset when the callback is fired. That is, if you zoom in your graph, click on the legend items, or twist a 3D surface plot, then those changes wonât be preserved across callback updates.
For many callbacks, this is OK and actually desirable: if your graph is updating with completely new data, perhaps with completely different axes ranges, youâll want the graph to recompute its ranges.
However, for certain callbacks, especially those that have a similar set of axes ranges, you may want to preserve the UI state between callbacks: if your viewers painstakingly zoom into a certain region of a chart then they might not that graph to completely reset when a dcc.Interval
fires or if they want to compare that region with another dataset.
pip install dash-core-components==0.39.0rc4
This version includes a new property in the layout
property of the figure
property of dcc.Graph
: uirevision
.
uirevision
is where the magic happens. This key is tracked internally by dcc.Graph
, when it changes from one update to the next, it resets all of the user-driven interactions (like zooming, panning, clicking on legend items). If it remains the same, then that user-driven UI state doesnât change.
It can be equal to anything, the important thing is to make sure that it changes when you want to reset the user state.
In the example below, we only reset the zoom when we change the dataset dropdown. If we change the color or if we add a âreferenceâ trace, then we donât reset the zoom. Thus, we set the uirevision
property to the dataset
value. Read more in the comments embedded in the example.
import dash
from dash.dependencies import Input, Output
import dash_html_components as html
import dash_core_components as dcc
import pandas as pd
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/stockdata.csv')
df['reference'] = df[df.columns[0]]
app = dash.Dash(__name__)
app.scripts.config.serve_locally = True
app.css.config.serve_locally = True
app.layout = html.Div([
html.Label('Color'),
dcc.Dropdown(
id='color',
options=[
{'label': 'Navy', 'value': '#001f3f'},
{'label': 'Blue', 'value': '#0074D9'},
{'label': 'Aqua', 'value': '#7FDBFF'},
{'label': 'TEAL', 'value': '#39CCCC'},
{'label': 'OLIVE', 'value': '#3D9970'},
{'label': 'GREEN', 'value': '#2ECC40'},
{'label': 'LIME', 'value': '#01FF70'},
{'label': 'YELLOW', 'value': '#FFDC00'},
{'label': 'ORANGE', 'value': '#FF851B'},
{'label': 'RED', 'value': '#FF4136'},
{'label': 'MAROON', 'value': '#85144b'},
{'label': 'FUCHSIA', 'value': '#F012BE'},
{'label': 'PURPLE', 'value': '#B10DC9'},
],
value='#001f3f'
),
html.Label('Reference'),
dcc.RadioItems(
id='reference',
options=[{'label': i, 'value': i} for i in ['Display', 'Hide']],
value='Display'
),
html.Label('Dataset'),
dcc.Dropdown(
id='dataset',
options=[{'label': i, 'value': i} for i in df.columns],
value=df.columns[0]
),
dcc.Graph(id='graph')
])
@app.callback(
Output('graph', 'figure'),
[Input('color', 'value'),
Input('reference', 'value'),
Input('dataset', 'value')])
def display_graph(color, reference, dataset):
data = [{
'x': df.index,
'y': df[dataset],
'mode': 'lines',
'marker': {'color': color},
'name': dataset
}]
if reference == 'Display':
data.append({'x': df.index, 'y': df['reference'], 'mode': 'lines', 'name': 'Reference'})
return {
'data': data,
'layout': {
# `uirevsion` is where the magic happens
# this key is tracked internally by `dcc.Graph`,
# when it changes from one update to the next,
# it resets all of the user-driven interactions
# (like zooming, panning, clicking on legend items).
# if it remains the same, then that user-driven UI state
# doesn't change.
# it can be equal to anything, the important thing is
# to make sure that it changes when you want to reset the user
# state.
#
# in this example, we *only* want to reset the user UI state
# when the user has changed their dataset. That is:
# - if they toggle on or off reference, don't reset the UI state
# - if they change the color, then don't reset the UI state
# so, `uirevsion` needs to change when the `dataset` changes:
# this is easy to program, we'll just set `uirevision` to be the
# `dataset` value itself.
#
# if we wanted the `uirevision` to change when we add the "reference"
# line, then we could set this to be `'{}{}'.format(dataset, reference)`
'uirevision': dataset,
'legend': {'x': 0, 'y': 1}
}
}
if __name__ == '__main__':
app.run_server(debug=True)
Let us know what you think! We will leave this open for community feedback for a week or two before releasing it.