Updating Image Glyphs with 2D numpy arrays in Bokeh 3.2.1

Attempted to reproduce the code in this previous post: https://discourse.bokeh.org/t/dynamically-updating-multiple-2d-numpy-arrays-in-image-glyph/8799

Expected Result: Switching between two images with a slider

Actual Result: Initial plot is produced, but slider callback seems to be failing. Looking at the console logs, the following error is produced:

 bokeh-3.2.1.min.js:521 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'dimension')
    at p._set_data (bokeh-3.2.1.min.js:521:1833)
    at p.set_data (bokeh-3.2.1.min.js:376:4575)
    at S.set_data (bokeh-3.2.1.min.js:372:4161)
    at S.update_data (bokeh-3.2.1.min.js:372:4006)
    at S.t (bokeh-3.2.1.min.js:372:2250)
    at S.i (bokeh-3.2.1.min.js:219:229)
    at c.emit (bokeh-3.2.1.min.js:180:757)
    at c.emit (bokeh-3.2.1.min.js:180:842)
    at d.eval (eval at _compile_function (bokeh-3.2.1.min.js:462:890), <anonymous>:7:26)
    at d.<anonymous> (bokeh-3.2.1.min.js:462:961)

Was there a change in bokeh caused this example to no longer work?

Hello Gingtastical and welcome to Bokeh discourse.

The input argument imdict={0:d1,1:d2} of CustomJS seems to make problems with newer Bokeh versions.
I changed it to a list:

cb = CustomJS(args=dict(src=src,imlist=[d1,d2],sl=sl),
              code='''
              src.data['im'] = [imlist[sl.value]]
              src.change.emit()
              ''')

and for me, it works.

Here the complete code:

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,imlist=[d1,d2],sl=sl),
              code='''
              src.data['im'] = [imlist[sl.value]]
              src.change.emit()
              ''')
sl.js_on_change('value',cb)
lo = layout([p,sl])
show(lo)

I don’t think that is expected, so a GitHub Issue would be appropriate.

@Bryan You are right, working with dictionaries in CustomJS should also work.
The use of a dictionary in the CustomJS arguments actually works, the dictionary then becomes a Map object in Javascript.
But calling a value of the map (former dictionary) with dict[key] in Javascript doesn’t seem to work anymore.
Instead, we can use dict.get(key).

So the initial Javascript code with dictionary will also work with the line:
src.data['im'] = [imdict.get(sl.value)]
instead of:
src.data['im'] = [imdict[sl.value]]

1 Like

Ah, right, I forgot about the recent move to more defined types on the BokehJS side of things.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.