Search code examples
javascriptpythonbokeh

Hide several lines using Checkboxes and CustomJS in Python Bokeh


Let say I have 2 different lines:

df[0]=fig.line(x = 'x', y = 'y',..., name = 'toto0',source=s0)
df[1]=fig.line(x = 'x', y = 'y',..., name = 'toto1',source=s1)

If I want to have to possibility to hide them, I use this piece of code:

checkbox = CheckboxGroup(labels=['toto0','toto1'], active=2, width=100)
callback = CustomJS(args=dict(l0=df[0],l1=df[1],checkbox=checkbox),
code="""
l0.visible = 0 in checkbox.active;
l1.visible = 1 in checkbox.active;
""")
checkbox.js_on_change('active', callback)
layout = row(fig,checkbox)
show(layout)

Now let's say I have 20 different lines. How to proceed to compact the code below ?

callback = CustomJS(args=dict(l0=df[0],...,l19=df[19],checkbox=checkbox),
code="""
l0.visible = 0 in checkbox.active;
l1.visible = 1 in checkbox.active;
...
l19.visible = 19 in checkbox.active;
""")

This is a Python and a JavaScript question ... thanks !


Solution

  • The main idea is to collect all line renderes in a list and pass this list to the CustomJS. There you can loop over this list again and apply your changes.

    Minimal Example

    import pandas as pd
    from bokeh.plotting import figure, show, output_notebook
    from bokeh.models import CheckboxGroup, CustomJS
    from bokeh.layouts import row
    output_notebook()
    
    df = pd.DataFrame(
        {'x':range(5), 
         'red':range(5), 
         'blue':list(range(5))[::-1], 
         'green':[2]*5}
    )
    
    fig = figure(width=300, height=300)
    
    line_renderer = []
    names = list(df.columns[1:])
    for name in names:
        line_renderer.append(
            fig.line(
                x = 'x', 
                y = name, 
                color=name,
                name =name, 
                source=df
            )
        )
    
    checkbox = CheckboxGroup(labels=names, active=list(range(len(names))), width=100)
    callback = CustomJS(args=dict(lines=line_renderer,checkbox=checkbox),
        code="""
        for(var i=0; i<lines.length; i++){
            lines[i].visible = checkbox.active.includes(i);
        }
        """
    )
    checkbox.js_on_change('active', callback)
    layout = row(fig,checkbox)
    show(layout)
    

    Output

    click