How to update the line_color proprety from a CustomJS call

When a CDS is updated inside a CustomJS call, one has to run a source.emit.change() for the plot to be updated. That works for me. But in addition to updating the values of the line, I would like to also update its color. I have managed to both capture the current color of the line, and the new color based on a Tap event that I capture and that leads to the CustomJS call, yet, I must be missing something similar to the change() call, as even though I change the line_color property, the color on the browser does not change. Here is a bit of code that I hope helps clarify my question:


from bokeh.models import Line, ColumnDataSource, CustomJS
from bokeh.plotting import figure

# This is the line whose color I would like to change
x = np.arange(100)
y = np.random.rand(100,)
CDS = ColumnDataSource(data=dict(x=x,y=y))
fig = figure(plot_width=1000,plot_height=100)
line = Line(x='x',y='y',line_color='#000000')
fig.add_glyph(CDS,line)

# This is a CustomJS call that has access to the line object above
# This is called after a tap event. the source contains the color for 
# each element that could be tapped
CustomJS(args=dict(source=source, ts_line=ts_line_glyph), code="""
    // Obtain Selected Component ID (what used tapped on)
    // --------------------------------------------------
    var data     = source.data;
    var selected = source.selected.indices;
    
    // Obtain the new color
    // --------------------
    var colors        = data['color']
    var new_color     = colors[selected]
    
    // Update line color
    // -----------------
    var ts_line_color = ts_line.line_color;
    """)

Hi javiergcas,

The code example didn’t run as posted, but I was able to make some changes based on what I think you’re looking for.

This adds a column to your data source for the new color for each (x,y) value, and then updates the selected line (the selection_glyph part is important) to be that color. In my example, I just created a column with #ff0000 all the way down, but obviously you can change this to be what you need.

from bokeh.models import Line, ColumnDataSource, CustomJS, TapTool
from bokeh.plotting import figure, show
import numpy as np

x = np.arange(100)
y = np.random.rand(100, )
color_list = ['#ff0000'] * 100
CDS = ColumnDataSource(data=dict(x=x, y=y, new_colors=color_list))
fig = figure(plot_width=1000, plot_height=500)

selected_line = Line(x='x', y='y', line_color='#000000')
line_renderer = fig.line('x', 'y', source=CDS, line_color='#000000')
line_renderer.selection_glyph = selected_line

callback = CustomJS(args=dict(source=CDS, ts_line=line_renderer), code="""
    // get new color from ColumnDataSource for the tapped x-value
    selected_x_value = source.selected['0d']['indices'][0]
    new_color = source.data["new_colors"][selected_x_value]

    // Update line color
    ts_line.selection_glyph.line_color = new_color
    """)

tap = TapTool(callback=callback)
fig.add_tools(tap)

show(fig)

When you click off the line, the color will revert to the original (#000000 in this case).

Just FYI

source.selected['0d']['indices'] # BAD 

should always be replaced with

source.selected.indices          # GOOD

nowadays. The former will has been deprecated for some time and will stop working completely with Bokeh 2.0

1 Like

This was very helpful. It now works.

1 Like