Hi,
I’m trying to embed an interactive bokeh model into a web app, which is designed to show a 3d array given filename.
The bokeh model is showing different slices (by bokeh plotting.image) of a 3d array controlled by a slider. Codes are listed In the end.
It can be partially done with the help of bokeh server if I use bokeh serve directly. However my app is using a flask backend with Angular frontend. From the examples of bokeh, I found an example to embed a bokeh app into a flask server, but there are still some problems.
My questions are:
-
What’s the best practice to implement this task. Is flask + bokeh server a good choice?
-
Can I use flask server instead of tornado?
-
In the code list below, data is preload by “np.load(“data3d.npy”)”, however in real case, it should be a response to some http request like “http://localhost:port/api/show3d/filename.npy”, how can we use bokeh server behind flask server to response to some http request? And is it possible to use one bokeh server to serve multiple users opening different files?
Best regards,
Hong Xiang
···
from random import random
from bokeh.layouts import column, gridplot
from bokeh.palettes import RdYlBu3
from bokeh.plotting import figure, curdoc
from bokeh.layouts import row, widgetbox
from bokeh.models.widgets import Slider, TextInput
import numpy as np
Load data
data3d = np.load(‘data3d.npy’)
x_range = (-3, 3)
y_range = (-3, 3)
z_range = (-3, 3)
x_show = 50
y_show = 50
z_show = 50
dw = 6
dh = 6
x0 = -3
y0 = -3
image from different views
p1 = figure(x_range=x_range, y_range=y_range)
r1 = p1.image(image=[data3d[:, :, z_show]], x=x0, y=y0, dw=dw, dh=dh, palette=“Spectral11”)
p2 = figure(x_range=p1.x_range, y_range=z_range)
r2 = p2.image(image=[data3d[:, y_show, :]], x=x0, y=y0, dw=dw, dh=dh, palette=“Spectral11”)
p3 = figure(x_range=p1.y_range, y_range=p2.y_range)
r3 = p3.image(image=[data3d[x_show, :, :]], x=x0, y=y0, dw=dw, dh=dh, palette=“Spectral11”)
p = gridplot([[p1, p2, p3]])
xind = Slider(title=“x”, value=0.0, start=-3.0, end=3.0, step=6.0/100)
yind = Slider(title=“y”, value=0.0, start=-3.0, end=3.0, step=6.0/100)
zind = Slider(title=“z”, value=0.0, start=-3.0, end=3.0, step=6.0/100)
r1.glyph.color_mapper.high = 1.0
r1.glyph.color_mapper.low = 0.0
r2.glyph.color_mapper.high = 1.0
r2.glyph.color_mapper.low = 0.0
r3.glyph.color_mapper.high = 1.0
r3.glyph.color_mapper.low = 0.0
ds1 = r1.data_source
ds2 = r2.data_source
ds3 = r3.data_source
def callback(attrname, old, new):
ix = min(max(int((xind.value + 3.0)/(6.0/100)), 0), 99)
iy = min(max(int((yind.value + 3.0)/(6.0/100)), 0), 99)
iz = min(max(int((zind.value + 3.0)/(6.0/100)), 0), 99)
print(ix, iy, iz)
ds1.data = {'image': [data3d[ix, :, :]]}
ds2.data = {'image': [data3d[:, iy, :]]}
ds3.data = {'image': [data3d[:, :, iz]]}
for w in [xind, yind, zind]:
w.on_change('value', callback)
inputs = widgetbox(xind, yind, zind)
curdoc().add_root(row(inputs, p, width=800))