Improving glyph toggling method

I am trying to improve the method of which I am toggling the visibility of glyphs. I am working with Bokeh objects in a Panel app so forgive me if this is the wrong place to post this question, but I believe the knowledge base here is capable of helping me out.

What I am trying to do is use a MultiSelect widget to toggle the visibility of glyphs on multiple figures. This could also be done with an interactive legend, but I am currently looking for other solutions that will allow the legend to only expand as needed. My current method is to make a dict to keep track of the glyphs and legend items of each figure. Then when the MultiSelect value changes it will loop through each option, glyph, and legend item and set the appropriate visibility one at a time. This method seems to work fine on smaller datasets and with fewer figures, but does not seem very modular as things expand. There are three main issues that I am having and unfortunately I was not able to preproduce all of them in my simple example.

  1. Speed: This is mainly a preference, but as the number of glyphs increases the looping operation becomes more noticeable. I would prefer to not see each glyph update one at a time, but rather them all change at the same time.

  2. Legend issues: I have noticed that as I click between options sometimes the legend does not stay to the right of the plot as I have specified. Below is an image showing what I mean where the top figure has the legend where I want it to be while the bottom is overlapping the plot.

  3. [bokeh] could not set initial ranges: This is my main concern and also the one that I have not been able to reproduce in my example. I get this warning in the console log when I do not have the first option selected. This also seems to induce legend issues as seen in the previous problem. Reading through the forums here it seems like this warning is caused when the visibility of all of the glyphs in a figure are turned off. This would explain why I would get the warning when I do not have the first option selected.

I believe what could help me here would be by somehow vectorizing the operation instead of looping through all of the possibilities. Even if used a loop to get a boolean list that would allow me to change the visibility of every glyph at once I think would solve my issue. Is it possible to do something like this?

Below is my simple example. Thank you for any help!

import panel as pn
import pandas as pd
import numpy as np
from bokeh.plotting import figure
from bokeh.layouts import gridplot
from bokeh.models import Legend, ColumnDataSource
from bokeh.palettes import Category10

pn.extension()

# List of glyph names
glyph_list = ['A','B','C','D','E','F','G','H']

def makeFig(title):
    # Configure figure
    fig = figure(title=title, width=300, height=200)
    fig.add_layout(Legend(label_text_font_size = '8pt',glyph_height = 10,
                          spacing =-4),'right')

    # Create dicts used to reference glyph and legend item visbility
    glpyh_dict = {}
    legend_dict = {}
    # loop through list to create glyphs
    for i in range(len(glyph_list)):
        df = pd.DataFrame()
        df['x'] = np.random.randint(0,10000,5000)
        df['y'] = np.random.randint(0,10000,5000)
        source = ColumnDataSource(df)
        glpyh_dict[glyph_list[i]] = fig.line(x='x', y='y', source=source, 
                                            legend_label=glyph_list[i], 
                                            color=Category10[10][i])
        legend_dict[glyph_list[i]] = fig.legend.items[-1]
    return fig, glpyh_dict, legend_dict

fig_1,fig_1_glyph,fig_1_legend = makeFig('Figure 1')
fig_2,fig_2_glyph,fig_2_legend = makeFig('Figure 2')
fig_3,fig_3_glyph,fig_3_legend = makeFig('Figure 3')
fig_4,fig_4_glyph,fig_4_legend = makeFig('Figure 4')

# Create MultiSelect widget for glyph selection
multiSelect = pn.widgets.MultiSelect(options=glyph_list,value=glyph_list,
                                     width=100,height = 200,name='Glyph Selection:')

# Function to update glyphs based on selection
def selectGlyph(event):
    for option in multiSelect.options:
        if option in multiSelect.value:
            fig_1_glyph[option].visible = True 
            fig_1_legend[option].visible = True
            fig_2_glyph[option].visible = True 
            fig_2_legend[option].visible = True
            fig_3_glyph[option].visible = True 
            fig_3_legend[option].visible = True
            fig_4_glyph[option].visible = True 
            fig_4_legend[option].visible = True
        else:
            fig_1_glyph[option].visible = False 
            fig_1_legend[option].visible = False
            fig_2_glyph[option].visible = False 
            fig_2_legend[option].visible = False
            fig_3_glyph[option].visible = False 
            fig_3_legend[option].visible = False
            fig_4_glyph[option].visible = False 
            fig_4_legend[option].visible = False

# Update glyphs on MultiSelect value change           
multiSelect.param.watch(selectGlyph, 'value')

    
figGrid = gridplot([[fig_1,fig_2],[fig_3,fig_4]])

pn.Row(multiSelect,figGrid).servable()

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