Search code examples
pythongraphplotlyplotly-dash

How to capture the data of the drawn shapes by the mouse in Dash


So I have this simple python dash application in which I've laid out a graph and a button. My goal is this: when I press the button, I want to retrieve the shapes that have been drawn.

import plotly.graph_objects as go
import dash
from dash import html, dcc, Input, Output, State

app = dash.Dash(__name__)
fig = go.Figure()

app.layout = html.Div([
                        dcc.Graph(id = "graph-pic", className="graph-pic", figure=fig, config={'modeBarButtonsToAdd':['drawrect', 'eraseshape']}),
                        html.Button("Shape count", id = "shape-count-button")
                    ])
        
fig.add_shape(editable=True, x0=-1, x1=0, y0=2, y1=3, xref='x', yref='y')

@app.callback(
    Output("graph-pic", "figure"),
    Input("shape-count-button", "n_clicks")
)
def on_shape_count_button_pressed(n_clicks):
    trigger_id = dash.callback_context.triggered_id

    if trigger_id == "shape-count-button":
        print("Shape count: " + str(len(fig.layout.shapes)))
        print(fig.layout.shapes)
    
    return dash.no_update

if __name__ == "__main__":
    app.run_server()

Dash app When I press the button, it only prints the first shape that I've added through code... and NOT the ones that I've drawn on the graph with the draw rectangle tool.

Output:

Shape count: 1
(layout.Shape({
    'editable': True, 'x0': -1, 'x1': 0, 'xref': 'x', 'y0': 2, 'y1': 3, 'yref': 'y'
}),)

Any hint would be appreciated!


Solution

  • You should use relayout_data to detect all the drawn shapes on the graph, and then you can parse the desired data as you would:

    import dash
    import json
    import plotly.graph_objects as go
    from dash import html, dcc, Input, Output, State
    
    app = dash.Dash(__name__)
    fig = go.Figure()
    
    app.layout = html.Div([
                            dcc.Graph(id = "graph-pic", className="graph-pic", figure=fig, config={'modeBarButtonsToAdd':['drawrect', 'eraseshape']}),
                            html.Button("Shape count", id = "shape-count-button"),
                            html.Div(id="text")
                        ])
            
    fig.add_shape(editable=True, x0=-1, x1=0, y0=2, y1=3, xref='x', yref='y')
    
    @app.callback(
        Output("text", "children"),
        Input("shape-count-button", "n_clicks"),
        Input("graph-pic", "relayoutData"),
    )
    def on_shape_count_button_pressed(n_clicks, relayout_data):
        
        trigger_id = dash.callback_context.triggered_id
    
        if trigger_id == "shape-count-button":
            text_lst = "Shape count: " + str(len(fig.layout.shapes))
            text_lst += str(fig.layout.shapes)
            
            if "shapes" in relayout_data:
                text_lst += json.dumps(relayout_data["shapes"], indent=2)
                
            return text_lst
        
        return dash.no_update
    
    if __name__ == "__main__":
        app.run_server()
    

    Output enter image description here