How to link ColorPicker with factor_cmap

Hello,

How can I link elements like vbar colored with factor_cmap (e.g. here) with ColorPicker?

The only way I’ve found is to create a separate ColumnDataSource for each bar (without using factor_map at all).

@dinya Actually I do not see any reason to have multiple CDS, one for each bar. One way to achieve what you want is to have a fruit Selector and one ColorPicker. Add a CustomJS callback to each of those widgets. Example below is based on using factor_cmap for fill_color of the vbar. If instead one is using a specific field in the CDS for color of the bars, then one can easily change callbacks for that case.

The CustomJS callback for the Selector is used to update the color property of the ColorPicker in order to display the current color of the selected fruit (bar) .

CustomJS callback for the ColorPicker is used to update the palette in the factor_cmap that is used to map fill_color to the fruits.

from bokeh.models import ColumnDataSource, ColorPicker, CustomJS, Select
from bokeh.palettes import Bright6
from bokeh.plotting import figure, show
from bokeh.transform import factor_cmap
from bokeh.layouts import column, row

fruits = ['Apples', 'Pears', 'Nectarines', 'Plums', 'Grapes', 'Strawberries']
counts = [5, 3, 4, 2, 4, 6]

source = ColumnDataSource(data=dict(fruits=fruits, counts=counts))

p = figure(
    x_range = fruits, height = 350,
    toolbar_location = None,
    title = "Fruit Counts"
    )

r_vbar = p.vbar(
    x = 'fruits', top = 'counts', width=0.9,
    source = source, legend_field = "fruits",
    line_color = 'white',
    fill_color = factor_cmap('fruits', palette=Bright6, factors=fruits)
    )

p.xgrid.grid_line_color = None
p.y_range.start = 0
p.y_range.end = 9
p.legend.orientation = "horizontal"
p.legend.location = "top_center"

select = Select(
    title = 'Fruit',
    value = fruits[0],
    options = fruits
    )

color_picker = ColorPicker(
    title = 'Bar color specific fruit',
    color = r_vbar.glyph.fill_color.transform.palette[fruits.index(select.value)]
    )

# add callbacks to widgets
# update color picker with current color of selected fruit
select.js_on_change(
    "value",
    CustomJS(
        args = {
            'vbar': r_vbar,
            'color_picker': color_picker
        },
        code="""
          const idx = this.options.indexOf(this.value);
          const color = vbar.glyph.fill_color.transform.palette[idx];
          color_picker.color = color;
        """
    )
)

# update vbar color mapper
color_picker.js_on_change(
    'color',
    CustomJS(
        args = {
            'select': select,
            'vbar': r_vbar,
            'plot': p
        },
        code="""
          const idx = select.options.indexOf(select.value);
          const {transform} = vbar.glyph.fill_color;
          transform.palette[idx] = this.color;
          vbar.glyph.fill_color = {field: 'fruits', transform: transform};
          plot.reset.emit();
        """
    )
)

show(column(p, row(select, color_picker)))
2 Likes

@Jonas_Grave_Kristens Thanks a lot. It looks exactly what I need :slight_smile:.

1 Like