Dash Python: функция @Callback в макете не вызывается

У меня есть простой фрейм данных:

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

Я создал функцию извлечения данных, которая разбивает данные в зависимости от положения столбца.

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    

макет.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')

Я также создал страницу вкладок следующим образом, состоящую из страницы с двумя вкладками. На странице tab_page отображаются вкладки с соответствующим столбцом данных (например, вкладка 1 для столбца 1). Я написал «печать (данные)», чтобы проверить, работает ли обратный вызов в функции макета. На одной вкладке работает, на другой нет.

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)

Обратный вызов в макете работает для get_layout(2), но не для get_layout(1). Почему это работает для одного, а не для другого?

🤔 А знаете ли вы, что...
Python поддерживает многозадачность и многопоточность.


77
1

Ответ:

Решено

Проблема в том, что у вас дублируются id в макете. И один обратный вызов не может быть связан с двумя объектами с одинаковыми идентификаторами:

Исправление 1 – удалить повторяющиеся идентификаторы

# 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

Исправление 2 – сделать два обратных вызова, по одному для каждой таблицы.

# 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

Исправление 3 — инициировать обратные вызовы

# tab_page.py
...
# initiate callbacks:
app.layout = [layout]

lay.create_callback(1)
lay.create_callback(2)


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

Интересные вопросы для изучения

Как извлечь объем из строки с помощью регулярного выражения?Как удалить большое пространство между строками на графике matplotlib?Как выбрать конкретный дочерний тег из родительского тега и извлечь из него данные?Есть ли способ добавить кнопки закрытия и добавления вкладок в tkinter.ttk.Notebook?Как использовать тип, хранящийся в переменной класса, в качестве подсказки типа для параметра метода того же класса?Почему JavaScript с первого раза так быстро выполняет обратные вызовы в цикле for?Принципы и практика программирования Страуструпа, 3-е издание, графика: почему передача функции обратного вызова в Window::timer_wait дает неожиданные результаты?Как Callable может вернуть значение из предопределенного обратного вызова void?Частичные цепочки обратных вызовов с использованием сюжетного тиреОбратные вызовы в Джулии, когда я использую реакционную сеть