Multiple outputs

hi!

i’ve got 3 dropdowns that populate a whole bunch of charts. I would like for a title (an html element above each chart, not the title from within the SVG graph element) to display the option that was chosen from each dropdown (the current value) for each of the charts.

The way I initially tried to solve this was to add a callback that returned the 3 values from the dropdowns and just place the div that the callback output to as a title for each of the charts. Of course, I ran into the duplicate id issue.

Does solving this require a hidden div like https://dash.plot.ly/sharing-data-between-callbacks suggests or is it a much simpler problem?

html.Div([
html.div(id='selected-filter-options'),
graph_A_GoesHere
])

html.Div([
html.div(id='selected-filter-options'), ## this breaks it
graph_B_GoesHere
])

@app.callback(Output("selected-filter-options", "children"),
[Input(filter_a, filter_b, filter_c, "value")]
def filter_selections(a,b,c):
       return "{} {} {}".format(a,b,c)

thank you

You cannot have multiple output for your callback. This is a current limitation of Dash. As stated in the tutorials:

Each Dash callback function can only update a single Output property. To update multiple Outputs, just write multiple functions.

Moreover, having multiple elements sharing the same id in intrinsically wrong: IDs are unique identifiers, so it will obviously break in some manner if you don’t respect that. A workaround would have been to do something like @app.callback([Output("selected-filter-options-1", "children"), Output("selected-filter-options-2", "children"),…], but as stated above, having multiple output for a single callback is (currently) not possible.

1 Like

thank you @ebosi for your response. i will create multiple functions

1 Like

but as stated above, having multiple output for a single callback is (currently) not possible.

I got PR’s to enable multi output:

2 Likes

Thanks for doing it — I’ll keep an eye on it!

If you do not want to write multiple function, you can wrap both graphs in a single Div object and have this object be the target of your callback. Inside of that callback you would be able to build both graphs.

I do not know if this is a good solution performance wise however, as you would need to build the full Graph objects rather than just updating their figure property

In case anyone ends up here - multiple outputs are NOW SUPPORTED- see here: 📣 Announcing multiple output support!

3 Likes

I am having the same error. Would be glad is someone can provide me with the solution.
Attaching the code.

app.layout = html.Div([
    html.H1('Smoothie Fixed Benefit Prices'),
    html.P("These prices are common for all routes in India."),
    html.P("Peak months are:-Jan,June, July, August, November, December."),
    html.P("Peak Hours ( During Peak Months ):-9 A.M. to Midnight"),
    html.P("Peak Hours ( During Non Peak Months ):-12 P.M. - 12 A.M."),
    html.H3('Select Airline'),
    dcc.Dropdown(
        id='datasource-1',
        options=[
            {'label': i, 'value': i} for i in ['Low Delay Airlines (6E, UK)','High Delay Airlines (9W,AI,SG,G8)']
        ],
    ),
    html.H3('Select Peak/Non-Peak Month'),
    dcc.Dropdown(
        id='datasource-2',
        options=[
            {'label': i, 'value': i} for i in ['Peak Month','Non Peak Month']
        ]

    ),   
    html.Hr(),
    html.Div('Select Peak/Non Peak Hour and Threshold Delay'),
    html.Div(
        id='controls-container'
    ),
    html.Hr(),
    html.Div('Output'),
    html.Div(
        id='output-container'
    )
])

def generate_control_id(value):
    return 'Control {}'.format(value)

DYNAMIC_CONTROLS = {
    'Low Delay Airlines (6E, UK)': dcc.Dropdown(
        id=generate_control_id('Low Delay Airlines (6E, UK)'),
        options=[{'label': '{}'.format(i), 'value': i} for i in list(price['Hour Peaks'][price['Airlines']=='Low Delay Airlines (6E, UK)'].unique())]
    ),
    'High Delay Airlines (9W,AI,SG,G8)': dcc.Dropdown(
        id=generate_control_id('High Delay Airlines (9W,AI,SG,G8)'),
        options=[{'label': '{}'.format(i), 'value': i} for i in list(price['Hour Peaks'][price['Airlines']=='High Delay Airlines (9W,AI,SG,G8)'].unique())]
    ),
    'Peak Month': dcc.Dropdown(
        id=generate_control_id('Peak Month'),
        options=[{'label': '{}'.format(i), 'value': i} for i in [60,90,120,150,180]]
    ),
    'Non Peak Month': dcc.Dropdown(
        id=generate_control_id('Non Peak Month'),
        options=[{'label': '{}'.format(i), 'value': i} for i in [60,90,120,150,180]]
    )
}

@app.callback(
    Output('controls-container', 'children'),
    [Input('datasource-1', 'value'),
     Input('datasource-2', 'value')])
def display_controls(datasource_1_value, datasource_2_value):
    # generate 2 dynamic controls based off of the datasource selections
    return html.Div([
        DYNAMIC_CONTROLS[datasource_1_value],
        DYNAMIC_CONTROLS[datasource_2_value],
    ])

def generate_output_id(value1, value2):
    return '{} {} container'.format(value1, value2)

@app.callback(
    Output('output-container', 'children'),
    [Input('datasource-1', 'value'),
     Input('datasource-2', 'value')])
def display_controls(datasource_1_value, datasource_2_value):
    # create a unique output container for each pair of dyanmic controls
    return html.Div(id=generate_output_id(
        datasource_1_value,
        datasource_2_value
    ))
def prem(a,b,c,d):
    return (price['Office Premium ( INR )'][(price['Hour Peaks']==a)&(price['Threshold Delay ( Min )']==b)&(price['Airlines']==c)&(price['Month Peaks']==d)])
def incidence(a,b,c,d):
    return (price['Incidence Rate (%)'][(price['Hour Peaks']==a)&(price['Threshold Delay ( Min )']==b)&(price['Airlines']==c)&(price['Month Peaks']==d)])

def generate_output_callback(datasource_1_value, datasource_2_value):
    def output_callback(control_1_value, control_2_value):
        # This function can display different outputs depending on
        # the values of the dynamic controls
        premium=prem(control_1_value,control_2_value,datasource_1_value,datasource_2_value)
        incidence_rate=incidence(control_1_value,control_2_value,datasource_1_value,datasource_2_value)
        return '''
            For the selected values, premium in INR is {} and incidence rate is {} %
        '''.format(
            list(premium)[0],
            list(incidence_rate)[0]
            )
    return output_callback

app.config.supress_callback_exceptions = True

# create a callback for all possible combinations of dynamic controls
# each unique dynamic control pairing is linked to a dynamic output component
for value1, value2 in itertools.product(
        [o['value'] for o in app.layout['datasource-1'].options],
        [o['value'] for o in app.layout['datasource-2'].options]):
    app.callback(
        Output(generate_output_id(value1, value2), 'children'),
        [Input(generate_control_id(value1), 'value'),
         Input(generate_control_id(value2), 'value')])(
        generate_output_callback(value1, value2)
    )

if __name__ == '__main__':
    application.run(port=8080)