Hi all,
recently I started playing around with partial updates and the allow_duplicate=True
parameter. For my app the benefit of using the new features is huge.
I thought I share two MRE do demonstrate the capabilities.
- change the trace color of selected traces, uses partial updates and duplicated outputs
- add annotations to a image displayed with
px.imshow()
, here you should take a look on the data size traveling from client to server depending on the button you click.
First example, change trace colors:
import dash
from dash import Input, Output, html, dcc, Patch
import plotly.graph_objects as go
import numpy as np
# generate some data
TRACES = 7
DATAPOINTS = 10
COLORS = [['darkgreen', 'gold'][c % 2] for c in range(DATAPOINTS)]
# create figure
figure = go.Figure(layout={'width': 1000, 'height': 800})
for idx in range(TRACES):
figure.add_scatter(
x=np.arange(DATAPOINTS),
y=np.arange(DATAPOINTS) + 10*idx,
mode='markers',
marker={'color': 'crimson', 'size': 10},
name=idx
)
app = dash.Dash(__name__)
app.layout = html.Div(
[
html.Div(
[
dcc.Dropdown(id='drop_post', options=[*range(TRACES)]),
dcc.Dropdown(id='drop_post_2', options=[*range(TRACES)]),
],
style={'width': '10%'}
),
html.Div([
dcc.Graph(
id='graph_post',
figure=figure,
)
]),
]
)
@app.callback(
Output('graph_post', 'figure', allow_duplicate=True), # <-- allow component property
Input('drop_post', 'value'), # to be updated from different
prevent_initial_call=True # callbacks
)
def new(i):
# Creating a Patch object
patched_figure = Patch()
# update all marker colors for selected trace index (drop down selection)
patched_figure["data"][i].update({"marker": {'color': 'darkblue', 'size': 20}})
return patched_figure
@app.callback(
Output('graph_post', 'figure'),
Input('drop_post_2', 'value'),
prevent_initial_call=True
)
def update(j):
# Creating a Patch object
patched_figure = Patch()
# update single marker colors with list of colors
patched_figure["data"][j].update({"marker": {'color': COLORS, 'size': 20}})
return patched_figure
if __name__ == '__main__':
app.run(debug=True, port=8051)
Second example, add annotations to figure:
import dash
from dash import Input, Output, html, dcc, State, Patch
import plotly.express as px
import numpy as np
# create quite big color image
img = np.ones(shape=(4000, 6000, 3))
img[:, :, 0] = 173
img[:, :, 1] = 21
img[:, :, 2] = 25
# pre define a shape to add
new_shape = {
'editable': True,
'xref': 'x',
'yref': 'y',
'layer': 'above',
'opacity': 1,
'line':
{
'color': '#fabd00',
'width': 2,
'dash': 'solid'
},
'fillcolor': '#fabd00',
'fillrule': 'evenodd',
'type': 'rect',
'x0': 0,
'y0': 0,
'x1': 3000,
'y1': 4000,
'name': 'default_name'
}
# create figure
figure = px.imshow(img, height=600, width=900)
app = dash.Dash(__name__)
app.layout = html.Div(
[
html.Div(
[
html.Button('< plotly 2.9.0', id='btn_pre'),
html.Button('>= plotly 2.9.0', id='btn_post'),
],
style={'width': '10%'}
),
html.Div([
dcc.Graph(
id='graph_pre',
figure=figure,
style={'display': 'inline-block'}
),
dcc.Graph(
id='graph_post',
figure=figure,
style={'display': 'inline-block'}
)
]),
]
)
@app.callback(
Output('graph_post', 'figure'),
Input('btn_post', 'n_clicks'),
prevent_initial_call=True
)
def new(_):
# Creating a Patch object
patched_figure = Patch()
# add shape to patch
patched_figure["layout"].update({"shapes": [new_shape]})
return patched_figure
# way to add annotations pre dash 2.9.0
@app.callback(
Output('graph_pre', 'figure'),
Input('btn_pre', 'n_clicks'),
State('graph_pre', 'figure'), # <-- state of current figure needed
prevent_initial_call=True
)
def old(_, current_figure):
# add shape to figure
current_figure["layout"].update({"shapes": [new_shape]})
return current_figure
if __name__ == '__main__':
app.run(debug=True, port=8050)
See also the release announcement
mred patch