Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to update plotly plot, and keep UI settings?

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!

like image 185
FriendFX Avatar answered Sep 09 '25 16:09

FriendFX