Is there any way to update BoxAnnotations dynamically in a callback of a widget?

I have been referring to this thread https://groups.google.com/a/continuum.io/forum/#!mydiscussions/bokeh/Exnb1oE6gz0. Similar to what’s been discussed there, I want to be able to update the BoxAnnotations on my plot dynamically. The BoxAnnotations are calculated on data based on the value of a slider, and for each value, it is possible that there will be different number of BoxAnnotations to be overlaid on the plot. Is it possible to remove the old BoxAnnotations and add new plots in slider callback?
I tried the example given in the link, however it seems that BoxAnnotation is not a renderer anymore. I am new to bokeh and haven’t really understood the different between renderers and references in a plot, please forgive my ignorance :sweat_smile:.

from bokeh.plotting import figure, curdoc
from bokeh.layouts import row
from bokeh.plotting import show, output_file
from bokeh.models import BoxAnnotation
TOOLS = "pan,wheel_zoom,box_zoom,reset,save"
x = [0,1,2,3]
y = [-10,220,40,90]
p = figure(x_axis_type="datetime", tools=TOOLS)
p.line(x=x, y=y, line_color="gray", line_width=1)
low_box = BoxAnnotation(top=80, fill_alpha=0.1, fill_color='red')
mid_box1 = BoxAnnotation(name='mid_box',bottom=80, top=90, fill_alpha=0.1, fill_color='green')
mid_box2 = BoxAnnotation(name='mid_box',bottom=100, top=100, fill_alpha=0.1, fill_color='green')
mid_box3 = BoxAnnotation(name='mid_box',bottom=110, top=120, fill_alpha=0.1, fill_color='green')
mid_box4 = BoxAnnotation(name='mid_box',bottom=120, top=130, fill_alpha=0.1, fill_color='green')
mid_box5 = BoxAnnotation(name='mid_box',bottom=130, top=140, fill_alpha=0.1, fill_color='green')
mid_box6 = BoxAnnotation(name='mid_box',bottom=140, top=150, fill_alpha=0.1, fill_color='green')
mid_box7 = BoxAnnotation(name='mid_box',bottom=150, top=160, fill_alpha=0.1, fill_color='green')
mid_box8 = BoxAnnotation(name='mid_box',bottom=160, top=170, fill_alpha=0.1, fill_color='green')
mid_box9 = BoxAnnotation(name='mid_box',bottom=170, top=180, fill_alpha=0.1, fill_color='green')
high_box = BoxAnnotation(bottom=180, fill_alpha=0.1, fill_color='red')
p.add_layout(low_box)
p.add_layout(mid_box1)
p.add_layout(mid_box2)
p.add_layout(mid_box3)
p.add_layout(mid_box4)
p.add_layout(mid_box5)
p.add_layout(mid_box6)
p.add_layout(mid_box7)
p.add_layout(mid_box8)
p.add_layout(mid_box9)
p.add_layout(high_box)
p.title.text = "Glucose Range"
p.xgrid[0].grid_line_color=None
p.ygrid[0].grid_line_alpha=0.5
p.xaxis.axis_label = 'Time'
p.yaxis.axis_label = 'Value'

remove_these = []
j = 0
# identify the items to delete
for i,r in enumerate(p.renderers):
    print("renderer loop identification phase" + str(type(r)))
    if r.name == 'mid_box':
        j = j+1
        remove_these.append(r)
# delete them
for j,r in enumerate(remove_these):
    print("renderer loop deleting phase" + str(type(r)))
    p.renderers.remove(remove_these[j])

The only output I get is :
renderer loop identification phase<class 'bokeh.models.renderers.GlyphRenderer'>

“renderer loop deleting phase” is never printed, and I checked the list of renderers as well, it only has a GlyphRenderer. I checked references and it has all the elements of the plot. Is it possible to use p.references() for my use-case then (I am using bokeh version 1.3.4)?

1 Like

I found that using Quad works very well here, it can be linked to a column data source, which suits my use case perfectly.

I am glad Quad satisfies your needs. If you need lots of boxes, that may in fact be the best option. Just for completeness, I’ll note that yes a BoxAnnotation can be updated from a Python or JS callback. The best practice is to update existing objects, not remove and re-create them, however. E.g.

mid_box1.top = new_top_value