I have a simple Dash application with a plot that updates when the 'Update' button is clicked. I've included an editable annotation on this graph. I want to be able to access whatever the user typed into the annotation so when the graph is updated, the annotation stays the same. I'm mostly wondering if there's a way to access how they edited it.
I've been trying to save the current annotation into a storage component which is accessed when the graph is created. I tried making a 'Save' button that changes the storage data to the current annotation text My only guess is that when the annotation is edited, the new text isn't stored in the same place that the default text was. That, or something is just going over my head, and I don't realize it.
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, State, Output
import random
app = dash.Dash(__name__)
app.config['suppress_callback_exceptions'] = True
app.layout = html.Div(
id = 'top_level',
children=[
html.Div(
id='plot_div',
children=[],
),
html.Button(
id = 'update',
children="Update",
),
dcc.Store(
id='annotation_storage',
data='Editable Annotation',
)
]
)
@app.callback(
Output('plot_div', 'children'),
[Input('update', 'n_clicks')],
[State('annotation_storage', 'data')]
)
def plot_update(clicked, annotation):
if clicked:
x = random.sample(range(1,101), 20)
y = random.sample(range(1,101), 20)
figure = {'data': [{'x': x, 'y': y,
'type': 'scatter', 'mode': 'markers'}],
'layout': {
'xaxis': {
'fixedrange': True,
'zeroline': False,
},
'yaxis': {
'fixedrange': True,
'zeroline': False,
},
'annotations': [{
'x': -0.05,
'y': 0.5,
'showarrow':False,
'text':annotation,
'xref':'paper',
'yref':'paper',
}],
}
}
return [dcc.Graph(
id = 'plot_output',
figure = figure,
config = {
'editable': True,
'edits': {
'axisTitleText': False,
'titleText': False,
},
'displayModeBar': False,
},
),
html.Button(
id = 'save_annotation',
children='Save',
),
]
@app.callback(
Output('annotation_storage', 'data'),
[Input('save_annotation', 'n_clicks')],
[State('plot_output', 'figure')]
)
def save_annotation(clicked, figure):
if clicked:
annotation = figure['layout']['annotations'][0]['text']
return annotation
if __name__ == '__main__':
app.run_server(debug=True, port=1000)
Currently the annotation is just reverting to the default text "Editable Annotation" when the graph is updated, even when the user edits it. I want the annotation to remain the same even when the graph is updated. Any insight would be greatly appreciated! Thanks!
As written in the Dash user guide:
The
dcc.Graphcomponent has four attributes that can change through user-interaction:hoverData,clickData,selectedData,relayoutData.
This implies that the figure attribute you use is not updated when the user interacts with the graph.
To fix your problem, update your callback function to use relayoutData instead of figure:
@app.callback(
Output('annotation_storage', 'data'),
[Input('save_annotation', 'n_clicks')],
[State('plot_output', 'relayoutData')]
)
def save_annotation(clicked, relayout_data):
if clicked:
annotation = relayout_data['annotations[0].text']
return annotation
You can run your app in debug mode, and add a break-point inside of the callback function to explore the values given by the four aforementioned attributes. That's what I did to find the key for the annotation text inside of relayout_data.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With