Problem with the date picker range and the range slider

I am trying to make those two can control each other. For now, only the slider can control the picker. Is there anyway to make them can control each other?

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

app = dash.Dash(__name__)

df = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv")
df = df[df["AAPL.Open"].notna()]

app.layout = html.Div(
    [
        dcc.DatePickerRange(
            id="date-range-picker",
            start_date=df["Date"].min(),
            end_date=df["Date"].max(),
            display_format="MMM D, YYYY",
        ),
        dcc.Graph(id="graph"),
        dcc.RangeSlider(
            id="range-slider",
            min=0,
            max=len(df) - 1,
            value=[0, len(df) - 1],
            marks={i: str(i) for i in range(0, len(df), 50)},
        ),
    ]
)


@app.callback(
    Output("date-range-picker", "start_date"), 
    Output("date-range-picker", "end_date"), 
    Output("range-slider", "value"),
    Input("range-slider", "value"), 
    Input("date-range-picker", "start_date"), 
    Input("date-range-picker", "end_date")
)
def update_date_range(value, start_date, end_date):
    return df.iloc[value[0]]["Date"], df.iloc[value[1]]["Date"], value


@app.callback(Output("graph", "figure"), Input("date-range-picker", "start_date"), Input("date-range-picker", "end_date"))
def update_graph(start_date, end_date):
    filtered_df = df[(df["Date"] >= start_date) & (df["Date"] <= end_date)]
    fig = px.line(filtered_df, x="Date", y="AAPL.Open")
    return fig


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

Hey @xychen, Welcome to the community.

I guess, you are looking for circular callbacks which you can learn from this post:

I also twisted your code a little bit so that you can understand the logic of doing it:


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


app = dash.Dash(__name__)

df = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv")
df = df[df["AAPL.Open"].notna()]

app.layout = html.Div(
    [
        dcc.DatePickerRange(
            id="date-range-picker",
            start_date=df["Date"].min(),
            end_date=df["Date"].max(),
            display_format="MMM D, YYYY",
        ),
        dcc.Graph(id="graph"),
        dcc.RangeSlider(
            id="range-slider",
            min=0,
            max=len(df) - 1,
            value=[0, len(df) - 1],
            marks={i: str(i) for i in range(0, len(df), 50)},
        ),
    ]
)


@app.callback(
    Output("date-range-picker", "start_date"), 
    Output("date-range-picker", "end_date"), 
    Output("range-slider", "value",allow_duplicate=True),
    Input("range-slider", "value"),     
    Input("date-range-picker", "start_date"), 
    Input("date-range-picker", "end_date"),
    prevent_initial_call=True,
)
def update_date_range(value, start_date, end_date):
    print(value)

    return df.iloc[value[0]]["Date"], df.iloc[value[1]]["Date"], value


@app.callback(Output("graph", "figure"),
              Output("range-slider", "value"), 
              Input("date-range-picker", "start_date"), 
              Input("date-range-picker", "end_date"),
              )
def update_graph(start_date, end_date):
    filtered_df = df[(df["Date"] >= start_date) & (df["Date"] <= end_date)]
    fig = px.line(filtered_df, x="Date", y="AAPL.Open")
    first_index =  filtered_df.index[0]
    last_index =   filtered_df.index[-1]


    return fig, [first_index, last_index]


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

Cheers!

2 Likes

Hey Berbere, thanks for your reply. Yes, the circular callback is what I was looking for. The post is really helpful. I got it working!

Hi @Berbere ,

This is valuable information. :star_struck:

Thanks for sharing.