Panel loading indicator doesn't show

Hi, I have several multi-tab apps in Bokeh, with the last few lines similar to those shown below. I want to use the loading indicator, but can’t get it to show.

It would seem I am making a simple error, so apologies for the basic question.

first = gridplot([[s1, s2, s3], [s4, s5, s6]], sizing_mode='stretch_both', merge_tools=False)
second = gridplot([[s7, s8, s9], [s10, s11, s12]], sizing_mode='stretch_both', merge_tools=False)

pn.extension(loading_spinner='dots', loading_color='#00aa41', sizing_mode="stretch_width")
pn.param.ParamMethod.loading_indicator = True

first = pn.pane.Bokeh(column(button, first), sizing_mode='stretch_both')
second = pn.pane.Bokeh(column(button, second), sizing_mode='stretch_both')

tabs = pn.Tabs(('first', first), ('second', second), css_classes=['custom_tab'], sizing_mode='stretch_both', dynamic=True)
tabs.servable()

Thanks.

Hi @Paul,

Could you please post a minimum example that could help us reproduce your issue?

Thanks. Here’s a fuller example.

import pandas as pd
import numpy as np

from bokeh.plotting import figure, curdoc, show
from bokeh.models import ColumnDataSource
from bokeh.layouts import gridplot, column

import panel as pn

dates = pd.date_range("20210101", periods=6)
df = pd.DataFrame(np.random.randn(6, 2), index=dates, columns=list("AB"))
df.rename_axis('Date', axis=0, inplace=True)

source = ColumnDataSource(df)

s1 = figure()
s1.line(x='Date',
        y='A',
        source = source)

s2 = figure()
s2.line(x='Date',
        y='B',
        source = source)

s3 = figure()
s3.line(x='Date',
        y='B',
        source = source)

s4 = figure()
s4.line(x='Date',
        y='B',
        source = source)

s5 = figure()
s5.line(x='Date',
        y='B',
        source = source)

s6 = figure()
s6.line(x='Date',
        y='B',
        source = source)

first = gridplot([[s1, s2], [s3]], sizing_mode='stretch_both', merge_tools=False)
second = gridplot([[s4, s5], [s6]], sizing_mode='stretch_both', merge_tools=False)

first_layout = pn.pane.Bokeh(column(first), sizing_mode='stretch_both')
second_layout = pn.pane.Bokeh(column(second), sizing_mode='stretch_both')

pn.extension(loading_spinner='dots', loading_color='#00aa41', sizing_mode="stretch_width")
pn.param.ParamMethod.loading_indicator = True

tabs = pn.Tabs(('first', first_layout), ('second', second_layout), css_classes=['custom_tab'], sizing_mode='stretch_both', dynamic=True).servable()
1 Like

To get the loading indicator to work, you need to have a plot updating. This is an (ugly) example of how it could be done:

import panel as pn
import holoviews as hv

hv.extension("bokeh")
pn.extension(throttled=True)
pn.param.ParamMethod.loading_indicator = True

w = pn.widgets.IntSlider(end=10000)
pn.Column(w, pn.bind((lambda i: hv.Curve(range(i))), w)).servable()

2 Likes

Thanks. It seems I misunderstood. I was looking for a loading icon that shows while the app is being loaded. The apps I am currently using just show a white screen until the charts appear.

1 Like

One way to show loading information is like below

import panel as pn
import time
import holoviews as hv

pn.extension(sizing_mode="stretch_width")

loading_message = pn.pane.Markdown("The app is loading ...")
loading_indicator = pn.indicators.Progress(value=20, type="primary")
plot = pn.pane.HoloViews(sizing_mode="stretch_both", loading=True)
layout = pn.Column(
    loading_message, loading_indicator, plot
)

def load():
    for _ in range(0,3):
        time.sleep(1)
        loading_indicator.value+=20
    data=pd.DataFrame({"x": [1,2], "y": [3,4]})
    plot.sizing_mode="stretch_both"
    plot.object = data.hvplot()
    
    loading_message.visible=False
    loading_indicator.visible=False

pn.state.onload(load)

layout.servable()

Another more reactive way could look like

import hvplot.pandas
import pandas as pd
import panel as pn
import time
import param

pn.extension(sizing_mode="stretch_width")

class StateBase(param.Parameterized):
    """General State Management Class. Inherit from this"""
    value = param.Parameter()

    def __init__(self, **params):
        super().__init__(**params)
        
        self.value = self
        
        for parameter in self.param:
            self.param[parameter].constant=True
      

    def update(self, **kwargs):
        """Use this method only to update the state parameters"""
        with param.edit_constant(self):
            self.param.update(**kwargs)
        self.param.trigger("value")


class AppState(StateBase):
    progress = param.Integer()
    data = param.Parameter()
    
state = AppState()

def component(state):
    if state.data is None:    
        loading_message = pn.pane.Markdown("The app is loading ...")
        loading_indicator = pn.indicators.Progress(value=state.progress, bar_color="primary")
        return pn.Column(loading_message, loading_indicator)

    return pn.pane.HoloViews(state.data.hvplot(), sizing_mode="stretch_both")

def load():
    for _ in range(0,3):
        time.sleep(1)
        state.update(progress=state.progress+20)
    state.update(data=pd.DataFrame({"x": [1,2], "y": [3,4]}))

icomponent = pn.bind(component, state=state.param.value)
pn.panel(icomponent, sizing_mode="stretch_both").servable()

pn.state.onload(load)
2 Likes

Thanks for your help. I’ll see if I can make it work.

1 Like