
I am trying to update the ColorBar to reflect a change in the data being displayed when the data is updated through a Select widget . I have associated log_cmap mapper to the glyph and the color bar but when updating .color_mapper.low / high the color bar update is not triggered (seems to be triggered next time the data is updated but then based on the old data). For comparison I also have a color bar with linear_cmap mapper, and this seems to update as expected when the on_change callback is executed. Not sure if I am updating the color bar incorrectly?
The example below got a Select widget for updating data, and another widget for choosing the mapper to use to color the data. When choosing to update the data the CDS is updated with new data and the callback for updating colorbar is called.
Running Bokeh 2.3.0, FireFox 78.9, RedHat 7.
#!/usr/bin/env python
import numpy as np
from bokeh.models import ColorBar, ColumnDataSource, NumeralTickFormatter
from bokeh.transform import linear_cmap, log_cmap
from bokeh.io import curdoc
from bokeh.plotting import figure
from bokeh.layouts import row, column
from bokeh.models.widgets import Select, Div
from bokeh.palettes import Spectral5, Reds5
src = ColumnDataSource(data = {'x': [], 'y': [], 'p1': [], 'p2': []})
MAPPER = {
'linear': linear_cmap(field_name = 'p1', palette = Spectral5, low = 1, high = 6),
'log': log_cmap(field_name = 'p2', palette = Reds5, low = 1, high = 1000),
}
def create_fig(mappers, title):
p = figure(plot_width = 700, title = title, toolbar_location = 'above', match_aspect = True)
p.min_border_left = 80
p.circle(
x = 'x', y = 'y', line_color = None,
color = mappers[map_select.value],
size=10, source=src, name = 'glyph'
)
p.xaxis[0].formatter = NumeralTickFormatter(format = '0')
p.yaxis[0].formatter = NumeralTickFormatter(format = '0')
for k,v in mappers.items():
color_bar = ColorBar(color_mapper=v['transform'], width=8, name = 'color_bar_'+k)
color_bar.visible = True
p.add_layout(color_bar, 'right')
if k != map_select.value:
color_bar.visible = False
return p
def color_bar_vis(attr, old, new):
# callback for mapper select
# hide and show correct colorbar
cbar = plot.select(name = 'color_bar_'+old)
cbar.visible = False
cbar = plot.select(name = 'color_bar_'+new)
cbar.visible = True
# pick new mapper
new_map = MAPPER[new]
if new == 'linear':
data_obj = src.data['p1']
else:
data_obj = src.data['p2']
# update low and high values of mapper
low = min(data_obj)
high = max(data_obj)
# update div widget with low/high of data to be mapped
txt = 'Low: {:.4f}, high: {:.4f}'.format(low, high)
info_text.text = txt
new_map['transform'].low = low
new_map['transform'].high = high
cbar.color_mapper.low = low
cbar.color_mapper.high = high
r = plot.select(name = 'glyph')
r.glyph.fill_color = dict(field = new_map['field'], transform = new_map['transform'])
def cb_data_select(attr, old, new):
# callback for data factor select
data_val = float(new)
new_data = np.random.rand(4,70)
x = new_data[0]*300+2000
y = new_data[1]*300+3000
p1 = new_data[2]*data_val / 2.0
p2 = new_data[3]*data_val
src.data = {'x': x, 'y': y, 'p1': p1, 'p2': p2}
# update color mapper and colorbar to updated data
# force update - assigning .value might not trigger callback
color_bar_vis('value', map_select.value, map_select.value)
map_select = Select(
title = 'Select mapper',
options = list(MAPPER.keys()),
value = list(MAPPER.keys())[0],
width = 300
)
map_select.on_change('value', color_bar_vis)
plot = create_fig(MAPPER, 'Mapper from widget')
data_select = Select(
title = 'Select data scaling factor',
options = ['2', '50'],
)
data_select.on_change('value', cb_data_select)
info_text = Div(text = '', width = 300)
wdg_col = column(data_select, map_select, info_text)
data_select.value = '2'
curdoc().add_root(row(wdg_col, plot))



