Label annotations not hiding with glyphs

Hi, The problem I am having is labels still showing, overlapping with other labels and the BoxAnnotations, looking untidy, when series are hidden (legend.click_policy=“hide”).

I found a partial fix using

circ.js_on_change('visible', CustomJS(args=dict(ls=labels),
                                         code="ls.visible = cb_obj.visible;"))

but it seems that all the series have to be visible when the plot loads and it is inconvenient to click through the legend items to hide all but the one of interest. I tired combining with

 circ.visible=False
 line.visible=False

but with no success. Ideally I’d like the plot to display with no series showing to start with and no labels visible or overlapping.
Minimal code to replicate issue:

'''Data'''
import random
x_labels = ['A' ,'B' , 'C', 'D', 'E', 'F', 'G']
scores_dict = {
    'x' : x_labels
}
names = ['S'+str(x) for x in range(35)] 
for n in names:
    scores_dict[n] = [random.randint(1,9) for i in range(7)]

from bokeh.io import show, output_notebook
from bokeh.models import ColumnDataSource, LabelSet, Label, Band, BoxAnnotation, Span, CustomJS
from bokeh.transform import factor_cmap, linear_cmap
from bokeh.plotting import figure

source = ColumnDataSource(scores_dict)
palette = ['#C40936', '#000000', '#198BCA']
series = list (scores_dict.keys()) 
series.pop(0) #removes 'x'
p = figure(x_range=x_labels, title=' Scores ', plot_width=800, plot_height=900, tools='save')

for s in range (len (series)):
    mapper = linear_cmap(field_name=series[s], palette=palette ,low=0.5 ,high=9.5)
    line = p.line(x='x', y=series[s], line_color='grey', source=source, line_width=2, alpha=0.8, legend_label=series[s])
    circ = p.circle(x='x', y=series[s], line_color=mapper, color=mapper, source=source, size=26, alpha=0.8, legend_label=series[s])
    labels = LabelSet(x='x', y=series[s], text=series[s], level='overlay', text_font_size='10pt', text_font_style='bold',
                      text_align='center', text_color='white', x_offset=0, y_offset=-7, source=source, render_mode='canvas')
    p.add_layout(labels)
    
# with .visible active as below the labels are visible even when the glyphs are hidden, 
# you can see the values agains the BoxAnnotations.
#     circ.visible=False
#     line.visible=False

# with these .js_on_change active instead (they don't seem to work with  .visible=False  
# all series are unhidden and have to be hidden by clicking the legend entries, inconvenient   
    circ.js_on_change('visible', CustomJS(args=dict(ls=labels),
                                             code="ls.visible = cb_obj.visible;"))

    line.js_on_change('visible', CustomJS(args=dict(ls=labels),
                                            code="ls.visible = cb_obj.visible;"))

    
high_box = BoxAnnotation(bottom=3.5, fill_alpha=0.1, fill_color='red')
mid_box = BoxAnnotation(bottom=6.5, top=3.5, fill_alpha=0.1, fill_color='lightgrey')
low_box = BoxAnnotation(top=6.5, fill_alpha=0.1, fill_color='blue')
p.add_layout(low_box)
p.add_layout(mid_box)
p.add_layout(high_box)

divide1 = Span(location=3, dimension='height', line_color='black', line_dash='solid', line_width=1)
divide2 = Span(location=5, dimension='height', line_color='firebrick', line_dash='dashed', line_width=2)
p.add_layout(divide1)
p.add_layout(divide2)

'''x-axis settings'''
#p.xgrid.ticker = [1, 2, 3, 4, 5, 6,7]
'''y-axis settings'''
p.y_range.start = 9.5
p.y_range.end = 0.5
p.y_range.range_padding = 0
p.yaxis.visible = False
p.ygrid.grid_line_color = None
'''both axis settings'''
p.axis.major_tick_line_color = None
'''Legend setup'''
p.right.append(p.legend[0])
p.legend.click_policy="hide" 

show(p)

I don’t understand.

circ.visible=False
line.visible=False

Here you’re hiding lines and circles, but you’re skipping labels. And then you say that the labels are visible - well, that’s exactly because you didn’t hide them. Just add labels.visible = False.

Also, those js_on_change calls can be replaced with:

circ.js_link('visible', labels, 'visible')
line.js_link('visible', labels, 'visible')

To improve your code further - if you don’t really need a Label or LabelSet instance, you can just use the text glyph. It’s must more straightforward.
You can remove all those calls to js_on_change or js_link and just use

labels = p.text(x='x', y=series[s], text=series[s], text_font_size='10pt', text_font_style='bold', text_baseline='middle',
                text_align='center', text_color='white', source=source, legend_label=series[s])

The initial manual setting of visible = False is still needed. But you can move those to the list of glyph arguments, along with x, y, etc to save three lines.

Thanks for the quick response, appreciate it.
I want the labels to be visible for just the selected series, i.e. when glyphs are not hidden, but otherwise they should be hidden so they don’t overlap. labels.visible=False appears to hide all the labels all the time.

But, combined with the js_on_change/js_link it works. I was confusing myself. Thank you.

Yes, the p.text approach is an improvement and does the job well.