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?
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))
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))
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With