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

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.

Related

How to execute callback only when the Bokeh slider is released

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.

Axis_type="mercator" in bokeh

I'm trying to run the chart given as example for Tile Provider Maps on the Bokeh geo-mapping site.
https://docs.bokeh.org/en/latest/docs/user_guide/geo.html.
from bokeh.plotting import figure, show, output_file
from bokeh.tile_providers import CARTODBPOSITRON
output_file("tile.html")
# range bounds supplied in web mercator coordinates
p = figure(x_range=(-2000000, 6000000), y_range=(-1000000, 7000000),
x_axis_type="mercator", y_axis_type="mercator")
p.add_tile(CARTODBPOSITRON)
show(p)
But instead of the expected chart I'm getting:
ValueError: expected an element of either Auto or Enum('linear', 'log', 'datetime'), got 'mercator'
If I delete the x_axis_type paremeters (x_axis_type="mercator", y_axis_type="mercator") I'm getting the chart, though as expected, without latitude and longitute labels. Is there a problem in the script or am I doing something wrong here?
Your version of Bokeh is too old, you will have to update to at least version 0.12.15 to use this feature

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!

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)

matplotlib - write TeX on Qt form

I'd like to add some TeX text to my Qt form, like label - just text, no graph, no lines, no borders, just TeX. I thought something like this: render TeX to bitmap and then place that bitmap on form, e.g. into QLabel. Or even better - use some backend, add it to form and use something tex_label.print_tex(<tex code>). Seems matplotplot has TeX parsers, but I can't figure out how to use them...
Thanks.
As a variant:
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
# Get window background color
bg = self.palette().window().color()
cl = (bg.redF(), bg.greenF(), bg.blueF())
# Create figure, using window bg color
self.fig = Figure(edgecolor=cl, facecolor=cl)
# Add FigureCanvasQTAgg widget to form
self.canvas = FigureCanvasQTAgg(self.fig)
self.tex_label_placeholder.layout().addWidget(self.canvas)
# Clear figure
self.fig.clear()
# Set figure title
self.fig.suptitle('$TeX$',
x=0.0, y=0.5,
horizontalalignment='left',
verticalalignment='center')
self.canvas.draw()

Resources