✊🏿 Black Lives Matter. Please consider donating to Black Girls Code today.
⚾️ It's finally Baseball season! Root for the home team... & Register for our Sports Analytics Webinar!

Separating tabs across different files

Hi there, I’m trying to make a structured project, generalizing tabs to make them reusable in other projects. The way I’m trying to do this is by having one main file that calls function/structures from other files. Where each file it calls corresponds to one tab.

The callback of the drop-down on the first tab is calling a function from another file, the function will do some data processing and return the figure section of my graph. The problem is that my graph doesn’t seem to show any data. There are no callback errors raised, so that can’t be the problem. Also, I’ve tested the code in one file, so that can’t be the problem either. Do you have any idea?

CODE IN MAIN:

import tab1
import tab2
import tab3
import tab4

app.layout = html.Div([
    html.Div(
        dcc.Tabs(
            tabs=[
                {'label': 'Locatie', 'value': 1},
                {'label': 'Usage Over Time', 'value': 2},
                {'label': 'Predictions', 'value': 3},
                {'label': 'Target Pricing', 'value': 4},
            ],
            value=1,
            id='tabs',
            vertical=vertical,
            style={
                'height': '100vh',
                'borderRight': 'thin lightgrey solid',
                'textAlign': 'left'
            }
        ),
        style={'width': '20%', 'float': 'left'}
    ),
    html.Div([dcc.Dropdown(id = 'tab-output',value = "Oogkliniek de Horsten"),
              dcc.Graph(id='graph'),
              html.Div(' '.join(get_sentences(10))),
               ],
            style={'width': '80%', 'float': 'right'})
], style={
    'fontFamily': 'Sans-Serif',
    'margin-left': 'auto',
    'margin-right': 'auto',
})

@app.callback(Output('tab-output', 'options'), 
              [Input('tabs', 'value')])
def display_content(tab):
    if tab == 1:
        return tab1.tab_content(gbq_data)
    if tab == 2:
        return tab2.tab_content(gbq_data)
    if tab == 3:
        return tab3.tab_content(gbq_data)
    if tab == 4:
        return tab4.tab_content(gbq_data)

@app.callback(Output('graph', 'figure'), [Input('tab-output', 'value')])
def update_graph(value):          
    return tab1.get_graph[gbq_data,value]


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

CODE IN tab1.py:

def tab_content(gbq_data):
    return [{'label': i,'value': i} for i in pd.unique(gbq_data['dim_locatie_naam'])]
    
def get_graph(gbq_data, loc_name):
    totaalcore_bench = gbq_data[gbq_data['dim_datum_jaar']==2016].drop_duplicates(['dim_locatie_id','agg_totaalscore_locatie'])['agg_totaalscore_locatie'].mean()
    aandoening_bench = gbq_data[gbq_data['dim_datum_jaar']==2016].drop_duplicates(['dim_locatie_id','agg_aandoeningspecifieke_score_locatie'])['agg_aandoeningspecifieke_score_locatie'].mean()
    kwal_algemeen_bench = gbq_data[gbq_data['dim_datum_jaar']==2016].drop_duplicates(['dim_locatie_id','agg_kwaliteitalgemeen_score_locatie'])['agg_kwaliteitalgemeen_score_locatie'].mean()       
    
    totaal_loc = gbq_data[(gbq_data['dim_locatie_naam']==loc_name) & (gbq_data['dim_datum_jaar']==2016)]['agg_totaalscore_locatie'].iloc[1]
    aand_loc = gbq_data[(gbq_data['dim_locatie_naam']==loc_name) & (gbq_data['dim_datum_jaar']==2016)]['agg_aandoeningspecifieke_score_locatie'].iloc[1]
    kwal_loc = gbq_data[(gbq_data['dim_locatie_naam']==loc_name) & (gbq_data['dim_datum_jaar']==2016)]['agg_kwaliteitalgemeen_score_locatie'].iloc[1] 
    
    data = [       
            {       # Locatie
                    'x': ['Aand','Alg','Totaal'],
                    'y': [aandoening_bench,kwal_algemeen_bench,totaalcore_bench],
                    'name': 'Benchmark',
                    'marker': {'color': 'rgb(255, 0, 255)'},
                    'type': 'bar'
            },
                    
            {       # Benchmark
                    'x': ['Aand','Alg','Totaal'],
                    'y': [aand_loc,kwal_loc,totaal_loc],
                    'name': 'Locatie',
                    'marker': {'color': 'rgb(0, 0, 255)'},
                    'type': 'bar'
            }]
    
    return {'data': data,
            'layout': {'margin': {
                            'l': 30,
                            'r': 0,
                            'b': 30,
                            't': 0},
                            'legend': {'x': 0, 'y': 1}}}

Greetings, Tom

Hi,

your code is not easy to test… you do not precise the libraries (import ...), some function are undefined (get_sentence) and the data are unknown.

Nevertheless, I managed to make a similar app as yours. I spotted the following mistakes:

  • return tab1.get_graph[gbq_data,value], get_graph is a function, so you have to replace the [] by ().
  • you did not import pandas in tab1.py, so pd.unique is unknown.
  • vertical=vertical is wrong, unless you defined the variable vertical somewhere else. You have to put "vertical" as a string.
  • I am not sure of your iloc[1], do you mean the first location? It should be [0]. But maybe it’s related to my df.

With that, your app is OK I think. See example inspired by you below:

main.py

import dash
from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objs as go
import pandas as pd

import tab1
# import tab2
# import tab3
# import tab4

df = pd.DataFrame(columns=['y1A', 'y1B', 'y1C', 'y2A', 'y2B', 'y2C'],
                 data=pd.np.random.randn(20,6))
df["location"] = ["City1"]*10 + ["City2"]*10

app = dash.Dash()

app.layout = html.Div([
    html.Div(
        dcc.Tabs(
            tabs=[
                {'label': 'Locatie', 'value': 1},
                {'label': 'Usage Over Time', 'value': 2},
                {'label': 'Predictions', 'value': 3},
                {'label': 'Target Pricing', 'value': 4},
            ],
            value=1,
            id='tabs',
            vertical='vertical',
            style={
                'height': '100vh',
                'borderRight': 'thin lightgrey solid',
                'textAlign': 'left'
            }
        ),
        style={'width': '20%', 'float': 'left'}
    ),
    html.Div([dcc.Dropdown(id = 'tab-output',value = "Oogkliniek de Horsten"),
              dcc.Graph(id='graph'),
              html.Div(' '.join(["a", "b"])),
             ],
             style={'width': '80%', 'float': 'right'})
], style={
    'fontFamily': 'Sans-Serif',
    'margin-left': 'auto',
    'margin-right': 'auto',
})

@app.callback(Output('tab-output', 'options'), 
              [Input('tabs', 'value')])
def display_content(tab):
    if tab == 1:
        return tab1.tab_content(df)
    # if tab == 2:
    #     return tab2.tab_content(gbq_data)
    # if tab == 3:
    #     return tab3.tab_content(gbq_data)
    # if tab == 4:
    #     return tab4.tab_content(gbq_data)

@app.callback(Output('graph', 'figure'),
              [Input('tab-output', 'value')])
def update_graph(value):          
    return tab1.get_graph(df,value)

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

tab1.py

import pandas as pd
def tab_content(df):
    return [{'label': i,'value': i} for i in pd.unique(df['location'])]
    
def get_graph(df, loc_name):
    idx = df['location'] == loc_name
    y1A = df['y1A'].mean()
    y1B = df['y1B'].mean()
    y1C = df['y1C'].mean()
    
    y2A = df.loc[idx, 'y2A'].iloc[0]
    y2B = df.loc[idx, 'y2B'].iloc[0]
    y2C = df.loc[idx, 'y2C'].iloc[0]
    
    data = [       
            {       # Locatie
                    'x': ['A', 'B', 'C'],
                    'y': [y1A, y1B, y1C],
                    'name': 'Benchmark',
                    'marker': {'color': 'rgb(255, 0, 255)'},
                    'type': 'bar'
            },
                    
            {       # Benchmark
                    'x': ['A', 'B', 'C'],
                    'y': [y2A, y2B, y2C],
                    'name': 'Locatie',
                    'marker': {'color': 'rgb(0, 0, 255)'},
                    'type': 'bar'
            }]
    
    return {'data': data,
            'layout': {'margin': {
                            'l': 30,
                            'r': 0,
                            'b': 30,
                            't': 0},
                            'legend': {'x': 0, 'y': 1}}}

rmk: you have the commentary “Locatie” and the name “Benchmark”, and vice-versa. Is it ok?

1 Like

Hi GwendalD,

I realize I haven’t really posted an easy reproducible problem (looking at the code), thank you for still taking a look! The issues you note are indeed correct, especially the first one. The code is now running:)

Again, thanks!

Hi @GwendalD
Your suggestion got me 90% of the way there, but I wasn’t able to successfully reproduce your example. I’m getting the following error message: TypeError: Unexpected keyword argumenttabs``
Is it possible that the dcc.tabs component has evolved since you posted your reply in April? Does your code example still run correctly for you? Thanks - A.

Hi!

Indeed the Tabs component encountered big change during this summer. This example may not work anymore.

You may go to https://dash.plot.ly/dash-core-components/tabs to see the new syntax.

1 Like

Thanks for the tip. I followed the new syntax as you suggested, and successfully built a multi-tab app with layout saved in multiple files.