Reputation: 357
I have a hexgraph displaying a heatmap for my data. For this I use a ColumnDataSource containing the x,y information, as well as the values and the color for this item. Addtionally I provide a color bar to the hex graph showing the information which value will lead to which color. This should work for different data sets, however I am not able to update the color bar according to min and high values during run time.
Here is an example code (the simple myapp example provided by bokeh) where each button press should add a color bar with different values:
from random import random
from bokeh.layouts import column
from bokeh.models import Button, LinearColorMapper, ColorBar, BasicTicker
from bokeh.palettes import RdYlBu3
from bokeh.plotting import figure, curdoc
from colorcet import CET_L18 as palette
# create a plot and style its properties
p = figure(x_range=(0, 100), y_range=(0, 100), toolbar_location=None)
p.border_fill_color = 'black'
p.background_fill_color = 'black'
p.outline_line_color = None
p.grid.grid_line_color = None
# add a text renderer to our plot (no data yet)
r = p.text(x=[], y=[], text=[], text_color=[], text_font_size="20pt",
text_baseline="middle", text_align="center")
i = 0
ds = r.data_source
# create a callback that will add a number in a random location
def callback():
global i
# BEST PRACTICE --- update .data in one step with a new dict
new_data = dict()
new_data['x'] = ds.data['x'] + [random()*70 + 15]
new_data['y'] = ds.data['y'] + [random()*70 + 15]
new_data['text_color'] = ds.data['text_color'] + [RdYlBu3[i%3]]
new_data['text'] = ds.data['text'] + [str(i)]
ds.data = new_data
color_mapper = LinearColorMapper(palette=palette, low=0, high=1000*i)
color_bar = ColorBar(color_mapper=color_mapper, ticker=BasicTicker(),
label_standoff=12, border_line_color=None, location=(0, 0), orientation="horizontal")
p.add_layout(color_bar, 'below')
i = i + 1
# add a button widget and configure with the call back
button = Button(label="Press Me")
button.on_click(callback)
#show(p)
# put the button and plot in a layout and add to the document
curdoc().add_root(column(button, p))
However, the color bar is not added during runtime. I also tried to add it before hand, then it will be displayed but not updated. How am I able to add a color bar and/or update it during runtime?
Upvotes: 0
Views: 709
Reputation: 38942
First, move setup of the layout outside of the callback for the button and have the callback only update data.
Next, use the linear_cmap
function from bokeh.transform
to construct the linear color map. This function gives you a transformer to get the color for a value in the color palette.
Finally, update the high
value of the transform in the callback
from random import random
from bokeh.layouts import column
from bokeh.models import Button, ColorBar, BasicTicker
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure, curdoc
from colorcet import CET_L18 as palette
from bokeh.transform import linear_cmap
# create a plot and style its properties
p = figure(x_range=(0, 100), y_range=(0, 100), toolbar_location=None)
p.border_fill_color = 'black'
p.background_fill_color = 'black'
p.outline_line_color = None
p.grid.grid_line_color = None
i = 0
color_mapper = linear_cmap(field_name='text', palette=palette, low=0, high=i)
color_bar = ColorBar(
color_mapper=color_mapper['transform'],
ticker=BasicTicker(),
label_standoff=12,
border_line_color='black',
location=(0, 0),
orientation="horizontal")
# add a text renderer to our plot (no data yet)
ds = ColumnDataSource(dict(x=[],y=[],text=[]))
r = p.text(x='x', y='y', text='text', text_color=color_mapper, text_font_size="20pt",
text_baseline="middle", text_align="center", source=ds)
p.add_layout(color_bar, 'below')
# create a callback that will add a number in a random location
def callback():
global i
i = i + 1
# BEST PRACTICE --- update .data in one step with a new dict
new_data = dict()
new_data['x'] = ds.data['x'] + [random()*70 + 15]
new_data['y'] = ds.data['y'] + [random()*70 + 15]
new_data['text'] = ds.data['text'] + [i]
ds.data = new_data
color_mapper['transform'].high = i
# add a button widget and configure with the call back
button = Button(label="Press Me")
button.on_click(callback)
#show(p)
# put the button and plot in a layout and add to the document
curdoc().add_root(column(button, p))
Upvotes: 1