Reloading when I change the multitabs

Hello everyone, I have already created 2 tabs for an application. But once I change the tab to the other, it reloads the pictures which takes the time to wait. So I wanna to ask whether there is a solution, like saving the pictures in some place rather than reading the files in the disque each time? Thanks a lot in advance!

Hi,

Could you provide us more information about how your tabs are implemented? For instance, are you loading the tab content (the referred pictures) in a callback?

Assuming that you want the content of all tabs loaded at once, you can switch your implementation to Method 2 described in the docs. Then the children of each tab will be “preloaded” when you switch tabs.

Is that what you are looking for?

Thx! Here’s my code logic, the source code is too long, so I handwritten the important parts of my application. Hope you can well understand, and for the link given, I’ll have a try later.
Thx a lot again for your answer.


That’s an old-school way to reply! :slight_smile:

All I needed was the second part actually, and you are implementing as I expected. Please look at the documentation I provided and let us know if there are further questions (I think that will do it for you).

Hello, I’ve understood the ‘preload’ logic, but I don’t know exactly whether I can use it, cause it is a little bit complexe in my case. So I paste my codes, Original Version vs New Version (which I modified with the frame of the example), thx a lot for your time!
[There is an error because the pictures are an array but not an objet, cuz it shows 44lignes*2colones each time, I paste the screenshot below]
Also, I would like to ask more details in order to ensure that I have well understood the ‘preload’ logic: At the beginning, it shows all the pictures. And then if I change the filtres, the pictures change as well? If I change the tab and switch back, it still shows rapidly all the pictures?

==========================
Original Version (app.py + onglet1.py):
app.py

from dash import Dash, dcc, html, callback, dash_table, State, MATCH, ALL
import dash_auth
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
import os

from onglet1 import onglet1_layout
from onglet2 import onglet2_layout
from onglet3 import onglet3_layout

app = Dash(__name__, suppress_callback_exceptions = True)
server=app.server     #déploiement

auth = dash_auth.BasicAuth(
    app,
    {'geodair':'geodair',
     'test':'test'}
)

app_tabs = html.Div([
    dcc.Tabs(id="tabs", value='tab-1',children=[
        dcc.Tab(label='Mesures à 7 jours', value='tab-1'),
        dcc.Tab(label='Mesures manquantes', value='tab-2'),
        dcc.Tab(label='Fichiers h par h', value='tab-3'),
    ]),
])

app.layout = dbc.Container([
    dbc.Row(dbc.Col(html.H1("Geod'air"))),
    html.Br(),
    dbc.Row(dbc.Col(app_tabs, width=12)),
    html.Div(id='tabs-content', children=[])
])

@callback(Output('tabs-content', 'children'),
          Input('tabs', 'value'))
def render_content(tab):
    if tab == 'tab-1':
        return onglet1_layout
    elif tab == 'tab-2':
        return onglet2_layout

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

Part of the codes written in onglet1.py

@callback(
    Output(component_id='hist', component_property='children'),
    Output(component_id='line', component_property='children'),
    Output(component_id="loading-output", component_property="children"),
    Output(component_id="output-container1", component_property="children"),
    Output(component_id="heure-container1-hor", component_property="children"),
    Output(component_id="heure-container1-QH", component_property="children"),
    Input(component_id="btn-télécharger", component_property="n_clicks"),
    Input(component_id='org', component_property='value'),
    Input(component_id='reg', component_property='value')
)

def update_fig(n_clicks, opt_org, opt_reg):
    if n_clicks:
        changed_id = [p['prop_id'] for p in callback_context.triggered][0]
        if 'btn-télécharger' in changed_id:
            ## download the csvfiles
    hists = org_statistique(opt_org, opt_reg)     #horaires
    print("SUCCESS print the groupe of pic1")
    lines = #### the same way to draw the pics
    print("SUCCESS print the groupe of pic2")
    heure_hor = getFileTime(get_csv_stat())
    heure_QH = getFileTime(get_csv_mes())
    return hists,lines,"","",heure_hor,heure_QH

def org_statistique(opt_org, opt_reg):
    ## read csvfile
    ## processus the data
    hists = [] 
    for i in range(len(reseaux)):
        df_hist_i = df_hist[df_hist['réseau']==reseaux[i]].reset_index(drop=True)
        fig_hist = px.bar(df_hist_i, x="jour", y="nbr", color="type de valeur", height=350,category_orders={"type de valeur": ["Données validées","Données brutes"]},labels={'nbr':'nombre de mesure'}, title = df_hist_i['organisme'].unique()[0] + "     réseau = " + str(df_hist_i['réseau'].unique()[0]))
        fig_hist.update_yaxes(range=[0,dict_res_mes[reseaux[i]]+10], matches=None)
        refline = go.Scatter(x=[-7,-6,-5,-4,-3,-2,-1,0,1], y=[dict_res_mes[reseaux[i]]]*9, name='référentiel', mode='lines',line=dict(color="#008000"))
        fig_hist = fig_hist.add_trace(refline)
        fig_hist.update_layout(legend=dict(orientation="h", yanchor="bottom", y=1.01, xanchor="right", x=1),xaxis=dict(tickvals=[-6,-5,-4,-3,-2,-1,0],ticktext=['J-6','J-5','J-4','J-3','J-2','J-1','J']))
        fig_hist.update_layout(hovermode="x")
        hists.append(dcc.Graph(id='hist'+str(i), figure=fig_hist))
    return hists

=====================
New Version:
app.py (I change nothing in tab1.py)
[So, the children of tab1 is solid? I could not change it with the filtres? Or I should write something in order to return tab1_layout?]

def hist():
    #read csvfile
    hists = [] 
    ### draw the histograms - recurrence
    reseaux = df_hist['réseau'].drop_duplicates().reset_index(drop=True)
    for i in range(len(reseaux)):
        df_hist_i = df_hist[df_hist['réseau']==reseaux[i]].reset_index(drop=True)
        fig_hist = px.bar(df_hist_i, x="jour", y="nbr", color="type de valeur", height=350,category_orders={"type de valeur": ["Données validées","Données brutes"]},labels={'nbr':'nombre de mesure'}, title = df_hist_i['organisme'].unique()[0] + "     réseau = " + str(df_hist_i['réseau'].unique()[0]))
        fig_hist.update_yaxes(range=[0,dict_res_mes[reseaux[i]]+10], matches=None)
        refline = go.Scatter(x=[-7,-6,-5,-4,-3,-2,-1,0,1], y=[dict_res_mes[reseaux[i]]]*9, name='référentiel', mode='lines',line=dict(color="#008000"))
        fig_hist = fig_hist.add_trace(refline)
        fig_hist.update_layout(legend=dict(orientation="h", yanchor="bottom", y=1.01, xanchor="right", x=1),xaxis=dict(tickvals=[-6,-5,-4,-3,-2,-1,0],ticktext=['J-6','J-5','J-4','J-3','J-2','J-1','J']))
        fig_hist.update_layout(hovermode="x")
        hists.append(dcc.Graph(id='hist'+str(i), figure=fig_hist))
    return hists

app.layout = html.Div([
     dcc.Tabs([
        dcc.Tab(label='Mesures à 7 jours', children=[
            dcc.Graph(
                figure=hist()
            )
        ]),
        dcc.Tab(label='Mesures manquantes',children=[
            dcc.Graph(
                figure={
                    'data':[
                        {'x':[1,2,3],'y':[2,4,3], 'type':'bar','name':'SF'},
                        {'x':[1,2,3],'y':[5,4,3], 'type':'bar','name':'Montr'},
                    ]
                }
            )
        ]),
    ])
])

err:

=================================

In addition, it works with no Userwarning, but it shows the warnings after adding the codes:

import dash_auth
auth = dash_auth.BasicAuth(
    app,
    {'test':'test'}
)

But actually what I’m written in the codes are “from dash import html”. So I’m wondering if there is something wrong with the version? AND I wanna to ensure that the users couldn’t changer their usernames and password right?

Thx a lot again for your time! I wish that I’ve explained my problems clearly.

The interface that I made, it works but each time I change the tabs, it costs a long time loading.

==

Starting from the bottom: Regarding your second question about the imports, I believe dash_auth is based on dash<2.0.0, so the deprecation warning comes from there. I am not sure about the details of dash_auth regarding usernames and passwords (and I welcome you to ask another question about it, to keep the context clean).


Now, the error message you posted is due to these lines in the first tab:

dcc.Graph(figure=hist())

hist() already returns an array of dcc.Graph components, so you should use children=hist() and not wrap it in dcc.Graph. That said, I don’t think you need such a drastic redesign - you can simply use the layouts you have imported on each tab:

app.layout = html.Div([
     dcc.Tabs([
        dcc.Tab(label='Mesures à 7 jours', children=onglet1_layout),
        dcc.Tab(label='Mesures manquantes',children=onglet2_layout),
    ])
])

I will try to elaborate a bit on what I meant by “preload”. The difference between your initial app with a callback changing the content of dcc.Tabs and the version I wrote above and is the following:

  • In the first case (aka Method 1), only the components for the selected tab (children) exist in the layout. Dash does not execute callbacks where components are missing in the layout, but execute them every time new component are rendered on it. So, say that “tab-1” is selected by default. Then the callbacks for components in tab-1 (figure updates and so on) will be executed, but not the callback for other tabs. If you select another tab, then those callbacks are computed. If you finally go back to tab 1, the callbacks will be recomputed again because the components were removed from the layout.

  • In the second case that I showed above (aka Method 2), all components for all tabs exist on the layout simultaneously. So all callbacks are executed when the app starts (unless prevented explicitly), but they won’t be re-executed when you change tabs. This can be very useful if you have “heavy” components where you want to preserve state when changing tabs (for example, dropdown selections and even zoom levels on graphs), but it can increase the initial page load, so it is a tradeoff.

Thx a lot! I’m so grateful for such a detailed reply!!! Now it works as what I expected!

[When I wanna to switch to the webpage with a lot of images, I need to click several times. However, the loading time is much shorter than before.]

from dash import Dash, dcc, html, callback, dash_table, State, MATCH, ALL
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc

import os

from onglet1 import onglet1_layout
from onglet2 import onglet2_layout
from onglet3 import onglet3_layout

app = Dash(__name__, suppress_callback_exceptions = True)
server=app.server

app.layout = html.Div([
    dcc.Tabs([
        dcc.Tab(label='Mesures à 7 jours', children=onglet1_layout),
        dcc.Tab(label='Mesures manquantes', children=onglet2_layout),
        dcc.Tab(label='Fichiers h par h', children=onglet3_layout),
    ])
])
    
if __name__ == '__main__':
    app.run_server(debug=True)