Bokeh/Python: CustomJS to modify column layout - bokeh

I’m generating a Bokeh report which uses tabs, sometimes I can get a lot of these and navigating the document becomes really cumbersome. Luckily the plots have some attributes which could be used to group some plots together. So I was trying to implement a way of filtering the number of visible tabs based on these attributes. I was pretty much successful on sketching a solution with bokeh server but my end solution would need to implement a CustomJS callback since I need to distribute the html report. I’m kind of lost since I’m not familiar with how to implement CustomJS callbacks, or even if what I’m trying to achieve is even possible without bokeh server. I tried to implement a CustomJS based on other people posts but so far I've been unsuccesfull.
My main objective would be to substitute the ‘change_plot’ callback with a CustomJS callback, if anyone has a pointer of how this could be possible I’d greatly appreciate some help.
I’m providing a minimal example of my script below. Any help or pointers would be much appreciated.
Bokeh Server version of what I'm trying to achieve:
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, Tabs, Panel, Dropdown, PreText
from bokeh.plotting import figure, curdoc
#Initialize variables
nplots = 6 # Number of plots
ngroup = 4 # Number of plots assigned to first group
# Definition of report structure
groups = [f'Quad' if i < ngroup else f'Linear' for i in range(nplots)] # Arbitrary grouping of plots
tabnames = [f'Title_{i}' for i in range(nplots)] # Individual plot names
# Creates list of unique groups without modifying first appearance order
cnt = 0
unq_grp = []
original_groups = groups[:]
while len(groups):
cnt = cnt + 1
unq_grp.append(groups[0])
groups = list(filter(lambda group: group != groups[0], groups))
if cnt > len(groups):
break
# Data Variables
x = [None]*nplots
y = [None]*nplots
# Plot Variables
fig = [None]*nplots
source = [None]*nplots
# Generates figures with plots from data with custom process
for i in range(nplots):
x[i] = [x[i] for x[i] in range(0, 10)]
if i < ngroup:
y[i] = [(i*n)**2 for n in x[i]]
else:
y[i] = [(i*n) for n in x[i]]
source[i] = ColumnDataSource(data=dict(x=x[i], y=y[i]))
fig[i] = figure()
fig[i].line('x', 'y', source=source[i], line_width=3, line_alpha=0.6)
# Callback to change Plot and Plot Title
def change_plot(attr, old, new):
index = int(new.split(',')[0])
group = int(new.split(',')[1])
title[group].text = f'Plot: {subgroup[group][index][0]}'
col[group].children[2] = fig[index]
subgroup = [None]*len(unq_grp) #List of tuples ('plot_name', ['tabname_index','unique_group_index'])
menu = [None]*len(unq_grp) #List that populates dropdown menu
group_dd = [None]*len(unq_grp) #Placeholder for dropdown GUI elements
tab = [None]*len(unq_grp) #Placeholder for tab GUI elements
title = [None]*len(unq_grp) #Placeholder for title GUI elements
col = [None]*len(unq_grp) #Placeholder for column GUI elements
# Cycle through each unique group
for i, group in enumerate(unq_grp):
# Filter the figures correspondig to current group
subgroup[i] = [(tabnames[j],str(f'{j},{i}')) if original_group == group else None for j, original_group in enumerate(original_groups)]
# Populates the dropdown menu
menu[i] = list(filter(None,subgroup[i]))
# Reference default figure index (first in the menu)
default = int(menu[i][0][1].split(',')[0])
# Creates GUI/Report elements
group_dd[i] = Dropdown(label = "Select Group", button_type = "default", menu=menu[i])
title[i] = PreText(text=f'Plot: {menu[i][0][0]}', width=200)
col[i] = column([group_dd[i],title[i],fig[default]])
# Listens to callback event
group_dd[i].on_change('value', change_plot)
# Creates tabs
tab[i] = Panel(child = col[i], title = group)
out_tabs = Tabs(tabs = tab)
curdoc().title = "Plotting Tool"
curdoc().add_root(out_tabs)
Standalone Report (my code so far...)
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, Tabs, Panel, Dropdown, PreText, CustomJS
from bokeh.plotting import figure, output_file, show
#Initialize variables
nplots = 6 # Number of plots
ngroup = 4 # Number of plots assigned to first group
# Definition of report structure
groups = [f'Quad' if i < ngroup else f'Linear' for i in range(nplots)] # Arbitrary grouping of plots
tabnames = [f'Title_{i}' for i in range(nplots)] # Individual plot names
output_file("tabs.html")
# Creates list of unique groups without modifying first appearance order
cnt = 0
unq_grp = []
original_groups = groups[:]
while len(groups):
cnt = cnt + 1
unq_grp.append(groups[0])
groups = list(filter(lambda group: group != groups[0], groups))
if cnt > len(groups):
break
# Data Variables
x = [None]*nplots
y = [None]*nplots
# Plot Variables
fig = [None]*nplots
source = [None]*nplots
# Generates figures with plots from data with custom process
for i in range(nplots):
x[i] = [x[i] for x[i] in range(0, 10)]
if i < ngroup:
y[i] = [(i*n)**2 for n in x[i]]
else:
y[i] = [(i*n) for n in x[i]]
source[i] = ColumnDataSource(data=dict(x=x[i], y=y[i]))
fig[i] = figure()
fig[i].line('x', 'y', source=source[i], line_width=3, line_alpha=0.6)
figcol = column(fig)
output_file("tabs.html")
subgroup = [None]*len(unq_grp) #List of tuples ('plot_name', ['tabname_index','unique_group_index'])
menu = [None]*len(unq_grp) #List that populates dropdown menu
group_dd = [None]*len(unq_grp) #Placeholder for dropdown GUI elements
tab = [None]*len(unq_grp) #Placeholder for tab GUI elements
title = [None]*len(unq_grp) #Placeholder for title GUI elements
col = [None]*len(unq_grp) #Placeholder for column GUI elements
cjs = [None]*len(unq_grp) #Placeholder for column GUI elements
# Cycle through each unique group
for i, group in enumerate(unq_grp):
# Filter the figures correspondig to current group
subgroup[i] = [(tabnames[j],str(f'{j},{i}')) if original_group == group else None for j, original_group in enumerate(original_groups)]
# Populates the dropdown menu
menu[i] = list(filter(None,subgroup[i]))
# Reference default figure index (first in the menu)
default = int(menu[i][0][1].split(',')[0])
# Creates GUI/Report elements
group_dd[i] = Dropdown(label = "Select Group", button_type = "default", menu=menu[i])
col[i] = column([group_dd[i],fig[default]])
cjs[i] = CustomJS(args=dict(col=col[i], select=group_dd[i], allfigs=figcol), code="""
// Split the index
var dd_val = (select.value)
var valARR = dd_val.split(',')
var index = parseInt(valARR[0])
// replace with appropiate figure?
col.children[1] = allfigs.children[index]
// send new column, maybe?
col.change.emit();
""")
# Listens to callback event
group_dd[i].js_on_change('value',cjs[i])
# Creates tabs
tab[i] = Panel(child = col[i], title = group)
out_tabs = Tabs(tabs = tab)
show(out_tabs)

You can show all the figures in a column layout, and show/hide the figure by setting the visible property.
Here is an example:
import numpy as np
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, Tabs, Panel, Select, PreText, CustomJS
from bokeh.plotting import figure, output_file, show
output_file("tabs.html")
x = np.linspace(0, 10)
def create_figure(x, y, label):
source = ColumnDataSource(data=dict(x=x, y=y))
fig = figure(name=label)
fig.line('x', 'y', source=source, line_width=3, line_alpha=0.6)
return fig
def create_select(figs, title="Select Group"):
names = [fig.name for fig in figs]
drop = Select(title=title, value=names[0], options=names)
for fig in figs[1:]:
fig.visible = False
callback = CustomJS(args=dict(figs=figs), code="""
let selected = cb_obj.value;
for(let fig of figs){
fig.visible = fig.name == selected;
}
""")
drop.js_on_change("value", callback)
return [drop] + figs
quad_figs = [create_figure(x, (i * x)**2, f"Quad {i}") for i in range(3)]
linear_figs = [create_figure(x, (i * x), f"Linear {i}") for i in range(3)]
tabs = Tabs(tabs=[
Panel(child=column(create_select(quad_figs)), title="Quad"),
Panel(child=column(create_select(linear_figs)), title="Linear")
])
show(tabs)

Related

How to use the format parameter of sliders?

Sliders have format property, see
https://docs.bokeh.org/en/latest/docs/reference/models/widgets.sliders.html
A) Where is the documentation for this property?
B) Is there an example of using the format attribute?
EDIT: is there a way to pass a function that takes the slider value and returns a string?
Formatting documentation can be found on this page with multiple examples. The sliders value can be used by calling slider.value.
I also edited an example where I added a formatter for the amplitude slider. The slider values in this example are used to change the sine wave.
You can run this example by using this command: bokeh serve script.py --show
import numpy as np
from bokeh.io import curdoc
from bokeh.layouts import row, column
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import Slider, TextInput
from bokeh.plotting import figure
# Set up data
N = 200
x = np.linspace(0, 4*np.pi, N)
y = np.sin(x)
source = ColumnDataSource(data=dict(x=x, y=y))
# Set up plot
plot = figure(plot_height=400, plot_width=400, title="my sine wave",
tools="crosshair,pan,reset,save,wheel_zoom",
x_range=[0, 4*np.pi], y_range=[-2.5, 2.5])
plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)
# Set up widgets
text = TextInput(title="title", value='my sine wave')
offset = Slider(title="offset", value=0.0, start=-5.0, end=5.0, step=0.1)
amplitude = Slider(title="amplitude", value=1.0, start=-5.0, end=5.0, step=0.0000001, format='0.000f') #Slider with different formatting
phase = Slider(title="phase", value=0.0, start=0.0, end=2*np.pi)
freq = Slider(title="frequency", value=1.0, start=0.1, end=5.1, step=0.1)
# Set up callbacks
def update_title(attrname, old, new):
plot.title.text = text.value
text.on_change('value', update_title)
def update_data(attrname, old, new):
# Get the current slider values
a = amplitude.value
b = offset.value
w = phase.value
k = freq.value
# Generate the new curve
x = np.linspace(0, 4*np.pi, N)
y = a*np.sin(k*x + w) + b
source.data = dict(x=x, y=y)
for w in [offset, amplitude, phase, freq]:
w.on_change('value', update_data)
# Set up layouts and add to document
inputs = column(text, offset, amplitude, phase, freq)
curdoc().add_root(row(inputs, plot, width=800))
curdoc().title = "Sliders"

bokeh selected.on_change not working for my current setup

Basically, this is an interactive heatmap but the twist is that the source is updated by reading values from a file that gets updated regularly.
dont bother about the class "generator", it is just for keeping data and it runs regularly threaded
make sure a file named "Server_dump.txt" exists in the same directory of the script with a single number greater than 0 inside before u execute the bokeh script.
what basically happens is i change a number inside the file named "Server_dump.txt" by using echo 4 > Server_dump.txt on bash,
u can put any number other than 4 and the script automatically checks the file and plots the new point.
if u don't use bash, u could use a text editor , replace the number and save, and all will be the same.
the run function inside the generator class is the one which checks if this file was modified , reads the number, transforms it into x& y coords and increments the number of taps associated with these coords and gives the source x,y,taps values based on that number.
well that function works fine and each time i echo a number , the correct rectangle is plotted but,
now I want to add the functionality of that clicking on a certain rectangle triggers a callback to plot a second graph based on the coords of the clicked rectangle but i can't even get it to trigger even though i have tried other examples with selected.on_change in them and they worked fine.
*if i increase self.taps for a certain rect by writing the number to the file multiple times, color gets updated but if i hover over the rect it shows me the past values and not the latest value only .
my bokeh version is 1.0.4
from functools import partial
from random import random,randint
import threading
import time
from tornado import gen
from os.path import getmtime
from math import pi
import pandas as pd
from random import randint, random
from bokeh.io import show
from bokeh.models import LinearColorMapper, BasicTicker, widgets, PrintfTickFormatter, ColorBar, ColumnDataSource, FactorRange
from bokeh.plotting import figure, curdoc
from bokeh.layouts import row, column, gridplot
source = ColumnDataSource(data=dict(x=[], y=[], taps=[]))
doc = curdoc()
#sloppy data receiving function to change data to a plottable shape
class generator(threading.Thread):
def __init__(self):
super(generator, self).__init__()
self.chart_coords = {'x':[],'y':[],'taps':[]}
self.Pi_coords = {}
self.coord = 0
self.pos = 0
self.col = 0
self.row = 0
self.s = 0
self.t = 0
def chart_dict_gen(self,row, col):
self.col = col
self.row = row+1
self.chart_coords['x'] = [i for i in range(1,cla.row)]
self.chart_coords['y'] = [i for i in range(cla.col, 0, -1)] #reversed list because chart requires that
self.chart_coords['taps']= [0]*(row * col)
self.taps = [[0 for y in range(col)] for x in range(row)]
def Pi_dict_gen(self,row,col):
key = 1
for x in range(1,row):
for y in range(1,col):
self.Pi_coords[key] = (x,y)
key = key + 1
def Pi_to_chart(self,N):
x,y = self.Pi_coords[N][0], self.Pi_coords[N][1]
return x,y
def run(self):
while True:
if(self.t == 0):
self.t=1
continue
time.sleep(0.1)
h = getmtime("Server_dump.txt")
if self.s != h:
self.s = h
with open('Server_dump.txt') as f:
m = next(f)
y,x = self.Pi_to_chart(int(m))
self.taps[x][y] += 1
# but update the document from callback
doc.add_next_tick_callback(partial(update, x=x, y=y, taps=self.taps[x][y]))
cla = generator()
cla.chart_dict_gen(15,15)
cla.Pi_dict_gen(15, 15)
x = cla.chart_coords['x']
y = cla.chart_coords['y']
taps = cla.chart_coords['taps']
#gen.coroutine
def update(x, y, taps):
taps += taps
print(x,y,taps)
source.stream(dict(x=[x], y=[y], taps=[taps]))
colors = ["#CCEBFF","#B2E0FF","#99D6FF","#80CCFF","#66c2FF","#4DB8FF","#33ADFF","#19A3FF", "#0099FF", "#008AE6", "#007ACC","#006BB2", "#005C99", "#004C80", "#003D66", "#002E4C", "#001F33", "#000F1A", "#000000"]
mapper = LinearColorMapper(palette=colors, low= 0, high= 15) #low = min(cla.chart_coords['taps']) high = max(cla.chart_coords['taps'])
TOOLS = "hover,save,pan,box_zoom,reset,wheel_zoom"
p = figure(title="Tou",
x_range=list(map(str,x)),
y_range=list(map(str,reversed(y))),
x_axis_location="above",
plot_width=900, plot_height=400,
tools=TOOLS, toolbar_location='below',
tooltips=[('coords', '#y #x'), ('taps', '#taps%')])
p.grid.grid_line_color = "#ffffff"
p.axis.axis_line_color = "#ef4723"
p.axis.major_tick_line_color = "#af0a36"
p.axis.major_label_text_font_size = "7pt"
p.xgrid.grid_line_color = None
p.ygrid.grid_line_color = None
p.rect(x="x", y="y",
width=0.9, height=0.9,
source=source,
fill_color={'field': 'taps', 'transform': mapper},
line_color = "#ffffff",
)
color_bar = ColorBar(color_mapper=mapper,
major_label_text_font_size="7pt",
ticker=BasicTicker(desired_num_ticks=len(colors)),
formatter=PrintfTickFormatter(format="%d%%"),
label_standoff=6, border_line_color=None, location=(0, 0))
curdoc().theme = 'dark_minimal'
def ck(attr, old, new):
print('here') #doesn't even print hi in the terminal if i click anywhere
source.selected.on_change('indices', ck)
p.add_layout(color_bar, 'right')
doc.add_root(p)
thread = cla
thread.start()
i wanted even to get a printed hi in the terminal but nothing
You have not actually added any selection tool at all to your plot, so no selection is ever made. You have specified:
TOOLS = "hover,save,pan,box_zoom,reset,wheel_zoom"
Those are the only tools that will be added, and none of them make selections, there for nothing will cause source.selection.indices to ever be updated. If you are looking for selections based on tap, you must add a TapTool, e.g. with
TOOLS = "hover,save,pan,box_zoom,reset,wheel_zoom,tap"
Note that there will not be repeated callbacks if you tap the same rect multiple times. The callback only fires when the selection changes and clicking the same glyph twice in a row results in an identical selection.

Change tooltip values based on Holoviews dropdown

I am trying to dynamically modify my y-axis tick formatting and tooltip formatting based on what is selected in a Holoviews dropdown. I figured I could do this in finalize_hooks. Since I don't know how to test for what has been selected in the dropdown, I used the title value to determine that. This seems to work ok though I am sure there could be a more elegant solution that I am not aware of. Also, I am able to change the tick formatter but the hover value doesn't change based on the above method. See example code below. Tooltip always shows Value1, never Value 2 no matter which country I select. Please advise if there is a way to fix this.
%%opts Bars [show_grid=True width=1400 height=400 xrotation=0] {+framewise}
macro_df = pd.read_csv('http://assets.holoviews.org/macro.csv', '\t')
key_dimensions = [('year', 'Year'), ('country', 'Country')]
value_dimensions = [('unem', 'Unemployment'), ('capmob', 'Capital Mobility'),
('gdp', 'GDP Growth'), ('trade', 'Trade')]
macro = hv.Table(macro_df, key_dimensions, value_dimensions)
hover = HoverTool(tooltips=[('year', '#year'),
('Value', '#unem{0.000%}')])
def apply_formatter(plot, element):
p = plot.handles['plot']
if 'Austria' in p.title.text:
plot.handles['yaxis'].formatter = NumeralTickFormatter(format="0")
p.hover[0].tooltips[1] = ('Value1', '#unem{0.0%}')
else:
plot.handles['yaxis'].formatter = NumeralTickFormatter(format="0.0%")
p.hover[0].tooltips[1] = ('Value2', '#unem{0.00%}')
bars = macro.to(hv.Bars, kdims='year', vdims=['unem']).opts(plot=dict(tools=[hover], finalize_hooks=[apply_formatter]))
bars
This seems to work
from bokeh.models import NumeralTickFormatter
from bokeh.models import HoverTool
macro_df = pd.read_csv('http://assets.holoviews.org/macro.csv', '\t')
key_dimensions = [('year', 'Year'), ('country', 'Country')]
value_dimensions = [('unem', 'Unemployment'), ('capmob', 'Capital Mobility'),
('gdp', 'GDP Growth'), ('trade', 'Trade')]
macro = hv.Table(macro_df, key_dimensions, value_dimensions)
def apply_formatter(plot, element):
p = plot.state
global x
x = p
if 'Austria' in p.title.text:
plot.handles['yaxis'].formatter = NumeralTickFormatter(format="0")
hover = HoverTool(tooltips=[('year', '#year'),
('Value', '#unem{0%}')])
p.tools = [hover]
else:
plot.handles['yaxis'].formatter = NumeralTickFormatter(format="0.0%")
hover = HoverTool(tooltips=[('year', '#year'),
('Value', '#unem{0.00%}')])
p.tools = [hover]
bars = macro.to(hv.Bars, kdims='year', vdims=['unem']).options(
tools=[], finalize_hooks=[apply_formatter])
bars

push_notebook does not update bokeh chart

It is kind of a complex example, but I desperately hope to get help...
I'm using jupyter-notebook 5.2.0, bokeh version is 0.12.9 and ipywidgets is 7.0.1.
Here is my DataFrame df:
import numpy as np
import pandas as pd
import datetime
import string
start = int(datetime.datetime(2017,1,1).strftime("%s"))
end = int(datetime.datetime(2017,12,31).strftime("%s"))
# set parameters of DataFrame df for simualtion
size, numcats = 100,10
rints = np.random.randint(start, end + 1, size = size)
df = pd.DataFrame(rints, columns = ['zeit'])
df["bytes"] = np.random.randint(5,20,size=size)
df["attr1"] = np.random.randint(5,100,size=size)
df["ind"] = ["{}{}".format(i,j) for i in string.ascii_uppercase for j in string.ascii_uppercase][:len(df)]
choices = list(string.ascii_uppercase)[:numcats]
df['who']= np.random.choice(choices, len(df))
df["zeit"] = pd.to_datetime(df["zeit"], unit='s')
df.zeit = df.zeit.dt.date
df.sort_values('zeit', inplace = True)
df = df.reset_index(drop=True)
df.head(3)
Now, let's create a bar plot, also using hover tool:
from bokeh.io import show, output_notebook, push_notebook
from bokeh.models import ColumnDataSource, HoverTool
from bokeh.plotting import figure
import ipywidgets as widgets
output_notebook()
# setup figure
hover = HoverTool(tooltips=[
("index", "$index"),
("ind", "#ind"),
("who", "#who"),
("bytes", "#bytes"),
("attr1", "#attr1"),
])
fig = figure(x_range=list(df.ind), plot_height=250, title="Test Bars",
toolbar_location=None, tools=[hover])
x = fig.vbar(x="ind", top="bytes", width=0.9, source=ColumnDataSource(df))
h=show(fig, notebook_handle=True)
I'm using a ipywidgets.widgets.SelectionRangeSlider to select a range of dates:
import ipywidgets as widgets
# create slider
dates = list(pd.date_range(df.zeit.min(), df.zeit.max(), freq='D'))
options = [(i.strftime('%d.%m.%Y'), i) for i in dates]
index = (0, len(dates)-1)
myslider = widgets.SelectionRangeSlider(
options = options,
index = index,
description = 'Test',
orientation = 'horizontal',
layout={'width': '500px'}
)
def update_source(df, start, end):
x = df[(df.zeit >= start) & (df.zeit < end)]
#data = pd.DataFrame(x.groupby('who')['bytes'].sum())
#data.sort_values(by="bytes", inplace=True)
#data.reset_index(inplace=True)
#return data
return x
def gui(model, bars):
def myupdate(control1):
start = control1[0].date()
end = control1[1].date()
#display(update_source(model, start, end).head(4))
data = update_source(model, start, end)
return myupdate
widgets.interactive(gui(df, x), control1 = myslider)
The problem is, I can't get an update to the graph from the widget:
x.data_source = ColumnDataSource(update_source(df, myslider.value[0].date(), myslider.value[1].date()))
push_notebook(handle=h)
At least, it does something with the plot, as hover is not working anymore...
What am I missing? Or is this a bug?
Thanks for any help
Markus
Figured out how to do it using bokeh: https://github.com/bokeh/bokeh/issues/7082, but unfortunately it only works sometimes...
Best to use CDSViewer.

Bokeh: textInput on_change function on widget doesn't work as expected

my short script looks like the following:
output_server('ts_sample.html')
count = 0
def update_title(attrname, old, new):
global count
count = count + 1
textInput = TextInput(title="query_parameters", name='fcp_chp_id', value='fcp_chp_id')
textInput.on_change('value', update_title)
curdoc().add_root(textInput)
p = figure( width=800, height=650,title="ts_sample",x_axis_label='datetime' )
p.line(np.array(data['date_trunc'].values, dtype=np.datetime64), data['latitude'], legend="test")
p.xaxis[0].formatter=bkmodels.formatters.DatetimeTickFormatter(formats=dict(hours=["%F %T"]))
show(curdoc())
It works, when bokeh server(bokeh serve) is running and I got the plotting, but on_change callback doesn't work as expected.
Assumed the value of textInput should be the content/string in the input box, but I changed it multiple times but the callback function update_title is never called (the count global variable is always 0). So apparently the underlying textInput.value is not changed, how can I change value attr and trigger the on_change function ?
Here's a simple TextInput example using a callback rather than .on_change(). This might be more helpful for beginners like me than the OP. I very slightly modified the slider example from
http://docs.bokeh.org/en/latest/docs/user_guide/interaction/callbacks.html#customjs-for-model-property-events
from bokeh.layouts import column
from bokeh.models import CustomJS, ColumnDataSource, Slider
from bokeh.models import TextInput
from bokeh.plotting import figure, show
x = [x*0.005 for x in range(0, 200)]
y = x
source = ColumnDataSource(data=dict(x=x, y=y))
plot = figure(plot_width=400, plot_height=400)
plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)
callback = CustomJS(args=dict(source=source), code="""
var data = source.get('data');
var f = cb_obj.get('value')
x = data['x']
y = data['y']
for (i = 0; i < x.length; i++) {
y[i] = Math.pow(x[i], f)
}
source.trigger('change');
""")
#slider = Slider(start=0.1, end=4, value=1, step=.1, title="power", callback=callback)
#layout = vform(slider, plot)
text_input = TextInput(value="1", title="power", callback=callback)
layout = column(text_input, plot)
show(layout)
I have the same problem as you .
After search, the on_change function not working with bokeh 0.10 realease but with the upcoming version 0.11 .
From : https://groups.google.com/a/continuum.io/forum/#!topic/bokeh/MyztWSef4tI
If you are using the (new) Bokeh server in the latest dev builds, you can follow this example, for instance:
https://github.com/bokeh/bokeh/blob/master/examples/app/sliders.py
From : https://groups.google.com/a/continuum.io/forum/#!topic/bokeh/PryxrZPX2QQ
The server has recently been completely re-written from the ground up
and is faster, smaller/simpler, and much easier to use and deploy and
explain. The major PR was just merged into master, and will show up in
the upcoming 0.11 release in December
For download the dev version : https://anaconda.org/bokeh/bokeh/files
I adapt the example, it may be helpful:
from bokeh.layouts import column
from bokeh.models import TextInput
from bokeh.models import CustomJS, ColumnDataSource, Slider
from bokeh.plotting import figure, show
x = [x*0.005 for x in range(0, 200)]
y = x
source = ColumnDataSource(data=dict(x=x, y=y))
plot = figure(plot_width=400, plot_height=400)
plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)
callback = CustomJS(args=dict(source=source), code="""
var data = source.data;
var f = cb_obj.value;
x = data['x']
y = data['y']
for (i = 0; i < x.length; i++) {
y[i] = Math.pow(x[i], f)
}
source.change.emit();
""")
#slider = Slider(start=0.1, end=4, value=1, step=.1, title="power", callback=callback)
#layout = vform(slider, plot)
text_input = TextInput(value="1", title="power", callback=callback)
layout = column(text_input, plot)
show(layout)

Resources