Black Lives Matter. Please consider donating to Black Girls Code today.

Dynamically declaration of dash callback

Basically, I want to add 1-6 live update figures into my page. The actual number is decided by some condition run-time’ly. My current solution is to still declare the callbacks statically, and update the layout dynamically. So the ‘app.config.supress_callback_exceptions’ is needed. Once one of the maximum six graph object is added to the layout, the corresponding callback get fired every update interval. This concept basically works.
My original idea was to call the make_graph_callback as needed so that only callbacks that is needed is declared. That would make it possible to support variant number of graphs in a single page. But I didn’t find a right place to call this function.
Does anybody has a better solution than this?

    app = dash.Dash()
    app.config.supress_callback_exceptions=True
    def make_graph_callback(index):
        @app.callback(Output('live-update-graph'+str(index), 'figure'),
                      events=[Event('interval-component-update-graph', 'interval')])
        def graph_callback():
            ### get data to gdf ###
            return {
                'data': [
                    go.Scatter(
                        name=field,
                        mode='lines',
                        type='scatter',
                        x=gdf['Time'],
                        y=gdf[field],
                        line={'width':1, 'dash': 'solid','shape':'spline','smoothing':\
                        '0','simplify':True})for field in ['A','B','C']
                ],

                'layout': go.Layout(
                    xaxis={'title': 'My Title', \
                        'tickformat': '%m-%d %H:%M:%S',\
                    },
                    yaxis=dict(title='Readings'),
                    margin={'l': 100, 'b': 60, 't': 40, 'r': 100},
                )
            }
            return fig

    def getLayout():
        # get the number of graph to be shown to ngraph
        for i in range(ngraph):
            graphs.append(html.Div(dcc.Graph(id='live-update-graph'+str(i))))

        return html.Div([
            html.Div(graphs,id='graphs'),
            dcc.Interval(
                id='interval-component-update-graph',
                interval=3*1000 # in milliseconds
            ),
            dcc.Interval(
                id='interval-component-update-layout',
                interval=6*1000 # in milliseconds
            )
        ])

    app.layout = getLayout

    make_graph_callback(0)
    make_graph_callback(1)
    make_graph_callback(2)
    make_graph_callback(3)
    make_graph_callback(4)
    make_graph_callback(5)

    @app.callback(Output('graphs', 'children'),
                  events=[Event('interval-component-update-layout', 'interval')],
                  state=[dash.dependencies.State('graphs','children')])
    def update_graph_layout(current_graphs):
        # get the number of graph to be shown to ngraph
        for i in range(ngraph):
            graphs.append(html.Div(dcc.Graph(id='live-update-graph'+str(i))))

        return graphs

    if __name__ == '__main__':
        app.run_server(debug=False, port=8090, host='0.0.0.0',processes=3)

Were you able to find a solution for adding callbacks dynamically?

There have been several community discussions with examples for this. Try searching “dynamic callbacks” (https://community.plotly.com/search?q=dynamic%20callbacks%20category%3A16) or “generate callbacks” (https://community.plotly.com/search?q=generate%20callbacks%20category%3A16)

Thanks, simply using the app.callback function instead of the decorator I could do everything I wanted.

By the way, thank you for this awesome framework :slight_smile:

1 Like

Here is story, that can help you to add callback to newly created graph. I spent 3 days to make it from this dash recipe:

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

app = dash.Dash()

app.layout = html.Div([
html.Button(id='button', n_clicks=0, children='Add graph'),
html.Div(id='container'),
html.Div(dcc.Graph(id='empty', figure={'data': []}), style={'display': 'none'}),
])


app.config['suppress_callback_exceptions']=True


for i in range(10):
    @app.callback(Output('graph_{}'.format(i), 'children'), [
    Input('dropdown_{}'.format(i), 'value'),
    ])
    def graph_update(something):
        return html.H1(something)


@app.callback(Output('container', 'children'), [Input('button', 'n_clicks')])
def display_graphs(n_clicks):
    graphs = []
    for i in range(n_clicks):
        graphs.append(html.Div(children=[
            html.H1("graph_{}".format(i)),
            dcc.Dropdown(
                value=['a'],
                options=[{'label': i, 'value': i} for i in ['a', 'b', 'c', 'd']],
                multi=True,
                id='dropdown_{}'.format(i)
            ),
            html.Div(id="graph_{}".format(i)),
        ]))
    return html.Div(graphs)


if __name__ == '__main__':
    app.run_server(debug=True, host='0.0.0.0')

Hi can you give a minimal example of how you achieved this? I mean without calling:

    make_graph_callback(0)
    make_graph_callback(1)
    make_graph_callback(2)
    make_graph_callback(3)
    make_graph_callback(4)
    make_graph_callback(5)

i.e. without knowing the number of graphs before