7

I have a Bokeh application that makes use of the Python callbacks for various widget events. With certain events, I'd like to execute some JavaScript code before making the callback to the Python function. Is this possible?

In this case, the Python callback is potentially long-running, and I'd like to start and stop a Javascript spinner object before and after the long-running Python code executes.

2 Answers 2

8

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))
Sign up to request clarification or add additional context in comments.

4 Comments

I am not sure how to connect an event (i.e. button click) to both my python callback and a property change on the "dummy" model instance? If the python callback changes the property, then the client side won't see that change until the callback completes (which would be good for stopping my spinner). But I am not sure how handle/capture the starting event.
"If the python callback changes the property, then the client side won't see that change until the callback completes" That's not true, see the example code in the updated answer.
So what is the mechanism that allows this to work? I was (obviously) assuming that a request would go from client to server to execute the python callback and that not data would "emit" from the python callback until that function returned - and the data was transferred back over the network to the client. What allows data to come from server back to the client before the python callback returns?
All changes to properties are currently detected immediately, and the corresponding protocol message for patching Bokeh documents with updated properties are also sent across the websocket immediately. There is an open issue somewhere to support a "batch" mode to defer updates until some later time, but there has not been much user demand around it so it has not been prioritized.
0

For example:

dummy_widget = TextInput(visible=False)
custom_callback = CustomJs(code="""
alert('Hello world');
""")

dummy_widget.js_on_change('value', custom_callback)

button = Button(label='Python Callback')

def python_callback(event):
   global dummy_widget
   
   dummy_widget.value = 'Hi user!'

button.on_click(bokeh.event.ButtonClick, python_callback)

curdoc().add_root(Layouts.column(button, dummy_widget))

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.