Is it possible to update just `layout`, not whole `figure` of Graph in callback?

I am also interested in what will be the way forward here. Right now in my app i have a figure where I am animating a moving vertical line along the time axis. I am returning the entire figure each time, which works ok as long as the figure stays simple. However as figure get more complex the animation delay gets longer and longer.

Also mydcc.relayout works well for a simple example, but when I tried to use it in my case i get into some sort of infinite loop of callbacks, because I also i have callbacks that listen to chart relayout data and then update the chart to zoom in/out. Which leaves me not sure if the issue is with my callbacks or with mydcc.relayout and I don’t know to debug it.

this also seems ideal to me:

Allow the developer to update nested properties in callbacks with something like
@app.callback(Output(‘my-graph’, ‘figure.layout.range’))

For now it seems like I am stuck with “returning the entire figure each time”, until solution 1 or 2 is created (not soon?) or I learn more React/JS and custom components and can make some sort of custom hack.

1 Like

Is there any update on this issue?

Your first soluction would be perfect.

Thanks!

2 Likes

No updates. This is actually quite a bit project and we’d likely need commercial sponsorship of the feature in order to move it forward

Sorry I can not help with that. I don´t belong to any company.

1 Like

I believe this is now possible to some extent thanks to State callback.

@app.callback(Output('graph', 'figure'),
             [Input('graph', 'relayoutData')], # this triggers the event
             [State('graph', 'figure')])
def graph_event(select_data,  fig):
    fig['layout'] = [YOUR STUFF]
    return fig
    
3 Likes

This is sorely needed for large plots e.g. choropleths that have megabytes of data to be overlaid on the map, which at the present need to be sent repeatedly when a layout change such as zoom level is made to the figure via callback.

After facing a similar issue it seems like client side callbacks are the best way to step around this issue!

1 Like

Would love to see your solution @sjtrny

Simplified Example

app.py

import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output, ClientsideFunction

app = dash.Dash(__name__)

fig_data = {
    "data": [{"type": "bar", "x": [1, 2, 3], "y": [1, 3, 2]}],
    "layout": {"title": {"text": ""}}
}

app.layout = html.Div([
    dcc.Store("fig-data", data=fig_data),
    dcc.Dropdown(
        id="city",
        options=[ {'label': z, "value": z} for z in ["Sydney", "Montreal"] ],
        value="Sydney"
    ),
    dcc.Graph(id="graph")
])

app.clientside_callback(
    ClientsideFunction("clientside", "figure"),
    Output(component_id="graph", component_property="figure"),
    [Input("fig-data", "data"), Input("city", "value")],
)

app.run_server(host="0.0.0.0", debug=True)

/assets/clientside.js

if (!window.dash_clientside) {
     window.dash_clientside = {}
 }

window.dash_clientside.clientside = {

    figure: function (fig_dict, title) {

        if (!fig_dict) {
            throw "Figure data not loaded, aborting update."
        }

        // Copy the fig_data so we can modify it
        // Is this required? Not sure if fig_data is passed by reference or value
        fig_dict_copy = {...fig_dict};

        fig_dict_copy["layout"]["title"] = title;

        return fig_dict_copy

    },

}

Choropleth Example

This example is a bit more involved so I put it in a repl.it

1 Like

I am still new to Dash Plotly. I do not know any Javascript. The solution proposed by tracek works (without external script).

This is what I have in my script. I can update the title of the graph. Hopefully this can help anyone who is struggling with this aspect.

@app.callback(
    Output(component_id='my-div', component_property='figure'),
    [Input(component_id='variable', component_property='value')],
    [State('my-div', 'figure')]
)
def update_output_div(input_value, fig):
    fig['layout'] = {"title": input_value}
    return fig
4 Likes

Hi,
I’m wondering how @tracek solution improve performance compared to reloading at each call back. Does changing the state only reload the changed attribute or it reloads the whole figure?
Thank you

1 Like

Any Update here ? I’m trying to update the annotation text of my Graph , is that possible?

2 Likes

The best workaround right now is to use a clientside callback to add or change the smaller pieces of the figure. See the example here: https://dash.plotly.com/clientside-callbacks

1 Like

Just a minor addition to your great example: You have to make a deep copy of the figure object. Otherwise you might run into issues when adding elements to an existing list, e.g. such as traces.

I was able to completely decouple any figure modification (e.g. adding descriptive shapes, traces, annotations etc.) while keeping the original (large) figure data in a dcc.Store object and not resend it on every update (of the auxiliary data).

1 Like

Would you mind sharing code on your example? I am facing a similar issue.

1 Like

Yes, can you show us your example, please?

I’m trying to make a choropleth with ~40k polygons that gets recolored depending on the value of a dropdown menu.
I want to the menu choice to trigger a server-side recomputation of the choropleth values, colors, and legend, then send only that info to the client and not the large amount of geodata again.
Any suggestions?

I haven’t tied with Plotly Choropleth, but it should be possible with the GeJSON component in Dash leaflet. If you color the polygons based on data from the hideout prop (via a custom JS function), you can update this prop only without changing the data prop (which holds the polygons).

Hi @chriddyp , I also wanted to update plots in a Dash applications, and I was tempted to use figure updates as documented at Creating and Updating Figures | Python | Plotly. Do we agree that at the moment I can’t use that in the context of a dash application? Thanks!

I think it depends on what you want to update. Can you elaborate on what you are trying to do, e.g. what properties you are modifying?