The best way to embed interactive image into Flask/Angular app?

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:

  1. What’s the best practice to implement this task. Is flask + bokeh server a good choice?

  2. Can I use flask server instead of tornado?

  3. 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))


Hi, Hong Xiang,

Have you found a good solution to your problem? I’m faced with a similar situation here.

Best,
Qinpeng

···

On Saturday, August 19, 2017 at 8:47:01 PM UTC-5, Xiang Hong wrote:

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:

  1. What’s the best practice to implement this task. Is flask + bokeh server a good choice?
  1. Can I use flask server instead of tornado?
  1. 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))


Hi,

there are still some problems

What are the problems? In your questions, you don’t specify any.

As of your questions:

  1. The provided use case is too narrow to judge about best practices. That being said, Flask+Bokeh should work just fine.
  2. Instead - no, only together since Bokeh is built on top of Tornado. Another possibility is to replace Flask with Tornado in your app. But that’s up to you to decide.
  3. You can create a Flask (or any other web server) handler that handles the required kind of URL and sets all required parameters to a new Bokeh document.
    Yes, it is possible to use one Bokeh server to serve multiple users with different data. Please refer to Bokeh server — Bokeh 3.3.2 Documentation for more details.
    Regards,

Eugene

···

On Sunday, August 20, 2017 at 8:47:01 AM UTC+7, Xiang Hong wrote:

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:

  1. What’s the best practice to implement this task. Is flask + bokeh server a good choice?
  1. Can I use flask server instead of tornado?
  1. 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))


Hi, Qinpeng,

Currently I’m using a ‘dirty’ solution:

As a summary, I use bokeh.embed.components to generate html and js for one image(or several images), and return these html/js strings to Angular.

Since this solution only requires normal python function calls, it is easy to combine it with Flask.

But it is NOT elegant at all.

It seems to be inefficient, there are many redundant repeated js if we use multiple images.

And it is hard to use many bokeh functionalities like widgets.

I’m still looking forward to get a better solution.

Code demo is listed below:

···

On Sat, Oct 14, 2017 at 2:18 AM, [email protected] wrote:

Hi, Hong Xiang,

Have you found a good solution to your problem? I’m faced with a similar situation here.

Best,
Qinpeng

On Saturday, August 19, 2017 at 8:47:01 PM UTC-5, Xiang Hong wrote:

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:

  1. What’s the best practice to implement this task. Is flask + bokeh server a good choice?
  1. Can I use flask server instead of tornado?
  1. 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))


Best regards,

Hong Xiang