обратные вызовы функций, зависящих от вкладки, в приложении plotly dash

#python #dynamic #callback #tabs #plotly-dash

Вопрос:

В настоящее время я создаю приложение dash с 1-50-ю вкладками. Я намерен использовать это в качестве гибкого инструмента визуализации для различных биологических наборов данных, и количество вкладок зависит от набора данных (рассматриваемые биологические данные могут быть разделены на группы, поэтому в каждой группе есть одна вкладка).

Проблема в том, что я не уверен, как организовать приложение — все примеры, похоже, имеют дело с тем, когда вы знаете, сколько вкладок вы получите (чего я не знаю, так как данные могут иметь разное количество групп и, следовательно, вкладок). Я думаю, что, возможно, мне следует связать обратные вызовы, чтобы определить, какая вкладка открыта (что-то вроде метода 1), а затем обновить графики, связанные с этой вкладкой, но я не уверен, что есть лучший способ.

Есть какие-нибудь предложения? Пожалуйста, обратите внимание на ошибки в приложении ниже, так как количество входов в функцию обратного вызова равно Nx ожидаемому числу, где N — количество групп. Это связано с тем, что я не знаю, как организовать функцию обратного вызова для конкретной вкладки 🙁

Спасибо! Тим

 df = biological_dataframe
types = set(list(chain.from_iterable(df['groups_column'])))

app = dash.Dash(__name__)

app.layout = html.Div([
    
    #one tab per biological 'group'
    dcc.Tabs([
        dcc.Tab(label=f'{typ}', 
                children=[
                    dcc.Graph(id = f'{typ}_id2'),
                    dcc.Graph(id = f'{typ}_id3'),
                    dcc.Graph(id = f'{typ}_id4'),
                    dcc.Slider(id=f'{typ}_id5',
                               #.....other params
                               ),
                    dcc.Slider(id=f'{typ}_id6',
                               #.....other params
                               ),
                    dcc.Slider(id=f'{typ}_id7',
                               #.....other params
                               ),
                    ]
                ) 
        for typ in types] )])

#lotsss of inputs/outputs
@app.callback(
    list(chain.from_iterable([[Output(f'{typ}_id2', 'figure'), 
                               Output(f'{typ}_id3', 'figure'),
                               Output(f'{typ}_id4', 'figure')] 
                              for typ in types])),
    list(chain.from_iterable([[Input(f'{typ}_id5', 'value'),
                               Input(f'{typ}_id6', 'value'),
                               Input(f'{typ}_id7', 'value'),
                               Input(f'{typ}_id1', 'value')] 
                              for typ in types]))
    )
def update_figure(num_res, num_bio, diversity_val):
    scope_df = copy.deepcopy(df)
    filtered_df = #filtered df based on slider vals, deleting to save space 
    scope_df['filtered'] = True
    scope_df['filtered'][filtered_df.index] = False
    fig1 = px.pie(filtered_df, 
                  values='interesting_vals_1', 
                  names='interesting_names_1')
    fig2 = px.pie(filtered_df, 
                  values='interesting_vals_2', 
                  names='interesting_names_2')
    fig3 = px.pie(scope_df, 
                  values='interesting_vals_3', 
                  names='interesting_names_3')
    
    fig1.update_layout(transition_duration=500)
    fig2.update_layout(transition_duration=500)
    fig3.update_layout(transition_duration=500)

    return fig1, fig2, fig3


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