Loading indicator when data is being updated

Hello,

we built a dashboard with bokeh which works well and it was a nice process learning bokeh. The only thing bugging us is that currently it’s not transparent to the user when data is being updated. So for example the user clicks something on the map and nothing (visually) happens for a few seconds until data is being updated, the user might click 3 news things meanwhile. This is expected but we’d like to improve this by adding some kind of ‘loading’ animation, initially it might be as simple as some gears turning somewhere on the dashboard.
So here’s my question: Is there a ‘catch all’ way to be notified on the JS side when data is being updated and when the update finishes? It seems to me this would be easiest, ie. anytime any communication happens we show some loading animation. A more refined solution would maybe overlay a loading animation over whatever components are currently being updated. We could probably address this from the python side as well (I’m more familiar with that), but the problem would be that the information to display the loading animation would need to be communicated as well so it would still be delayed.

Thanks!

I have a Loading Indicator PR with Panel. Panel is an extension of Bokeh.

The loading indicator works by adding and removing a css class. The loading indicator it self is a svg (could also be a gif).

You can find the PR here https://github.com/holoviz/panel/pull/1730 and experiment with the solution here https://awesome-panel.org/loading-spinners

Thank you. This looks interesting. I’ll check it out. Question: If I understand correctly this would ‘overload’ all widgets to get this ‘spinner behavior’? Does it trigger in JS? How does it know when data is being updated? I’m looking for a global, JS-side indicator that we can monitor to see if any data is being updated.

For the time being we might just want a global spinner and not individual spinners per widget, one because of the complexity (although this solution looks really simple) and two because we have some data being updated that aren’t bokeh widgets.

Also: How would I use this in bokeh?

1 Like

The spinner I have implemented is set manually by “you” in the code.

The global spinner based on a js indication is something different. Panel (built on top of bokeh) now has a global busy indicator.

OK, I’m looking for a way to determine ‘busy’ state on the JS side in bokeh. Ideally a ‘catch all’ signal which triggers when any data is being updated and when it finishes.

The way I have been doing it is by putting the html code of a loading animation in a ‘static’ folder under the bokeh app and then write its content to a Div() widget via a decorator function while running time consuming tasks. Below the Div() widget gets selected by its name “loader”. You can use it to decorate callbacks or other time consuming functions.

# html code for the loading animation
with open(os.path.join(static_path,'loader.html'),'r') as infile:
    loader = infile.read().replace('\n','')

def busy(func):
	'''
	Decorator function to display a loading animation when the program is working on something
	'''
	def wrapper():
		# show the loading animation for time consuming tasks
		curdoc().select_one({'name':'loader'}).text = loader

		func() # run the decorated function

		# hide the loading animation
		curdoc().select_one({'name':'loader'}).text = ""
	return wrapper
1 Like

I tried your solution with some changes:

In my situation some click can trigger updates to multiple widgets, I added the ‘busy’ decorator to basically all ‘on_change’ callbacks. This results in the busy indicator switching between ‘busy’ and ‘done’ multiple times while the overall update process is still ongoing.
So I tested the following: I set the loader text to ‘0’ initially and increment it by ‘1’ each time the decorator is called and decrement by ‘1’ each time the wrapped function completes. Now I would assume only if the value is ‘0’ all updates are complete. This seems to work better, when I render the div it goes up to 2 or 3 depending on how much is being updated.

One problem I observed: When I update a large CDS, the CDS is still streaming data to the frontend while the counter is already ‘0’ (so update complete).

So it seems like the python side completes and the data will be updated in the background. So it would be really great if one could somehow tap into that process or enable ‘blocking’ behavior until sync is complete (there are probably good reasons against it)…