Dynamically updating multiple 2D numpy arrays in Image Glyph

Here’s a basic example showing how to use columndatasource to accomplish this:

import numpy as np

from bokeh.plotting import figure, show
from bokeh.layouts import layout
from bokeh.models import Image, ColumnDataSource, Slider, CustomJS

#dummy data taken from https://docs.bokeh.org/en/2.4.0/docs/gallery/image.html
N = 500
x = np.linspace(0, 10, N)
y = np.linspace(0, 10, N)
xx, yy = np.meshgrid(x, y)
#d1 = first image
d1 = np.sin(xx)*np.cos(yy)
#make a second image
d2 = np.sin(xx**2)*np.cos(yy**2)

p = figure(tooltips=[("x", "$x"), ("y", "$y"), ("value", "@im")])
p.x_range.range_padding = p.y_range.range_padding = 0

#initialize a column datasource and assign first image into it
src = ColumnDataSource(data={'x':[0],'y':[0],'dw':[10],'dh':[10],'im':[d1]})
#create the image randerer pointing to the field names in src, and the source itself
im_rend = p.image(image='im', x='x', y='y', dw='dw', dh='dh', palette="Spectral11", level="image",source=src)

p.grid.grid_line_width = 0.5

#a widget to put a callback on
sl = Slider(start=0,end=1,value=0,step=1,width=100)

#the key here is to pass a dictionary to the callback all the information you need to UPDATE the columndatasource that's driving the renderer
# imdict is basically this --> if slider value is 0, i want to get d1, if slider value is 1, i want to get d2
cb = CustomJS(args=dict(src=src,imdict={0:d1,1:d2},sl=sl)
              ,code='''
              //assign the im field in the datasource the 2D image associated with the slider value
              src.data['im'] = [imdict[sl.value]]
              src.change.emit()
              ''')
sl.js_on_change('value',cb)
lo = layout([p,sl])
show(lo)

example_imcds

Now this example only changes the values in the image array, but you could easily expand out imdict to update the other field values in the columndatasource (i.e. the width/height etc.) by just making expanding out imdict to something like:

imdict = {0:{'x':[0],'y':[0],'dw':[10],'dh':[10],'im':[d1]}
          ,1:{'x':[3],'y':[2],'dw':[50],'dh':[50],'im':[d2]}}

Then in the callback instead of updating only the ‘im’ field in src, update the columndatasource data entirely:

src.data = imdict[sl.value]

Hope this helps!

2 Likes