Change where x axis begins in chart while Dash is running

I have a simplified version of my program below. Is there a way to change where the x axis begins in the chart while Dash is running? The x axis in the current program starts at 09:31:00, but while the program is running I would like to change the start of the x axis to e.g. 09:32:00. How can I do that?

I know that I can zoom by drawing a box in the Dash chart with the mouse, but then if I add new time series data for e.g. 09:36:00 then those values wont be shown if I have zoomed in. Therefore I just want to stay zoomed out and only change the start of the x axis if possible with e.g. a button or something.

import dash
from dash.dependencies import Output, Input
from dash import dcc
from dash import html
import plotly.graph_objects as go

x_time=["09:31:00", "09:32:00", "09:33:00", "09:34:00", "09:35:00"]
y1=[6.385030,6.468024,6.526419,6.640735,6.722588]
y2=[6.293187,6.341455,6.381316,6.449572,6.506755]

app = dash.Dash(__name__)
app.layout = html.Div(
    [
        dcc.Graph(style={'height': '100vh'}, id = 'live-graph', animate = False),
        dcc.Interval(
            id = 'graph-update',
            interval = 1000,
            n_intervals = 0
        ),
    ]
)

@app.callback(Output('live-graph', 'figure'),
              Input('graph-update', 'n_intervals'))
def update_graph_scatter(n):
    fig = go.Figure(go.Scatter(x=x_time, y=y1, mode='lines', name='y1', line=dict(color="#ff0000")))
    fig.add_trace(go.Scatter(x=x_time,   y=y2, mode='lines', name='y2', line=dict(color="#00ff00")))
    fig.update_layout(uirevision=True) 
    return fig

if __name__ == '__main__':
    app.run_server()

Hi,

If you want to change it as new data is added via the callback, you can just update the xaxis_range via:

fig.update_layout(xaxis_range=["09:32:00.", "09:36:00"])

Please also be mindful that uirevision=True makes possible to preserve the zoom state after callback updates, which seems to be the opposite of what you want as behavior.

I like using uirevision=True because if not then when clicking e.g. the y2 legend to the right of the chart to have the y2 line disappear from the chart then it would come back on the chart after one second which I don’t want.

If I am using fig.update_layout(xaxis_range=[start,end]), how can I change the value of “start” when the program is running through some interface? Sometimes while the program is running I might want to see how the chart looks like when start=“09:33:00” and at a later point I want to go back to see the chart from start=“09:31:00” again. Can I add a field to the chart where I can enter the start value (e.g. “09:32:00”)?

There are multiple ways to specify the layout, depending on when you need to do it.

Sometimes while the program is running I might want to see how the chart looks like when start=“09:33:00” and at a later point I want to go back to see the chart from start=“09:31:00” again. Can I add a field to the chart where I can enter the start value (e.g. “09:32:00”)?

It looks like you want the user to be able to set the start of the x range. I would do it with two daq.NumericInput (see here), one for hour and other for minute. Then just set a callback to update the layout if the values in these components are updated.

On the other hand, you don’t want to hardcode the selected value in the layout if you want the range to update when the user starts the page (say, you want to see the past 30 minutes from when the page is opened/reloaded)… To do so, the easiest alternative is to generate the NumericInput components via a callback that is triggered by a “dummy” component modified just at startup. Then you can return the component with whatever value you want calculated during page load.

I would be happy to provide you an example if you want to go on this direction.

Yes, if you could provide an example of how to extend the code that would be great, thanks!

1 Like

Ok, this might be a bit longer than I expected as example, but I hope it will make my point clear…

from dash import Dash, html, Input, Output
import dash_daq as daq
import datetime as dt

app = Dash(__name__)

def make_time_picker(id_prefix):
    
    now = dt.datetime.now()
    
    return html.Div(
        [
            daq.NumericInput(
                id=f"{id_prefix}-hour",
                min=0,
                max=23,
                value=now.hour
            ),
            daq.NumericInput(
                id=f"{id_prefix}-min",
                min=0,
                max=59,
                value=now.minute
            )
        ],
        style={"display": "flex"}
    )



app.layout = html.Div(
    [
        html.Div(
            [
                html.H2("This one is fixed"),
                make_time_picker("fixed"),
            ]
        ),
        html.Div(
            id="time-picker-container",
            children=[
                html.H2("This one is dynamic")
            ]
        ),
        html.Div(id="dummy-div")      
    ]
)


@app.callback(
    Output("time-picker-container", "children"),
    Input("dummy-div", "children")
)
def update_time_picker(dummy):
    # dummy is used just to trigger the callback 
    # on page load...

    # now will be re-evaluated every time the page
    # reloads, instead of when app.layout is evaluated
    return [
        html.H2("This one is dynamic"),
        make_time_picker("dynamic")
    ]

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

The thing to notice is that the first component will have as initial value the time the app process started, while the second will be updated every time the browser is reloaded (so it “follows” the time).

This is crucial in any app deployed in a server, as the process starts and persists (in many cases) for days… So it is actually relevant for dates as well.

I haven’t added the part where you use the selectors to actually update the chart, but I imagine it should be pretty straightforward to do. If not, just ping me and I would be very happy to help you further.

Hope it clarifies my point!