Python – Dash dynamic slider

Dash dynamic slider… here is a solution to the problem.

Dash dynamic slider

Streamlining question:

I want slider indexed by dropdown. So, in the dropdown list, I will have A, B, which will result in a slider that adjusts the A value or a slider that adjusts the B value. At any given time, I want to have a bar chart showing the values of A and B.

Here’s the code to do this, except for one problem: if I adjust A’s value from its default value, switch the dropdown to B, and then switch it back to A, A will reset back to its default value.

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State

app = dash. Dash(__name__)

app.layout = html. Div([

dcc.Dropdown(id='segselect', options = [{'label': 'A', 'value': 'A'}, 
        {'label': 'B', 'value': 'B'}]),

.html. Div(id='SliderAContainer'),
    .html. Div(id='SliderBContainer'),
    dcc.Graph(id='plot_graph')

])

app.config['suppress_callback_exceptions']=True

@app.callback(Output('SliderAContainer', 'children'),
    [Input('segselect', 'value')])
def return_containerA(seg):
    if seg == 'A':
        return html. Div(dcc.Slider(id='A', min = 0, max = 10, step = 1))
    else:
        return html. Div(dcc.Slider(id='A', min = 0, max = 10, step = 1), style={'display': 'none'})

@app.callback(Output('SliderBContainer', 'children'),
    [Input('segselect', 'value')])
def return_containerB(seg):
    if seg == 'B':
        return html. Div(dcc.Slider(id='B', min = 0, max = 10, step = 1, value = 2))
    else:
        return html. Div(dcc.Slider(id='B', min = 0, max = 10, step = 1, value = 2), style={'display': 'none'})

@app.callback(
    Output('plot_graph', 'figure'),
    [Input('A', 'value'), Input('B', 'value')])
def plot_A(A, B):
    return {
            'data': [
                {'y': [A, B], 'type': 'bar'},
            ],
        }

if __name__ == '__main__':
    app.run_server(debug=True, port=8041, dev_tools_hot_reload=False)

How do I make A not reset after switching the menu to B and back without triggering a circular dependency?


Original:
It’s a bit complicated to set up, so let’s look at an example.

Let’s say I’m modeling a tax bill. I have a database of citizens that lists their state, marital status, and income.

I want to create a slider that allows me to adjust my tax rates based on marital status and state, and then calculate the average tax bill.

There were too many sliders on the screen at once, so I set up a drop-down menu that lets us decide to adjust by state or marital status, and then a second drop-down menu that lets us select a specific status or a specific marital status. So there are two drop-down menus:

  1. Select (State or Marital Status).
  2. Select a specific option, for example. Texas or married (dynamic response first drop-down menu).

With two drop-down menus selected, I want to place a slider on the screen to adjust the tax rate for that state or marital status

The slider is where I’m stuck. I need them to provide three special features:

    If we adjust the tax rate for a specific

  1. state, say Florida +5%, and then +4% for a specific marital status, such as married, then I want the tax rate increased by +9% for married Floridians.
  2. If I set the tax rate for Florida and then switch the drop-down menu back from Florida, then I want the slider to be at the tax rate selected for Florida.
  3. I want marital status options and status options to be dynamically generated from the uploaded database.

So far, I have tried two methods.

Method 1: Create a dictionary of sliders — one per state, one per marital status — that place all sliders in the layout, but none of the sliders are visible except the one selected from the drop-down menu. This breaks because I need to calculate the average tax bill, so create a callback with all possible hikes as input. However, the callback only works when all functions are rendered, which breaks.

Method 2: Create a tax rate dictionary and a slider with a dynamic name. Then I can create a dynamic dictionary with all states and marital status, which updates based on the selected drop-down list and slider. However, in order to update without clearing the previously selected value, this dynamic dictionary needs to be created by a callback that treats itself as state, and Dash does not allow circular dependencies, even for state!

I’d be happy to post the code, but it would be very long, so I chose to describe what I tried above.

Solution

Looking at your code, I understand that you don’t mind simply hiding the slider. The problem is that you use the container’s children property, which “recreates” the slider every time it changes, effectively resetting them. My suggestion is to change the style property of the container directly:

app.layout = html. Div([

dcc.Dropdown(id='segselect', options=[{'label': 'A', 'value': 'A'},
                                          {'label': 'B', 'value': 'B'}]),

.html. Div(dcc.Slider(id='A', min=0, max=10, step=1), id='SliderAContainer'),
    .html. Div(dcc.Slider(id='B', min=0, max=10, step=1, value=2), id='SliderBContainer'),
    dcc.Graph(id='plot_graph')

])   

@app.callback(Output('SliderAContainer', 'style'),
              [Input('segselect', 'value')])
def return_containerA(seg):
    if seg == 'A':
        return {}
    else:
        return {'display': 'none'}

@app.callback(Output('SliderBContainer', 'style'),
              [Input('segselect', 'value')])
def return_containerB(seg):
    if seg == 'B':
        return {}
    else:
        return {'display': 'none'}

Note that I had to add sliders as child sliders in the layout settings because they are no longer generated by callback functions.

Also, note that you no longer need the suppress_callback_exceptions configuration because sliders always exist in the DOM. But it doesn’t hurt to leave it.

Related Problems and Solutions