Kristian Vybiral
Kristian Vybiral

Reputation: 539

Bokeh server and on.click (on.change) doing nothing

I am trying to get Bokeh server to print out, however all I get is an instance running on http://localhost:5006/?bokeh-session-id=default with the radio buttons. When I click the buttons nothing happens. Is there something I am missing?

from bokeh.models.widgets import RadioButtonGroup
from bokeh.plotting import figure, show, output_server


def my_radio_handler():
    print 'Radio button option selected.'


radio_button_group = RadioButtonGroup(
        labels=["Option 1", "Option 2", "Option 3"], active=0)

radio_button_group.on_click(my_radio_handler)

output_server()

show(radio_button_group)

Upvotes: 2

Views: 3488

Answers (2)

bigreddot
bigreddot

Reputation: 34568

The answer above shows how this can properly be accomplished with bokeh.client and push_session and session.loop_until_closed.

I'd like to add a few more comments for context. (I am a core developer of the Bokeh project)

There are two general ways that Bokeh applications can run:

Directly In the Server

These are scripts that are run something like:

bokeh serve --show my_app.py

In this case, the code, the objects, all the callbacks, etc. are running in the bokeh server itself. The situation looks like this:

                          browser <---> Bokeh Server

This is the method I would always recommend first. It takes the least amount of code, is the simplest to deploy, can be scaled out, uses less network traffic, and is more robust.

In a Separate Python Process

It's also possible to create the app and run callbacks in a separate process, using bokeh.client. Then the situation looks like this:

               browser <---> Bokeh Server <---> another python process

Then the Bokeh server really just become a middleman, relaying messages between the browser and your python process. This has disadvantages:

  • doubles the network traffic (for the new python process, unavoidable by definition)
  • takes more code to write (all the session stuff not need in "Server Apps")
  • requires the python process to block indefinitely to service the callbacks (have to call session.loop_until_closed)

In the past, there were specific use-cases where using bokeh.client was necessary, for example: being able to customize app sessions per-use. But now that HTML request arguments are available to "Server Apps" that can be accomplished without bokeh.client. I would say there are now fewer reasons to reach for the bokeh.client approach. For this reason I always recommend the bokeh serve my_app.py method as the first, best way to use the Bokeh server.


But, getting back to the question at hand: So what happens in the Separate Python Process scenario, if you forget to call session.loop_until_closed? Well the python process (the one that has your callbacks!) finishes, and the process terminates. Then there is nothing left to actually run the callbacks.

Well, this is essentially exactly the situation with output_server. It does the "first half" of the session setup, loading the objects in to the server, but then your python script that calls output_server finishes, and nothing is left to execute any callbacks. output_server is basically still around due to accidents of history, but I would say it is not very useful at all. At best, it can load apps with no callbacks into a Bokeh server, but then why would you need a Bokeh server except to connect web apps to real python callbacks?

There is currently (as of release 0.12.2) an open issue discussing deprecating output_server for this reason:

https://github.com/bokeh/bokeh/issues/5154

TLDR; I would not recommend using output_server for any reason at this point.

Upvotes: 6

Tiger-222
Tiger-222

Reputation: 7150

You need to sync the server and the current session to get informations back.

from bokeh.client import push_session
from bokeh.models.widgets import RadioButtonGroup
from bokeh.plotting import curdoc, figure, show, output_server


def my_radio_handler(new):
    print 'Radio button ' + str(new) + ' option selected.'


radio_button_group = RadioButtonGroup(
    labels=['Option 1', 'Option 2', 'Option 3'], active=0)
radio_button_group.on_click(my_radio_handler)

# Open a session to keep our local document in sync with server
session = push_session(curdoc())

# Open the document in a browser
session.show(radio_button_group)

# Run forever (this is crucial to retrive callback's data)
session.loop_until_closed()

You can get more control on the click handler using on_change():

def my_radio_handler_complete(attr, old, new):
    print(attr, old, new)
    print('Radio button ' + str(new) + ' option selected.')

radio_button_group.on_change('active', my_radio_handler_complete)

FYI, in the source-code, on_click is just a proxy for on_change() and defined as:

def on_click(self, handler):
   self.on_change('active', lambda attr, old, new: handler(new))

Upvotes: 2

Related Questions