As of Bokeh 1.0.4, "busy" / "done" events (to enable things like triggering spinners or other UI events) are still an open feature request.
In the mean time, your best bet is to use some "dummy" model to trigger a CustomJS
callback. For instance, you could add an invisible glyph, and trigger a CustomJS
any property on it as a proxy for a "busy" event. This is clunky, but serviceable.
Here is a very rough outline example. The first alert will pop up immediately. Close it, the next alert will pop up 5 seconds later.
import time
from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.models import Button, CustomJS
from bokeh.plotting import figure
p = figure()
p.circle([1,2,3,4,5], [2,6,3,1,6])
dummy = p.circle([1], [2], alpha=0)
dummy.glyph.js_on_change('size', CustomJS(code="""
alert(cb_obj.size.value)
"""))
b = Button()
def cb():
dummy.glyph.size = 10
time.sleep(5)
dummy.glyph.size = 20
b.on_click(cb)
curdoc().add_root(column(b, p))