Access plotly figure relayout data in notebook

Question:

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.

Asked By: FBruzzesi

||

Answers:

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)
Answered By: vestland