pythonplotlyjupyterplotly-python

Access plotly figure relayout data in notebook


Is there a way to access a figure relayout data in jupyter notebook/lab?

What I mean is the following:

import plotly.graph_objects as go

# Define the figure
fig = (go.Figure(
    go.Scatter(
        x=[0, 1, 2, 3],
        y=[-1, 1, -2, 2]
       )
    )
    .update_layout(
        modebar_add=['drawrect']
    )
)

# Show the figure
fig.show()
# Now do some drawing in the figure here! 

# Next cell I would like to access the shapes

I know this is possible using dash callbacks as in here.


Solution

  • You can do this fairly easily if you're willing to use Plotly Dash in Jupyterlab with JupyterDash and the mode attribute of app.run_server() set to 'inline'.

    Below is an image of the resulting notebook with full information of a drawn line in the second cell. The complete setup with your figure is included below, and builds on one of the many examples from Image annotations with Dash.

    If this is something you can use, I'd be happy to explain things in more detail.

    enter image description here

    Complete code:

    import dash
    import dash_bootstrap_components as dbc
    import dash_core_components as dcc
    from dash.dependencies import Input, Output, State, ClientsideFunction
    import dash_html_components as html
    from jupyter_dash import JupyterDash
    import plotly.express as px
    import plotly.graph_objects as go
    import json
    
    app = JupyterDash(external_stylesheets=[dbc.themes.BOOTSTRAP])
    
    fig = (go.Figure(
        go.Scatter(
            x=[0, 1, 2, 3],
            y=[-1, 1, -2, 2]
           )
        ))
    
    fig.update_layout(dragmode="drawline")
    config = {
        "modeBarButtonsToAdd": [
            "drawline",
            "drawopenpath",
            "drawclosedpath",
            "drawcircle",
            "drawrect",
            "eraseshape",
        ]
    }
    
    app.layout = html.Div(
        [
            html.H4(
                "Draw a line"
            ),
            dcc.Graph(id="graph1", figure=fig, config=config),
            # dcc.Markdown("Characteristics of shapes"),
            html.Pre(id="annotations-data-pre"),
        ]
    )
    
    @app.callback(
        Output("annotations-data-pre", "children"),
        Input("graph1", "relayoutData"),
        prevent_initial_call=True,
    )
    def on_new_annotation(relayout_data):
        if "shapes" in relayout_data:
            global relayoutdata
            relayoutdata = json.dumps(relayout_data["shapes"], indent=2)
            return ''
        else:
            return dash.no_update
    
    app.run_server(mode='inline', port=8002)