pythoncallbackplotlyplotly-dash

Dash Python: @Callback in Layout function not being called


I have a simple dataframe:

import pandas as pd
df = pd.DataFrame({'Class1': [1, 2, 3, 4, 5],
                   'Class2': [6, 7, 8, 9, 10]}
)  

I have created a data extraction function that splits the data based on the position of the column.

data_extraction.py

def dataExtraction(arg1):

    df = pd.DataFrame({'Class1': [1, 2, 3, 4, 5],
                             'Class2': [6, 7, 8, 9, 10]})  ## <-- or Import df from somewhere
    df = df[[f'Class{arg1}']]

    return df    

layout.py

import dash_bootstrap_components as dbc
from dash import dcc, html, Input, Output, callback
from dash import Dash, dash_table, State
import dash_daq as daq
import pandas as pd

def update_page(arg1, arg2):
    layout = html.Div(children=[
        html.H1(f'Class {arg1}'),
        daq.NumericInput(
            id='numericinput1',
            min=0,
            max=100,
            value=0, ), 
        html.Br(),
        dash_table.DataTable(
            id='tableTest',
            data=arg2.to_dict('records'),
            columns=[{"name": i, "id": i} for i in arg2.columns]),
    ])

    return layout

@callback(
    Output('tableTest', 'data'),
    Input('numericinput1', 'value'),
    State('tableTest', 'data'),
)

def updateTableTest(x,data):

    data = pd.DataFrame(data)

    if x > 0:
        **print(data) # Indicator that shows callback is working**
    print("CALLBACK WORKING!!!")

        return data.to_dict('records')
    return data.to_dict('records')

I have also constructed a tab page as follows that consists of a page with 2 tabs. The tab_page displays the tabs with their respective data column (E.g. tab 1 for column 1). I have written 'print (data)' to check that the callback in the layout function is working. It is working for one tab but not the other.

tab_page.py

import layout as lay
import data_extraction as de
import dash_bootstrap_components as dbc
from dash import html, Dash
import dash

app = Dash()

dash.register_page(__name__, 
                   path='/tabs') 

def get_layout(position):
        df = de.dataExtraction(position)
        layout = lay.update_page(position, df)
        return layout


tab1_content = dbc.Card(
    dbc.CardBody(
        [
                **get_layout(1)#<--- CALLBACK NOT WORKING HERE The print(data) is NOT working**
        ]
    ),
    className="mt-1",
)

tab2_content = dbc.Card(
    dbc.CardBody(
        [
                get_layout(2) #<--- The print(data) is working
        ]
    ),
    className="mt-2",
)

layout = html.Div(children = [
    dbc.Tabs(
    [
        dbc.Tab(tab1_content, label="1",activeLabelClassName="text-success"),
        dbc.Tab(tab2_content, label="2",activeLabelClassName="text-success"),
    ]
)])


app.layout = [layout]

if __name__ == '__main__':
    app.run(debug=True)

The callback in the layout works for the get_layout(2) but not get_layout(1). Why does it work for one and not the other?


Solution

  • The problem that you have duplicated ids in the layout. And single callback can't be linked to two objects with the same ids:

    Fix 1 - remove duplicated ids

    # layout.py
    def update_page(arg1, arg2):
    
        layout = html.Div(children=[
            html.H1(f'Class {arg1}'),
            daq.NumericInput(
                id=f"numericinput{arg1}",
                min=0,
                max=100,
                value=0, ), 
            html.Br(),
            dash_table.DataTable(
                id=f"tableTest{arg1}",
                data=arg2.to_dict('records'),
                columns=[{"name": i, "id": i} for i in arg2.columns]),
        ])
    
        return layout
    

    Fix 2 - make two callbacks, one for each table

    # layout.py
    def create_callback(i):
    
        @callback(
            Output(f'tableTest{i}', 'data'),
            Input(f'numericinput{i}', 'value'),
            State(f'tableTest{i}', 'data'),
        )
        def updateTableTest(x, data):
    
            data = pd.DataFrame(data)
            print(x)
            if x > 0:
                print(data) # Indicator that shows callback is working
    
                return data.to_dict('records')
            return data.to_dict('records')      
        
        return updateTableTest
    

    Fix 3 - initiate callbacks

    # tab_page.py
    ...
    # initiate callbacks:
    app.layout = [layout]
    
    lay.create_callback(1)
    lay.create_callback(2)
    
    
    if __name__ == '__main__':
        app.run(debug=True)
    ...