How do I use a slider to change the color of a line?

I have a semi-working example here, where moving the slider updates the plot, as does hovering over either of the scatter plots.

What I would like is if moving the slider also updated the color of the plot. How do I do that?


import numpy as np
import bokeh
from bokeh.models import ColumnDataSource, CustomJS, Slider
from bokeh.plotting import figure, show, output_notebook
from bokeh.models import HoverTool
from bokeh.layouts import layout

output_notebook()

# make some fake data
n = 100
n_time_pts = 1000
time = np.linspace(0,1,n_time_pts)
all_traces = [np.random.random(n_time_pts) + x for x in np.arange(n)]

x = [np.mean(x) for x in all_traces]
y = [np.std(x) for x in all_traces]
z = [np.std(x)/np.mean(x) for x in all_traces]

# make colors
colors = list(bokeh.palettes.inferno(n))
rejected_color = "#969696"
for i in np.arange(10):
    colors[i] = rejected_color
colors = tuple(colors)

source = ColumnDataSource(data=dict(time=time, y=all_traces[0]))
scatter_source = ColumnDataSource(data=dict(x=x, y=y,z=z))
marker_source = ColumnDataSource(data=dict(x=[1], y=[1], z=[1]))
line_color = ColumnDataSource(data=dict(color=["black"]))

slider = Slider(start=0, end=n, step=1, value=0, max_height=50, max_width=500)

# make figure panels
trace_fig = figure(width=1000, height=500, tools=[])
trace_fig.xaxis.axis_label = 'Time (s)'

scatter1 = figure(sizing_mode="stretch_width", 
                 height=500, 
                 max_width = 500,
                 tools=[])
scatter2 = figure(sizing_mode="stretch_width", 
                 height=500, 
                 max_width = 500,
                tools=[])



# make plots in figures
line_handle = trace_fig.line('time','y',source=source, color=line_color.data["color"][0])

scatter1.circle(x,y, size=10, color=colors, alpha=0.8, hover_alpha=0)
marker1 = scatter1.circle('x','y',size=20,fill_color = None, color="red", source=marker_source)

scatter2.circle(x,z, size=10, color=colors, alpha=0.8)
marker2 = scatter2.circle('x','z',size=20,fill_color = None, color="red", source=marker_source)

# add a hover tool that sets the link data for a hovered circle
code = """
    if (cb_data.index.indices.length > 0) {
        if (cb_data.index.indices[0] > 0) { // dirty hack to ignore the zero index of the marker
            slider.value = (cb_data.index.indices[0])
            }
    };

"""

callback = CustomJS(args=dict(slider=slider), code=code)


scatter1.add_tools(HoverTool(tooltips=[],callback=callback))
scatter2.add_tools(HoverTool(tooltips=[],callback=callback))

# slider callback
callback = CustomJS(args=dict(source=source, 
                              all_traces=all_traces, 
                              slider=slider,
                             marker_source=marker_source,
                             scatter_source=scatter_source,
                             line_color=line_color,
                             colors=colors),
                    code="""
                        source.data.y = all_traces[slider.value];
                        source.change.emit();
                        
                        marker_source.data.x[0] = scatter_source.data.x[slider.value];
                        marker_source.data.y[0] = scatter_source.data.y[slider.value];
                        marker_source.data.z[0] = scatter_source.data.z[slider.value];
                        marker_source.change.emit();
                        

                        line_color.data.color = colors[slider.value];
                        line_color.change.emit();
                    """)

slider.js_on_change('value', callback)


show(layout([
        [slider],
        [trace_fig],
        [scatter1, scatter2],
       ]))

Nice job with that hover setup!

There are a lot of ways to accomplish the color thing, but based on your code the easiest to implement is to pass the line renderer object, NOT the line’s datasource to the callback. That renderer has a glyph model (Line), which has a line_color property. You set this when you instantiate the renderer using the higher level api here:

# make plots in figures
line_handle = trace_fig.line('time','y',source=source, line_color=line_color.data["color"][0]) #arg should be line_color not just color (deprecation I believe)

Here you assign an initial color to the line as the first item in your colors list. All you need to do is update that property to the index of the slider value, and to access that property in JS land, you need to pass “line_handle” into the callback:

callback = CustomJS(args=dict(source=source, 
                              all_traces=all_traces, 
                              slider=slider,
                             marker_source=marker_source,
                             scatter_source=scatter_source,
                             line_color=line_color, #don't need this anymore, could be removed
                             line_handle = line_handle, #add the renderer to the callback arg
                             colors=colors),
                    code="""
                        source.data.y = all_traces[slider.value];
                        source.change.emit();
                        
                        marker_source.data.x[0] = scatter_source.data.x[slider.value];
                        marker_source.data.y[0] = scatter_source.data.y[slider.value];
                        marker_source.data.z[0] = scatter_source.data.z[slider.value];
                        marker_source.change.emit();
                        
                        //comments things in JS
                        //line_color.data.color = colors[slider.value];
                        //line_color.change.emit();
                        line_handle.glyph.line_color = colors[slider.value]
                    """)
1 Like

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