dash-core-components icon indicating copy to clipboard operation
dash-core-components copied to clipboard

Config attribute of dcc.Graph does not respond to callbacks (is read-only)

Open ndrezn opened this issue 4 years ago • 1 comments

When using a callback to control the modebar settings on a chart through the config property of a dcc.Graph, the graph does not update but retains the initial setting.

Note: I have not (yet) tested with other config settings.

Minimal example

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import plotly.express as px

app = dash.Dash()
server = app.server

app.layout = html.Div(
    [
        dcc.Graph(
            id="line-graph",
            figure=px.line(x=[1, 2, 3], y=[1, 2, 3]),
        ),
        dcc.RadioItems(
            options=[
                {"label": "Show modebar", "value": 1},
                {"label": "Hide modebar", "value": 0},
            ],
            value=1, # Note that if this value is set to 0 then the modebar will stay turned off rather than turned on.
            id="radio",
        ),
        html.Div([html.H3("Current config"), html.P(id="cur-config")]),
    ]
)


@app.callback(
    [Output("line-graph", "config"), Output("cur-config", "children")],
    [Input("radio", "value")],
)
def toggle_modebar(i):
    vals = [False, True]
    config = {"displayModeBar": vals[i]}
    return config, str(config)


if __name__ == "__main__":
    app.run_server(debug=True, port=8050)

Screen recording

https://user-images.githubusercontent.com/38958867/107691811-edb77d00-6c79-11eb-8488-c750b635f815.mov

ndrezn avatar Feb 11 '21 20:02 ndrezn

One workaround for this is generating a new dcc.Graph in a callback with the same id and using the state of the existing figure to prevent needing to regenerate it.

Screen recording of this (and the expected behaviour)

https://user-images.githubusercontent.com/38958867/107695876-2f96f200-6c7f-11eb-880a-e4c9459a8ca1.mov

Code

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import plotly.express as px

app = dash.Dash()
server = app.server

app.layout = html.Div(
    [
        html.Div(
            dcc.Graph(id="line-graph"),
            id="graph",
        ),
        dcc.RadioItems(
            options=[
                {"label": "Show modebar", "value": 1},
                {"label": "Hide modebar", "value": 0},
            ],
            value=1,
            id="radio",
        ),
        dcc.RadioItems(
            options=[
                {"label": "Bar", "value": 1},
                {"label": "Line", "value": 0},
            ],
            value=1,
            id="fig-type",
        ),
    ]
)


@app.callback(Output("line-graph", "figure"), [Input("fig-type", "value")])
def create_fig(i):
    figures = [px.line(x=[1, 2, 3], y=[1, 2, 3]), px.bar(x=[1, 2, 3], y=[1, 2, 3])]
    return figures[i]


@app.callback(
    Output("graph", "children"),
    [Input("radio", "value")],
    [State("line-graph", "figure")],
)
def toggle_modebar(i, fig):
    vals = [False, True]
    config = {"displayModeBar": vals[i]}

    return dcc.Graph(id="line-graph", figure=fig if fig else {}, config=config)


if __name__ == "__main__":
    app.run_server(debug=True, port=8050)

ndrezn avatar Feb 11 '21 20:02 ndrezn