Stripped down problem:
I'd like to have sliders that are indexed by a dropdown. So on a dropdown I'll have A, B, and that would lead to either a slider that adjusts the value of A or a slider that adjusts the value of B. At all times, I'd like to have a bar plot that shows the values of A and B.
Below is code that does this except for one problem: if I adjust the value of A from its default, switch the dropdown to B, and then switch it back to A, A resets 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 can I make it so that A does not reset after switching the menu to B and back, without triggering a circular dependency?
Original: It's a bit of a complicated set up, so let's look at an example.
Say that I am modeling tax bills. I have a database of citizens that lists their state, marital status, and income.
I want to create sliders that allow me to adjust the tax rate by both marital status and state, and then compute the average tax bill.
There are too many sliders to have on the screen at once, so I set a dropdown menu that lets us decide to adjust by state or by marital status, and then a second dropdown that lets us select a particular state or a particular marital status. So there are two dropdowns:
- Choose (State or Marital Status)
- Choose a particular option, eg. Texas or Married (responds dynamically to the first dropdown)
Having chosen the two dropdowns, I want to place a slider on the screen that adjusts the tax rate for that state or marital status. .
The sliders are where I'm stuck. Three particular features I need from them:
- If we adjust the tax rate in a particular state, say Florida by +5%, and then for a particular marital status, say married, by +4%, Then I want married Floridians to have a +9% tax increase. This means that I need to store the tax rates for Florida, even when the dynamically generated slider for Florida is not on the screen.
- If I set the tax rate for Florida, then switch the dropdowns away from Florida and then come back, then I would like the slider to be at the chosen tax rate for Florida.
- I want the options for marital status and the options for states to be dynamically generated from the uploaded database.
I've tried two approaches so far.
Approach 1: Create a dict of sliders---one for each state and one for each marital status---put all in layout, but make all but the one chosen by the dropdowns invisible. This breaks because I need to calculate the average tax bill, and so create a callback which takes all possible rate increases as inputs. However, callbacks only work when all functions are rendered, and this breaks.
Approach 2: Create a dict of tax rates, and just one slider with a dynamic name. Then I can create a dynamic dict of all states and marital statuses, that updates based on the selected dropdowns and slider. However, in order to update without wiping out previously selected values, this dynamic dict would need to be created by a callback which takes itself as a state, and Dash does not allow circular dependencies, even for state!
I'm happy to post code, but it will be super long, so I opted to describe the approaches I've tried above.