Search code examples
jupyter-notebookdatepickeripywidgets

iPyWidget - DatePicker interact with SelectionRangeSlider problem


I'm trying to interact DatePicker with SelectionRangeSlider widgets. So far I could manage to link the slider with the DatePicker, but I'm getting trouble with the opposite way.

With this code the period slider updates the DatePicker:

from datetime import datetime
import pandas as pd
import ipywidgets as widgets

start_date = datetime(2021, 7, 1)
end_date = datetime(2021, 7, 5)

dates = pd.date_range(start_date, end_date, freq='D')

options = [(date.strftime(' %d %b %Y '), date) for date in dates]

pick_start = widgets.DatePicker(
    description='',
    disabled=False,
    value = start_date
)
pick_end = start_day = widgets.DatePicker(
    description='',
    disabled=False,
    value=end_date
)

selection_range_slider = widgets.SelectionRangeSlider(
    options=options,
    index=(0, len(options) - 1),
    description='Period',
    orientation='horizontal',
    layout=widgets.Layout(width='100%', padding='35px')
)


def update_pick(*args):
    pick_start.value = datetime.strptime(selection_range_slider.value[0].strftime("%Y-%m-%d"), "%Y-%m-%d")
    pick_end.value = datetime.strptime(selection_range_slider.value[1].strftime("%Y-%m-%d"), "%Y-%m-%d")

def update_slider(*args):
    selection_range_slider.value[0] =  pick_start.value.strftime("%Y-%m-%d")
    selection_range_slider.value[1] =  pick_end.value.strftime("%Y-%m-%d")

selection_range_slider.observe(update_pick, 'value')
pick_start.observe(update_slider, 'value')
pick_end.observe(update_slider, 'value')

center_layout = widgets.Layout(display='flex',
                                       align_items='center',
                                       width='100%')

day = widgets.HBox(children=[pick_start, selection_range_slider, pick_end], layout=center_layout)

display(day)

Also try with this function without result:

def update_slider(*args):
    start_date = datetime(pick_start.value.year, pick_start.value.month, pick_start.value.day)
    end_date = datetime(pick_end.value.year, pick_end.value.month, pick_end.value.day)

    dates = pd.date_range(start_date, end_date, freq='D')

    options = [(date.strftime(' %d %b %Y '), date) for date in dates]
    
    selection_range_slider.value[0] =  options[0]
    selection_range_slider.value[1] =  options[len(options)-1]

Need help interacting DatePicker with SelectionRangeSlider

Thanks!


Solution

  • you have done a nice job of creating a standalone question here, thanks. A couple suggestions for your next Q to speed up an answer.

    1. Please include your imports, I can't run your code without them:
    from datetime import datetime
    import pandas as pd
    import ipywidgets as widgets
    
    1. Please post any error messages that you see when your code runs. I got a ValueError which would have improved the question even more.

    The error said there was an issue assigning to an index of a tuple, Here's my solution.

    • Assign the whole tuple directly, you cannot update index 0 and then index 1 of your selection_range_slider

    • Use raw datetimes to pass between widgets, your mixed string conversions were causing some issues. You can still change how the dates are displayed in the slider by using a dictionary for options.

    from datetime import datetime
    import pandas as pd
    import ipywidgets as widgets
    
    start_date = datetime(2021, 7, 1)
    end_date = datetime(2021, 7, 5)
    
    dates = pd.date_range(start_date, end_date, freq='D')
    
    options = {date.strftime(' %d %b %Y '): date for date in dates}
    
    pick_start = widgets.DatePicker(
        description='',
        disabled=False,
        value = start_date
    )
    pick_end = start_day = widgets.DatePicker(
        description='',
        disabled=False,
        value=end_date
    )
    
    selection_range_slider = widgets.SelectionRangeSlider(
        options=options,
        index=(0, len(options) - 1),
        description='Period',
        orientation='horizontal',
        layout=widgets.Layout(width='100%', padding='35px')
    )
    
    
    def update_pick(*args):
        pick_start.value = selection_range_slider.value[0]
        pick_end.value = selection_range_slider.value[1]
    
    def update_slider(*args):
        selection_range_slider.value =  (pick_start.value, pick_end.value)
    
    selection_range_slider.observe(update_pick, 'value')
    pick_start.observe(update_slider, 'value')
    pick_end.observe(update_slider, 'value')
    
    center_layout = widgets.Layout(display='flex',
                                           align_items='center',
                                           width='100%')
    
    day = widgets.HBox(children=[pick_start, selection_range_slider, pick_end], layout=center_layout)
    
    display(day)