'Dash callback duplicate issue - Python

I have a dashboard built with dash-plotly of this structure:

app.layout = html.Div(
    children=[
        html.Div(children=[
                     html.Div(children=[dcc.Dropdown(options=[
                                              {'label': 'Viability', 'value':'fig_viability'},
                                              {'label': 'Mean Diameter', 'value': 'fig_diameter'},
                                              {'label': 'Concentration', 'value': 'fig_concentration'}],
                                              id='graph-selector')]]),

                     html.Div(children=[
                                  dcc.Graph(id='graph'),
                                  dcc.Interval(
                                      id='interval-component',
                                      interval=15 * 1000,  # in milliseconds
                                      n_intervals=0)])])])   

I am trying to make an app displaying 3 different graphs based on the dropdown choice. The thing is that the excel files which serve as a source of data will be changed every couple of minutes. I tried to use "Interval" component to make this live graph update but I think I messed up somewhere in callbacks, as it throws me an error:

"In the callback for output(s): graph.figure Output 0 (graph.figure) is already in use. Any given output can only have one callback that sets it."

My callbacks and excel file manipulations are below:

@app.callback(Output('graph', 'children'),
                  [Input('interval-component', 'n_intervals')])
    def update_figures(n):
        
    df = pd.read_excel(...)

    fig_viability = px.scatter(df,
                            x="Sample Name",
                            y="Result",
                            color="Sample Name",
                            template='plotly_dark').update_layout(
     {'title': 'Viability Plot (%)',
      'modebar_add': ['v1hovermode', 'toggleSpikelines'],
      'plot_bgcolor': 'rgba(0, 0, 0, 0)',
      'paper_bgcolor': 'rgba(0, 0, 0, 255)'})

      fig_diameter = px.scatter(df,
                          x="Sample Name",
                          y="Mean Diameter (microns)",
                          color="Sample Name",
                         template='plotly_dark').update_layout(
     {'modebar_add': ['v1hovermode', 'toggleSpikelines'],
      'title': 'Mean diameter Plot (microns)',
      'plot_bgcolor': 'rgba(0, 0, 0, 0)',
      'paper_bgcolor': 'rgba(0, 0, 0, 255)'})

      fig_concentration = px.scatter(df,
                               x="Sample Name",
                               y="Concentration",
                               color="Sample Name",
                              template='plotly_dark').update_layout(
      {'title': 'Concentration Plot',
       'modebar_add': ['v1hovermode', 'toggleSpikelines'],
       'plot_bgcolor': 'rgba(0, 0, 0, 0)',
       'paper_bgcolor': 'rgba(0, 0, 0, 255)'})

    @app.callback(
        Output('graph', 'figure'),
        [Input(component_id='graph-selector', component_property='value')]
    )
    def select_graph(value):
        if value == 'fig_viability':
            return fig_viability
        elif value == 'fig_diameter':
            return fig_diameter
        else:
            return fig_concentration

Sorry if that's too wordy, I am newbie to Python and Dash, so tried to give as much detail as possible. Prior to plotting the excel file, three discrete files have to be combined to get this one common file with all the data. The issue is that these files will be changed, as I mentioned earlier, hence the need for live updating.

I hope someone will know how to do it better.. :(

P.S. The data frame looks like this:

   Sample Name  Result  Concentration  Mean Diameter
0            A     244            122              8
1            A     150              8              3
2            A     133             78             10
3            B     567             34             40
4            B     122            622              4
5            B     100            180             20
6            C      55             20             12
7            C      10             11             50
8            C      76            230             37
9            D     230             67              2


Solution 1:[1]

In the current version (2.3.0) of Dash, an element can only be targeted as an Output by a single callback. You can work around the limitation by bundling callbacks with subsequent branching based on the callback context, but it quickly becomes cumbersome.

An alternative is to use dash-extensions (can be installed via pip). After installing, simply replace your dash imports with their counter parts from the enrich module,

from dash_extensions.enrich import Dash, Input, State, Output, html, dcc

and it should work without any code changes.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 emher