I having a problem with the callback, I got everything worked expect the part when the graph doesn't update even thou the array is updated when I change the slider.
import numpy as np
from bokeh.io import curdoc
from bokeh.layouts import row, widgetbox
from bokeh.models import ColumnDataSource, Slider
from bokeh.plotting import figure
data = {'x_values': [0,0,2,2,4,4],
'y_values': [10,0,0,5,5,10]} #Seting up data
source = ColumnDataSource(data=data) # Map plot
plot = figure(title="Step Well",
tools="save,wheel_zoom")
plot.line('x_values', 'y_values',source=source)
def update_data(attrname, old, new):
Step = StepHeight.value
x = [0,0,2,2,4,4]
y = [10,0,0,Step,Step,10]
source.data = ColumnDataSource(dict(x=x, y=y))
source.on_change('value', update_data)
StepHeight = Slider(title="Step Height",
value=4.0,
start=2.0, end=6.0, step=0.2)
# Set up layouts and add to document
inputs = widgetbox(StepHeight)
layout = row(inputs, plot)
curdoc().title = "Sliders"
curdoc().add_root(layout)
You were trying to make source.data a column data source but source should be the column data source. Source.data is just a dictionary. I changed some things in your code and it should work fine now.
import numpy as np
from bokeh.io import curdoc
from bokeh.layouts import row, widgetbox
from bokeh.models import ColumnDataSource, Slider
from bokeh.plotting import figure
data = {'x_values': [0,0,2,2,4,4],
'y_values': [10,0,0,5,5,10]} #Seting up data
source = ColumnDataSource(data=data) # Map plot
plot = figure(title="Step Well",
tools="save,wheel_zoom")
plot.line('x_values', 'y_values',source=source)
def update_data(attrname, old, new):
y = [10,0,0,new,new,10]
source.data['y_values'] = y
StepHeight = Slider(title="Step Height",
value=4.0,
start=2.0, end=6.0, step=0.2)
StepHeight.on_change('value', update_data)
# Set up layouts and add to document
inputs = widgetbox(StepHeight)
layout = row(inputs, plot)
curdoc().title = "Sliders"
curdoc().add_root(layout)
Related
I need to use the values we receive from Bokeh widgets to run some code and plot newly generated data. I cannot modify the data inside javascript that goes into CustomJS as there not available the libraries that are available in python. I understand that I can use bokeh server and run python callbacks, but this has to happen in a Databricks notebook. Is this possible?
As I cannot expose the code that I am working on, I prepared a sample code that hopefully conveys what I am trying to do.
import numpy as np
from bokeh.io import curdoc, show
from bokeh.models import ColumnDataSource, Grid, LinearAxis, Patches, Plot, Rect
from bokeh.embed import file_html
from bokeh.plotting import figure, output_file, show
from bokeh.resources import CDN
x = [x*0.005 for x in range(0, 200)]
y = x
source = ColumnDataSource(data=dict(x=x, y=y))
button_cds = ColumnDataSource(data=dict(button_val=[1]))
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(button_cds = button_cds), code="""
var data = button_cds.data;
var f = cb_obj.value;
data['button_val'] = f;
button_cds.change.emit();
""")
slider = Slider(start=0.1, end=4, value=1, step=.1, title="power")
slider.js_on_change('value', callback)
for i in range(len(source.data['x'])):
source.data['y'][i] = x[i]**button_cds.data['button_val'][0]
layout = column(slider, plot)
displayHTML(file_html(layout, CDN))
Please note that I can do modifying the source.data in CustomJS, but I have to use python code.
I have 2 bokeh rows. The top row contains a DataTable and a TextInput, both of which are able to stretch_width in order to fit the width of the browser. The bottom row contains a gridplot, which is able to stretch_width, but only does so by distorting the scale of the image. Ideally, I would like the gridplot to update the amount of columns displayed based on the size of the browser.
Consider the following example:
import pandas as pd
from bokeh.models.widgets import DataTable, TableColumn
from bokeh.models import ColumnDataSource, TextInput
from bokeh.plotting import figure, output_file, save
from bokeh.layouts import row, column, gridplot
def get_datatable():
"""this can stretch width without issue"""
df = pd.DataFrame({'a': [0, 1, 2], 'b': [2, 3, 4]})
source = ColumnDataSource(df)
Columns = [TableColumn(field=i, title=i) for i in df.columns]
data_table = DataTable(columns=Columns, source=source, sizing_mode='stretch_width', max_width=9999)
return data_table
def get_text_input():
"""this can stretch width without issue"""
return TextInput(value='Example', title='Title', sizing_mode="stretch_width", max_width=9999)
def get_gridplot():
"""
this requires columns to be hard-coded
stretch_width is an option, but only distorts the images if enabled
"""
figs = []
for _ in range(30):
fig = figure(x_range=(0,10), y_range=(0,10))
_ = fig.image_rgba(image=[], x=0, y=0)
figs.append(fig)
return gridplot(children=figs, ncols=2)
top_row = row([get_datatable(), get_text_input()], max_width=9999, sizing_mode='stretch_width')
bottom_row = row(get_gridplot())
col = column(top_row, bottom_row, sizing_mode="stretch_width")
output_file("example.html")
save(col)
My end goal is to have the gridplot automatically update the amount of columns based on the width of the browser. Is there a way to do this natively in bokeh? If not, is it possible to do this via a CustomJs javascript callback?
Solution
Consider using sizing_mode=“scale_width” when calling figure.
fig = figure(x_range=(0,10), y_range=(0,10), sizing_mode=“scale_width”)
Note
It may be preferable to use scale_width instead of stretch_width more generally.
Bokeh Doc Example: https://docs.bokeh.org/en/latest/docs/user_guide/layout.html#multiple-objects
I'm trying to generate a bokeh application which allows the user to change the glyphs of a plot. Unfortunately, the glyphs don't change after calling on_change() method of the dropdown button although I'm able to change the axis label in a similar way. However, changing plot.glyph outside of the called function works fine.
from bokeh.layouts import layout
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import Dropdown
from bokeh.plotting import figure
from bokeh.io import curdoc
from bokeh.models.markers import Cross, Circle
source=ColumnDataSource(dict(x=[1,2,3],y=[4,5,6]))
fig=figure()
plot=fig.circle(x='x', y='y',source=source)
fig.xaxis.axis_label='This is a label'
#this changes the glyphs from circle to cross
plot.glyph=Cross(x='x', y='y', size=20, line_color='firebrick',
fill_color='firebrick', line_alpha=0.8, fill_alpha=0.3)
def update_plot(attr,old,new):
if dropdown.value=='cross':
#this changes the axis label but not the glyphs
fig.xaxis.axis_label='Label changed'
plot.glyph=Cross(x='x', y='y', size=20, line_color='firebrick',
fill_color='firebrick', line_alpha=0.8, fill_alpha=0.3)
elif dropdown.value=='circle':
#this also only changes the axis label but not the glyphs
fig.xaxis.axis_label='Label changed again'
plot.glyph=Circle(x='x', y='y', size=20, line_color='firebrick',
fill_color='firebrick', line_alpha=0.8, fill_alpha=0.3)
menu=[('Circle','circle'),('Cross', 'cross')]
dropdown=Dropdown(label="Select marker", menu=menu, value='circle')
dropdown.on_change('value', update_plot)
lay_out=layout([fig, dropdown])
curdoc().add_root(lay_out)
I'm not sure about the exact reasons why your approach doesn't work, but this one works:
from bokeh.io import curdoc
from bokeh.layouts import layout
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import Dropdown
from bokeh.plotting import figure
source = ColumnDataSource(dict(x=[1, 2, 3], y=[4, 5, 6]))
plot = figure()
renderers = {rn: getattr(plot, rn)(x='x', y='y', source=source,
**extra, visible=False)
for rn, extra in [('circle', dict(size=10)),
('line', dict()),
('cross', dict(size=10)),
('triangle', dict(size=15))]}
def label_fn(item):
return 'Select marker ({})'.format(item)
menu = [('No renderer', None)]
menu.extend((rn.capitalize(), rn) for rn in renderers)
dropdown = Dropdown(label=label_fn(None), menu=menu, value=None)
def update_plot(attr, old, new):
dropdown.label = label_fn(new)
for renderer_name, renderer in renderers.items():
renderer.visible = (renderer_name == new)
dropdown.on_change('value', update_plot)
lay_out = layout([plot, dropdown])
curdoc().add_root(lay_out)
Basically, I create all necessary renderers beforehand, and then just switch visible flag of each one.
Also, note the correct terminology. What you're calling a plot, is not actually a plot but a glyph renderer. And plot and figure are basically the same thing.
I have got a pandas dataframe whose columns I want to show as lines in a plot using a Bokeh server. Additionally, I would like to have a slider for shifting one of the lines against the other.
My problem is the update functionality when the slider value changes. I have tried the code from the sliders-example of bokeh, but it does not work.
Here is an example
import pandas as pd
from bokeh.io import vform
from bokeh.plotting import Figure, output_file, show
from bokeh.models import CustomJS, ColumnDataSource, Slider
df = pd.DataFrame([[1,2,3],[3,4,5]])
df = df.transpose()
myindex = list(df.index.values)
mysource = ColumnDataSource(df)
plot = Figure(plot_width=400, plot_height=400)
for i in range(len(mysource.column_names) - 1):
name = mysource.column_names[i]
plot.line(x = myindex, y = str(name), source = mysource)
offset = Slider(title="offset", value=0.0, start=-1.0, end=1.0, step=1)
def update_data(attrname, old, new):
# Get the current slider values
a = offset.value
temp = df[1].shift(a)
#to finish#
offset.on_change('value', update_data)
layout = vform(offset, plot)
show(layout)
Inside the update_data-function I have to update mysource, but I cannot figure out how to do that. Can anybody point me in the right direction?
Give this a try... change a=offset.value to a=cb_obj.get('value')
Then put source.trigger('change') after you do whatever it is you are trying to do in that update_data function instead of offset.on_change('value', update_data).
Also change offset = Slider(title="offset", value=0.0, start=-1.0, end=1.0, step=1, callback=CustomJS.from_py_func(offset))
Note this format I'm using works with flexx installed. https://github.com/zoofio/flexx if you have Python 3.5 you'll have to download the zip file, extract, and type python setup.py install as it isn't posted yet compiled for this version...
Does anyone have an example of how to run python code in jupyter by clicking a button in Bokeh?
UPDATE The original answer was very out of date. The answer has been updated to reflect changes since Bokeh 0.11 which was released in January of 2016.
A complete example pared from the sliders demo, that uses features from Bokeh 0.12.4:
from numpy import linspace, pi, sin
from bokeh.io import curdoc
from bokeh.layouts import row, widgetbox
from bokeh.models import ColumnDataSource, Slider
from bokeh.plotting import figure
# Set up data
x = linspace(0, 4*pi, 200)
y = sin(x)
source = ColumnDataSource(data=dict(x=x, y=y))
# Set up plot
plot = figure(x_range=(0, 4*pi), y_range=(-2.5, 2.5))
plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)
# Set up widgets
amplitude = Slider(title="amplitude", value=1.0, start=-5.0, end=5.0)
freq = Slider(title="frequency", value=1.0, start=0.1, end=5.1)
# Set up callbacks
def update(attrname, old, new):
# Get the current slider values
a = amplitude.value
k = freq.value
# Update the data for the new curve
source.data = dict(x=x, y=a*sin(k*x))
amplitude.on_change('value', update)
freq.on_change('value', update)
# Set up layout and add to document
inputs = widgetbox(amplitude, freq)
curdoc().add_root(row(inputs, plot, width=1200))
Run with bokeh serve --show <filename> and get the following responsive web app in your browser: