How to change the extent and position of an existing image in bokeh? - 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)

Related

How can i update the ColumnDataSource by an "selected.on_change()" event?

First, I created a scatter plot out of geogr. coordinates. If i click on one of these circles a second line-plot next to that scatter plot shows further informations depending on what circle i've clicked. That means i have to update the current ColumnDataSource shown in the line-plot by a new one. But if i click on one of those circles the current Source will not be updated. The line-plot still shows the dataset of the old Source.
I'll try to give you a short example of what i've done so far:
def callback(attr, old, new):
# Depending on what circle i've clicked i start a SQL request
# to gain my dataset i want to plot and the new title of the diagram.
# To change the title actually works:
line_plot.title.text = 'new_title'
# "source_new_values" is a ColumnDataSource created out of a
# SQL-request of my database.
# To change the current source doesn't work. The line-plot is still
# showing the old dataset.
source_current_values = source_new_values
scatter_plot = figure(x_axis_label='lat', y_axis_label='lon')
scatter_plot.circle(x='long', y='lat', source=source_coordinates)
# I use the indices to identify what circle was clicked.
source_coordinates.selected.on_change('indices', callback)
line_plot = figure(x_axis_label='time', x_axis_type='datetime',
y_axis_label='values', title='title')
line_plot.line(x='date', y='value', source=source_current_values)
The solution for tat Problem is I'm not able to update the source by a ColumnDataSource, but by a Dictionary using:
source_current_values.data = Dict("some content")

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.

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.

how to make the plot, but not open a new tab in bokeh?

Here is what I do to make plots with bokeh
p1=make_plot(df)
output_file("out.html", title="out example")
show(p1)
This always open a new tab to refresh the plot, but I do not want that. It's enough to just refresh a tab with previous version of the plot.
How can I stop show() to open a new tab and just make the new plot?
I have tried the option broweser=None or broweser='' but that does not work.
Use save instead of show in the last step. This will save the plot instead of opening it in browser.
from bokeh.io import save
p1=make_plot(df)
output_file("out.html", title="out example")
save(p1)
You are looking for save(), not show()
from bokeh.io import output_file,show,save
from bokeh.plotting import figure
output_file('out.html',title='out example')
fig = figure()
show(fig) # will pop up in the browser
a = raw_input() # just press any key to continue
fig.line(range(10),range(10))
save(fig) # you can refresh your browser tab to see the change

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