How to execute callback only when the Bokeh slider is released - bokeh

I have a slider that affects a line in a plot:
vline = Span(location=0, dimension='height')
plot.renderers.extend([vline])
callback = CustomJS(args=dict(vline=vline), code="vline.location = slider.value;")
slider = Slider(start=-5, end=5, value=0, step=.1, callback=callback)
callback.args["slider"] = slider
I would like to, beyond changing the line, also execute an operation, call it commit_line(), via JS, that POSTs the value (and later updates another plot).
I could make the callback above call commit_line(), but that is unsuitable because it will make a couple hundred calls to the server just by sliding the slider.
In UX, this is typically addressed by executing only the expensive operation on release (of the slider). Can this be achieved in Bokeh sliders? If yes, how?

UPDATE for the current Bokeh v2.3.0 version: You should use:
JS callback:
from bokeh.models import CustomJS, Slider
from bokeh.plotting import show
slider = Slider(start=0, end=10, value=5)
slider.js_on_change('value_throttled', CustomJS(code='console.log(this.value)'))
show(slider)
Python callback:
from bokeh.models import Slider
from bokeh.plotting import curdoc
slider = Slider(start=0, end=10, value=5)
slider.on_change('value_throttled', lambda attr, old, new: print(new))
curdoc().add_root(slider)
Please note that the answer below was given for an older Bokeh version
and doesn't apply anymore for the current Bokeh v2.3.0
Pass callback_policy = "mouseup" parameter to your Slider constructor.
So:
slider = Slider(start = 1,
end = 10,
value = 1,
step = 1,
callback_policy = 'mouseup')
It comes handy when consulting Bokeh documentation to expand the JSON Prototype to find out which attributes a method actually supports, many methods are namely inherited from the base classes. Please note that JSON Prototype refers to the BokehJS model so it is not guaranteed you find all those properties in the DOM model when inspecting the code e.g. in Google Chrome Developers Tools.

In Bokeh 2.2.0, try using the "value_throttled" property:
self.date_range.on_change("value_throttled", callback)
This is working for me for a DateRangeSlider - would expect similar behaviour from other Sliders based on inheritance hierarchy.

Related

Bokeh EditTools callback to server

I am trying to understand how to use callbacks for the new Bokeh EditTools (e.g. BoxEditTool or similar).
Specifically, I would like to see on the server side the coordinates of the newly added rectangles, but I am not sure how to do this.
I am running the following server app
def app( curdoc ):
TOOLS = "tap"
p = figure(title="Some Figure", tools=TOOLS)
source = ColumnDataSource( {'xs':[1], 'ys':[1], 'width':[.1],'height':[.1]})
r = p.rect('xs','ys','width','height', source=source)
p.add_tools(BoxEditTool( renderers = [r]))
def cb( attr, old, new ):
print(r.data_source.data)
r.data_source.on_change("selected", cb)
curdoc.add_root(column(p))
I do get printout from the cb when I select different rectangles, but the r.data_source.data does not change
Thanks for the help!
The behaviour you're describing is actually a bug in the current distribution of Bokeh (0.13.0). You can read more in the google groups discussion. To summarize, there was a problem with the synchronization of the data at the server, it has been resolved and merged.
Note that the on_change method for the Rect glyph ColumnDataSource should watch the 'data' attribute and not 'selected'.
Other than that your snippet looks good, but if you want a working example you can look here. This code is under development but at this stage it reads images and allows drawing ROIs, as well as a simple mechanism for serializing and loading them.
Hope this helps!

Can I draw an annotation in bokeh with data from a ColumnDataSource?

I'd like to draw a vertical line on my Bokeh plot which gets moved around by javascript in the browser at runtime. (It's a timebar that marks the current time on a time series plot.)
For drawing a static vertical line, I'm using:
from bokeh.models import Span
timebar = Span(location=where_I_want_the_timebar, dimension=height)
my_figure.add_layout(timebar)
In order to enable the interactivity, I think I need to get the location from a ColumnDataSource. However, I can't figure out how to do that, because Span does not accept a source argument.
Alternatively, is there another way for me to move the timebar at runtime?
I found a workaround. In python:
from bokeh.models import Span
timebar = Span(name='timebar' location=where_I_want_the_timebar, dimension=height)
my_figure.add_layout(timebar)
Then in javascript in the browser:
let timebar = Bokeh.documents[0].get_model_by_name('timebar')
timebar.attributes.location = my_new_desired_location
timebar.change.emit()
If someone posts a way to use a ColumnDataSource I will accept that answer.

RangeSlider unexpeted attribute Bokeh

I am trying to use the RangeSlider within my Bokeh-application, initializing the object as so
from bokeh.models.widgets import RangeSlider
#RangeSlider
slider = RangeSlider(title="OAS slider", start=0, end=1000, value=(0,2000),
step=0.1)
When trying to compile the app I get the following error message:
AttributeError: unexpected attribute 'value' to RangeSlider, possible attributes are
callback, callback_policy, callback_throttle, css_classes, disabled, end,
height, js_callbacks, name, orientation, range, sizing_mode, start, step,
tags, title or width
I could change the code to use range instead as so
#RangeSlider altered inputs
slider = RangeSlider(title="OAS slider", start=0, end=1000, range=(0,2000),
step=0.1)
and it works.
However, as the example on the Bokeh-homepage found here
https://github.com/bokeh/bokeh/blob/master/examples/app/export_csv/main.py
used the value and works, I would be more eager to understand why I get the error message, rather than just changing to range... The example in the link also used the key word format, which obviously also generates the error above.
Could be a version issue. I am running Bokeh version 0.12.4
Thanks
This is a version issue. The RangeSlider was actually mostly broken for a long time, until we switched to a different underlying slider library to implement it. But some changes were necessary to make the switch. The correct property for 0.12.7 and newer is value and I would recommend to use that version or later if you want too use RangeSlider especially (0.12.4 is over a year old.)

How to change the extent and position of an existing image in bokeh?

I am displaying 2d data as images of varying shapes in a bokeh server, and therefore need to dynamically update not only the image's data source, but also its dw, dh, x, and y properties. In the dummy example below, these changes are made in a callback function which is connected to a Button widget.
I've figured out that I need to access the glyph attribute of the image's GlyphRenderer object, and I can do so through its update() method (see code). But the changes don't take effect until I click the toolbar's Reset button. I've noticed that the changes also mysteriously take effect the second time I activate the callback() function. What is the proper way to make these changes?
import bokeh.plotting
import bokeh.models
import bokeh.layouts
import numpy as np
# set up the interface
fig1 = bokeh.plotting.figure(x_range=(0, 10), y_range=(0, 10))
im1 = fig1.image([], dw=5, dh=5)
button = bokeh.models.Button(label='scramble')
# add everything to the document
bokeh.plotting.curdoc().add_root(bokeh.layouts.column(button, fig1))
# define a callback and connect it
def callback():
# this always works:
im1.data_source.data = {'image': [np.random.random((100,100))]}
# these changes only take effect after pressing the "Reset"
# button, or after triggering this callback function twice:
im1.glyph.update(x=1, y=1, dw=9, dh=9)
button.on_click(callback)
I don't immediately see why you code isn't work. I can suggest explicitly using a ColumnDataSource and linking all of the Image glyph properties to columns in that source. Then you should be able to update the source.data in a single line and have all of the updates apply.
Here's some incomplete sample code to suggest how to do that:
from bokeh.models import Image, ColumnDataSource
from bokeh.plotting import figure
# the plotting code
plot = figure()
source = ColumnDataSource(data=dict(image=[], x=[], y=[], dw=[], dh=[]))
image = Image(data='image', x='x', y='y', dw='dw', dh=dh)
plot.add_glyph(source, glyph=image)
# the callback
def callback():
source.data = {'image': [np.random.random((100,100))], 'x':[1], 'y':[1], 'dw':[9], 'dh':[9]}
button.on_click(callback)

Unable to set bokeh plotting parameters using BOKEH + HOLOVIEW plotting option via Python

Is there any documentation specifying how to pass Bokeh parameters via holoview?
I am reading the tutorials but I think there is something small I have missed.
There is an example online which describes this in Ipython but I am trying to do it via a python WITHOUT Ipython notebook.
http://holoviews.org/Tutorials/Bokeh_Backend.html?highlight=bokeh
When I run this program I get the curves but the color does not change and I also get this error: WARNING:root:Curve01537: Setting non-parameter attribute style={'line_color': 'green'} using a mechanism intended only for parameters
How can we set the parameter?
Code Example here
from pprint import pprint, pformat
import holoviews as hv
import numpy as np
import pathlib, os
import webbrowser
import lasio, las
from holoviews import Store
from holoviews.plotting.bokeh.element import (line_properties, fill_properties, text_properties)
def plot_bokeh(plot):
#Create renderer instance
myrenderer = hv.Store.renderers['bokeh'].instance(fig='html')
out_file_name = "".join(["./OUTPUT/","gyro", "_graph.html"])
with open (out_file_name, 'w') as f:
#Plot static html
f.write (myrenderer.static_html(plot))
f.close()
webbrowser.open_new_tab(pathlib.Path(os.path.abspath(out_file_name)).as_uri())
def holoview_sandbox():
curve_opts = dict(line_color='green')
xs = np.linspace(0, np.pi*4, 100)
data = (xs, np.sin(xs))
holo_plot = hv.Curve(data, label='MY LABEL' , style=curve_opts)
plot_bokeh(holo_plot)
if __name__ == '__main__':
holoview_sandbox()
In HoloViews the options aren't bound to the objects themselves, which has various benefits including being able to plot with different backends. The pure-Python way of setting style options is this:
curve_opts = dict(line_color='green')
xs = np.linspace(0, np.pi*4, 100)
data = (xs, np.sin(xs))
holo_plot = hv.Curve(data, label='MY LABEL')(style=curve_opts)
The Options Tutorial describes how to set options like this, but please let us know if you found some of that unclear.
This syntax works as well
holo_plot.opts(style={'color': 'green'})
When you change the entry 'line_color' to 'color' in the dict() of Philipp's answer, then this works for the matplotlib backend as well.
Details about setting options can also be found here in addition to Philipp's link.

Resources