Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

figure as state out of sync #879

Closed
nicolaskruchten opened this issue Nov 5, 2020 · 10 comments · Fixed by #905 or mstoelzle/solving-occlusion#28
Closed

figure as state out of sync #879

nicolaskruchten opened this issue Nov 5, 2020 · 10 comments · Fixed by #905 or mstoelzle/solving-occlusion#28

Comments

@nicolaskruchten
Copy link
Member

nicolaskruchten commented Nov 5, 2020

In this simple app from @emmanuelle, figure.layout.shapes doesn't seem to be updated when figure is captured as State, meaning that we have to do all sorts of gymnastics with relayoutData to work with shapes.

@alexcjohnson can you help me figure out if this is something in dcc or if Plotly.js is doing something odd here, in which case help me translate this into a CodePen that @archmoj can use?

import dash_html_components as html
import dash_core_components as dcc
import plotly.graph_objects as go
from dash.dependencies import Input, Output, State
import dash
import json

app = dash.Dash(__name__)

fig = go.Figure().update_layout(dragmode='drawrect')

app.layout = html.Div([
    dcc.Graph(id='graph', figure=fig),
    html.Pre(id='annotations-data'),
])

@app.callback(
    Output('annotations-data', 'children'),
    [Input("graph", "relayoutData")],
    [State("graph", "figure")]
)
def on_new_annotation(relayout_data, fig):
    return json.dumps(go.Figure(fig).layout.shapes, indent=2)

app.run_server(debug=True)
@alexcjohnson
Copy link
Contributor

This is in DCC. The issue is that we clone the layout for responsive sizing

const layoutClone = this.getLayout(figure.layout, responsive);

Short term I believe you can work around it by providing an empty shapes array in the initial figure, as the cloning process only goes as deep as needed to set the responsive attributes.

I wonder though whether we could get away with mutating the layout for these responsive tweaks, rather than cloning it. Given that we're expecting it to be mutated by the plotting process anyway, perhaps this is actually the better way to do it?

Note that this problem is magnified in DDK, as it clones much more deeply in order to theme its graphs.

@nicolaskruchten
Copy link
Member Author

Setting shapes=[] does not fix this, unfortunately.

@alexcjohnson
Copy link
Contributor

Ah, because adding shapes does not mutate layout.shapes, it replaces the array. Bummer, OK. We could alter the relayout call inside plotly.js so that it's just adding the new shape, but the array modification form of relayout is tricky and unpleasant for users. I suggest we try to mutate instead of clone here, and see if this has any adverse consequences.

@nicolaskruchten
Copy link
Member Author

I suggest we try to mutate instead of clone here, and see if this has any adverse consequences.

Where "here" is in the dash-core-components repo?

@emmanuelle
Copy link
Contributor

I think @alexcjohnson means "here" = when adding a new shape (mutate the shapes object instead of cloning it).

@alexcjohnson
Copy link
Contributor

No, I meant in DCC. There may be other places this causes problems in plotly.js, but if we fix it on the DCC side it will cover all these bases.

@alexcjohnson
Copy link
Contributor

Basically just take the method call I linked above and turn it into mutating the layout instead of making a new layoutClone.

@emmanuelle
Copy link
Contributor

Pasting here a comment by Alex on how to test this (in https://github.com/plotly/dash-core-components/tree/dev/tests/integration/graph)

I bet you can just run a relayout command corresponding to the conditions you’re most interested in. Something like dash_dcc.driver.execute_script("Plotly.relayout(document.querySelector('#my-graph'), {shapes: […]})")

@emmanuelle
Copy link
Contributor

@almarklein, @nicolaskruchten mentioned that #584 targets the same piece of the dcc code so that it could be interesting to target the two issues together.

@dellacortelab
Copy link

I am running into related issues with a callback that Outputs a figure and loads the figure from a State.
Many of deeper attributes, like line_colors of traces or _grid_ref are set to None in the figure dictionary received from State.

I had to use various hacks, like creating empty figures with subplot grids or storing color information in store objects and adding them back in annoying for-loops over the traces.

Is it somehow possible to return a "deep dictionary" of a figure that retains attributes currently lost?

I am using Dash 2.5.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants