Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bokeh Server callback from tools

I'm kinda new on Python, and currently working on a interactive plot visualization using Bokeh where I need to show multiple related charts. To accomplish this i'm using bokeh server.

I've been reading the docs and some examples but i've been unable to find an example of a python callback (executed in the server) triggered by a selection on the plot. Basically what i would like to do is something like:

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

TOOLS = "tap"
p = figure(title="Some Figure", tools=TOOLS)

source = ColumnDataSource(dict(x=[[1, 3, 2], [3, 4, 6, 6]], y=[[2, 1, 4], [4, 7, 8, 5]], name=['A', 'B']))

p.patches('x', 'y', source=source, color=["firebrick", "navy"], alpha=[0.8, 0.3], line_width=2)


def callback():
    print("TapTool callback executed on Patch {}")


??? <- some code here linking the taptool with the callback function defined above


curdoc().add_root(column(p))

and then when executing the server and clicking on a patch:

2017-02-14 16:32:00,000 TapTool callback executed on Patch A

is this behavior something that can be achieved with bokeh?

like image 925
Sebastian Yonekura Baeza Avatar asked Feb 14 '17 19:02

Sebastian Yonekura Baeza


3 Answers

Edit from project maintainers.

There was some confusion and regressions around selections leading up to 1.0. For any post 1.0 version, for most use case you would now want to use a callback on the 'indices' property of selected:

source.selected.on_change('indices', callback)

This kind of usage is now continuously and rigorously maintained under integration tests, and is what should be used for any post 1.0 Bokeh version.


The selected event can be linked to an update function as follows:

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

TOOLS = "tap"
p = figure(title="Some Figure", tools=TOOLS)

source = ColumnDataSource(dict(x=[[1, 3, 2], [3, 4, 6, 6]],
                y=[[2, 1, 4], [4, 7, 8, 5]], name=['A', 'B']))

pglyph = p.patches('x', 'y', source=source, color=["firebrick", "navy"],
                                alpha=[0.8, 0.3], line_width=2)

def callback(attr, old, new):
    # The index of the selected glyph is : new['1d']['indices'][0]
    patch_name =  source.data['name'][new['1d']['indices'][0]]
    print("TapTool callback executed on Patch {}".format(patch_name))

pglyph.data_source.on_change('selected',callback)

curdoc().add_root(column(p))

Update for newer Bokeh versions. Tested on version 0.12.16

As @Karel Marik mentions glyphs can not mixed direct values assignment and ColumnDataSource at the same time. So the previous code does not work. Here is an update using only source which also includes code to print the multiple selections (made with shift + click):

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

TOOLS = ["tap"]
p = figure(title="Some Figure", tools=TOOLS)

source = ColumnDataSource(dict(
    x=[[1, 3, 2], [3, 4, 6, 6]],
    y=[[2, 1, 4], [4, 7, 8, 5]],
    name=['A', 'B'],color=["firebrick", "navy"],
    alpha=[0.8,0.3],line_width=[3,3]))

pglyph = p.patches('x', 'y', color="color", alpha="alpha",
                   line_width="line_width", source=source)

def callback(attr, old, new):
    # The index of the selected glyph is : new['1d']['indices'][0]
    selections = new['1d']['indices']
    print("Number of selections:{}".format(len(selections)))
    for index in selections:
        patch_name =  source.data['name'][index]
        print("TapTool callback executed on Patch {}".format(patch_name))

pglyph.data_source.on_change('selected',callback)

curdoc().add_root(column(p))
like image 62
Pablo Reyes Avatar answered Nov 02 '22 15:11

Pablo Reyes


The solution published by Pablo is great but it doesn't work with recent bokeh version (0.12.13). I got an runtime error (Supplying a user-defined data source AND iterable values to glyph methods is not possible)

Following works for me ...

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

TOOLS = "tap"
p = figure(title="Some Figure", tools=TOOLS)

source = ColumnDataSource(dict(x=[[1, 3, 2], [3, 4, 6, 6]], y=[[2, 1, 4], [4, 7, 8, 5]], alphas = [0.8, 0.3], colors=["firebrick", "navy"], name=['A', 'B']))

pglyph = p.patches(xs='x', ys='y', source=source, line_width=2, alpha = 'alphas', color='colors')

def callback_fcn(attr, old, new):
    # The index of the selected glyph is : new['1d']['indices'][0]
    patch_name =  source.data['name'][new['1d']['indices'][0]]
    print("TapTool callback executed on Patch {}".format(patch_name))

pglyph.data_source.on_change('selected',callback_fcn)

curdoc().add_root(column(p))
like image 21
Karel Marik Avatar answered Nov 02 '22 17:11

Karel Marik


Since bokeh 0.13 the solution proposed by @Karel_Marik a needs small tweak:

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

TOOLS = ["tap"]
p = figure(title="Some Figure", tools=TOOLS)

source = ColumnDataSource(dict(
    x=[[1, 3, 2], [3, 4, 6, 6]],
    y=[[2, 1, 4], [4, 7, 8, 5]],
    name=['A', 'B'],color=["firebrick", "navy"],
    alpha=[0.8,0.3],line_width=[3,3]))

pglyph = p.patches('x', 'y', color="color", alpha="alpha",
                   line_width="line_width", source=source)

def callback(attr, old, new):
    # The index of the selected glyph is : new['1d']['indices'][0]
    selections = new['1d']['indices']
    print("Number of selections:{}".format(len(selections)))
    for index in selections:
        patch_name =  source.data['name'][index]
        print("TapTool callback executed on Patch {}".format(patch_name))

pglyph.data_source.on_change('selected',callback)

pglyph.selected.on_change('indices', callback)

curdoc().add_root(column(p))

I hope this helps somebody.

like image 1
vaxherra Avatar answered Nov 02 '22 15:11

vaxherra