How do I add loading indicator to a holoviz panel which is displayed while some computation is done?

Hi,

I posted a question on stackoverflow: python 3.x - How do I add loading indicator to a holoviz panel which is displayed while some computation is done? - Stack Overflow

I just discovered this community, and re-posting the same here. Below are the question details:

I am working on a project which uses the holoviz panel for display. The panel has a button which does some heavy computation on click and displays result after a few seconds. How do I modify the code such that I get a loading indicator(spinner) while data is being fetched? I probably need to add loading_indicator = True somewhere(pass as parameter), but haven’t had any success so far. Is there an alternate way? Minimal code:

import panel as pn
import pandas as pd

pn.extension()

submit_button = pn.widgets.Button(name='Submit', button_type='primary', width = 180)
radio_buttons_group = pn.widgets.RadioButtonGroup(options=['Ying', 'Yang'], button_type='default')
outer_col = pn.Column(radio_buttons_group, submit_button)

@pn.depends(submit_button.param.clicks, watch=True)
def display_result(event):
    if(len(outer_col) > 2):
        outer_col.pop(len(outer_col)-1)

    if(radio_buttons_group.value == 'Ying'):
        ying_view()
    else:
        yang_view()
        
def ying_view():
    #some heavy computation
    outer_col.append(pd.DataFrame({'col1': [1, 2], 'col2': [3, 4]})) #sample
    
def yang_view():
    #some heavy computation
    outer_col.append(pd.DataFrame({'col1': [5, 6], 'col2': [7, 8]})) #sample
    
outer_col

I just want to include the (bare minimum) loading indicator. Nothing fancy please.
Thanks in advance!

2 Likes

You can use pn.panel(..., loading_indicator=True) as below.

import panel as pn
import pandas as pd
import time

pn.extension()

submit_button = pn.widgets.Button(name='Submit', button_type='primary', width = 180)
radio_buttons_group = pn.widgets.RadioButtonGroup(options=['Ying', 'Yang'], button_type='default')

@pn.depends(submit_button.param.clicks)
def result(event):
    if submit_button.clicks<1:
        return "Choose an option and click submit"
    if(radio_buttons_group.value == 'Ying'):
        return ying_view()
    else:
        return yang_view()
        
def ying_view():
    #some heavy computation
    time.sleep(0.5)
    return pd.DataFrame({'col1': [1, 2], 'col2': [3, 4]}) #sample
    
def yang_view():
    #some heavy computation
    time.sleep(0.5)
    return pd.DataFrame({'col1': [5, 6], 'col2': [7, 8]}) #sample

outer_col = pn.Column(radio_buttons_group, submit_button, pn.panel(result, loading_indicator=True))
outer_col.servable()

Explore the live app here (Note: time.sleep is not working in Webassembly).

4 Likes

That’s very kind of you Marc. I really appreciate the answer. Follow-up:
I can’t get rid of the line

if(len(outer_col) > 2):
        outer_col.pop(len(outer_col)-1)

I understand that you provided a sanity check(clicks < 1), but it is already accounted for somewhere else. Is there a way we can work around this as well(keeping this piece of code intact instead of placing the sanity check)?

Thanks a lot Marc

Here’s another solution by @carl which might be helpful for someone

import panel as pn
import pandas as pd

pn.extension()

submit_button = pn.widgets.Button(name='Submit', button_type='primary', width = 180)
radio_buttons_group = pn.widgets.RadioButtonGroup(options=['Ying', 'Yang'], button_type='default')
outer_col = pn.Column(radio_buttons_group, submit_button)

#added loading widget indicator
loading = pn.indicators.LoadingSpinner(value=False, width=100, height=100,visible=False)

#function to activate / deactivate loading widget      
def load_display(x):
    if(x=='on'):
        loading.value=True
        loading.visible=True
    if(x=='off'):
        loading.value=False
        loading.visible=False


@pn.depends(submit_button.param.clicks, watch=True)
def display_result(event):
    if(len(outer_col) > 2):
        outer_col.pop(len(outer_col)-1)

    if(radio_buttons_group.value == 'Ying'):
        ying_view()
    else:
        yang_view()
        
def ying_view():
    #some heavy computation
    load_display('on') #call loading widget with 'on' to activate
    time.sleep(0.5)
    outer_col.append(pd.DataFrame({'col1': [1, 2], 'col2': [3, 4]})) #sample
    load_display('off')
    
def yang_view():
    #some heavy computation
    load_display('on')
    time.sleep(0.5)
    outer_col.append(pd.DataFrame({'col1': [5, 6], 'col2': [7, 8]})) #sample
    load_display('off')
    
x = pn.Row(outer_col, loading)
x.show()