How to update plotly plot, and keep UI settings?

Viewed 498

Following code creates two subplots and updating it each second in browser window.

I can zoom it and hide some lines in plot, but each second all data updates and set zoom and all lines visibility to default

How I can keep settings for zoom and selected lines while updating?

import datetime
import random

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
from plotly.subplots import make_subplots

# https://dash.plotly.com/live-updates

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div(
    html.Div([
        html.H4('Two random plots'),
        dcc.Graph(id='live-update-graph'),
        dcc.Interval(
            id='interval-component',
            interval=1 * 1000,  # in milliseconds
            n_intervals=0
        )
    ])
)

DATA = {
    'time': [],
    'val0': [],
    'val1_1': [],
    'val1_2': []
}


def update_data():
    DATA['val0'].append(random.randint(0, 50))
    DATA['val1_1'].append(random.randint(0, 50))
    DATA['val1_2'].append(random.randint(0, 50))
    DATA['time'].append(datetime.datetime.now())


@app.callback(Output('live-update-graph', 'figure'),
              Input('interval-component', 'n_intervals'))
def update_graph_live(n):
    update_data()

    # Create the graph with subplots
    fig = make_subplots(rows=2, cols=1, vertical_spacing=0.2)
    fig['layout']['margin'] = {'l': 30, 'r': 10, 'b': 30, 't': 10}
    fig['layout']['legend'] = {'x': 0, 'y': 1, 'xanchor': 'left'}

    fig.append_trace({
        'x': DATA['time'],
        'y': DATA['val0'],
        'name': 'val 0',
        'mode': 'lines+markers',
        'type': 'scatter'
    }, 1, 1)

    fig.append_trace({
        'x': DATA['time'],
        'y': DATA['val1_1'],
        'text': DATA['time'],
        'name': 'val 1.1',
        'mode': 'lines+markers',
        'type': 'scatter'
    }, 2, 1)

    fig.append_trace({
        'x': DATA['time'],
        'y': DATA['val1_2'],
        'text': DATA['time'],
        'name': 'val 1.2',
        'mode': 'lines+markers',
        'type': 'scatter'
    }, 2, 1)

    return fig


if __name__ == '__main__':
    app.run_server(debug=True)
1 Answers

Since November 2018, there is a solution as posted on the Plotly Community Forum. The layout property of the figure property of dcc.Graph has a property uirevision, for which the post says:

uirevision is where the magic happens. This key is tracked internally by dcc.Graph, when it changes from one update to the next, it resets all of the user-driven interactions (like zooming, panning, clicking on legend items). If it remains the same, then that user-driven UI state doesn’t change.

So if you never want to reset the zoom settings, no matter how much the underlying data changes, just set uirevision to a constant, like so:

fig['layout']['uirevision'] = 'some-constant'

before returning the figure in your update function.

And there is more:

There are also cases where you want more control. Say the x and y data for a plot can be changed separately. Perhaps x is always time, but y can change between price and volume. You might want to preserve x zoom while resetting y zoom. There are lots of uirevision attributes, that normally all inherit from layout.uirevision but you can set them separately if you want. In this case set a constant xaxis.uirevision = 'time' but let yaxis.revision change between 'price' and 'volume'. Be sure to still set layout.uirevision to preserve other items like trace visibility and modebar buttons!

Related