3

I need to be able to add and remove text annotations to a graph and place them by dragging them with the mouse.

I am using python and i am familiar with the image annotation options but i don't know how i would go about making dynamic text annotations

vestland
  • 55,229
  • 37
  • 187
  • 305

1 Answers1

5

Draggable annotations are very much possible with Plotly. But you'll have to unleash Dash, and put your plotly graph object in a dcc.Graph component with the following settings for config:

config={
    'editable': True,
    'edits': {
        'annotationPosition': True
    }

By "dynamic", my understanding is that you'd like it to be possible to insert as many annotations as you want. This is pretty straight forward using a dcc.Input component and a html.Button in a callback. The following app is produced by the complete code snippet below. If this is something you can use, I'd be happy to look more into the user friendliness with regards to how annotations are cleared. Right now, if the input field for annotations is empty, an initial figure fig1 is returned to the dcc.Graph component without any annotations.

Dash app:

enter image description here

Complete code

import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objs as go

import dash_bootstrap_components as dbc
from dash import Dash, html, dcc, Input, Output, State, dash_table, ctx



# some random data
np.random.seed(42)
y = np.random.normal(0, 10, 50)
x = np.arange(0, 50)

app = Dash(external_stylesheets=[dbc.themes.SLATE])

# initial figure for the dcc.Graph component
fig1 = px.scatter(x=x, y=y)

# app layout
app.layout = dbc.Container([dbc.Row([dbc.Col([html.H1("Dynamic and draggable annotations",
                                                      style={"text-align": "center"}, ),
                                              dbc.Label(
                                                  'Annotation input field :   '),
                                              dcc.Input(
                                                  id='input_annotations', type='text', style={'width': '60%'}),
                                              html.Button(id='btn_submit', type='submit', children='Add annotation')])]),
                            dbc.Row([dbc.Col([dcc.Graph(
                                id='graph1',
                                figure=fig1,
                                config={
                                    'editable': True,
                                    'edits': {
                                        'shapePosition': True,
                                        'annotationPosition': True
                                    }
                                }
                            )])])])

# app interactivity
@app.callback(
    Output(component_id='graph1', component_property='figure'),
    Input(component_id='graph1', component_property='figure'),
    State(component_id='input_annotations', component_property='value'),
    Input('btn_submit', 'n_clicks')
)
def update_output_div(figure, annotations, n_clicks):

    # Get existing figure in dcc.Graph with id='graph1'
    # This lets you grap an existing figure with existing annotations
    # and add more annotations to it
    fig = go.Figure(figure)

    # if the input field is empty,
    # an initial figure is returned to id='graph1'
    try:
        print('trey')
        if len(annotations) != 0:
            fig.add_annotation(x=25, y=0,
                               text=annotations,
                               showarrow=True,
                               arrowcolor="black",
                               arrowsize=3,
                               arrowwidth=1,
                               arrowhead=1)

        else:
            return fig1
    except:
        return fig1
    return fig


if __name__ == '__main__':
    app.run_server(debug=True, port='8091')
vestland
  • 55,229
  • 37
  • 187
  • 305
  • 1
    Great solution! We can take this a step further and persist these annotations on page refresh by using dcc.Store. Check out the example here - https://community.plotly.com/t/allowing-users-to-edit-graph-properties-without-code-changes/72031 – Atharva Katre Mar 22 '23 at 06:04