Bokeh Server callback from tools

南笙酒味 提交于 2020-01-21 03:23:08

问题


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?


回答1:


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))



回答2:


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))



回答3:


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.



来源:https://stackoverflow.com/questions/42234751/bokeh-server-callback-from-tools

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!