3

I'm using Bokeh to create scatter plots by passing a ColumnDataSource to the figure.circle function. The data source has columns that designate certain colors for each point, with a hex code in each row, because the coloring scheme I want to use is somewhat complicated.

Is there a way to change the column used to color the circles in the callback of a widget? I'm imagining a dropdown menu allowing users to choose various coloring schemes for the points.

James Draper
  • 5,110
  • 5
  • 40
  • 59
qsfzy
  • 554
  • 5
  • 17

2 Answers2

7

Here is an example of a solution using a models.Select widget and models.CustomJS to select out of two coloring schemes defined in the ColumnDataSource of Figure.circle:

import bokeh
import bokeh.plotting
p = bokeh.plotting.figure(x_range=(0,4), y_range=(0,4), plot_height=200 )
csource = bokeh.models.ColumnDataSource(data=dict(
        x=[1,2,3],
        y=[1,2,1],
        colors1=["#ff0000","#00ff00","#0000ff"],
        colors2=["#ff00ff","#ffff00","#00ffff"]))
cir = p.circle(x="x",y="y",fill_color="colors1",line_color="colors1",
               size=20,source=csource)
cb_cselect = bokeh.models.CustomJS(args=dict(cir=cir,csource=csource), code ="""
    var selected_color = cb_obj.value;
    cir.glyph.line_color.field = selected_color;
    cir.glyph.fill_color.field = selected_color;
    csource.trigger("change")
""")
color_select = bokeh.models.Select(title="Select colors", value="colors1", 
                    options = ["colors1","colors2"], callback = cb_cselect)
layout = bokeh.layouts.gridplot([[p],[color_select]])
bokeh.io.output_file("output.html")
bokeh.io.show(layout)

The output looks like enter image description here

Pablo Reyes
  • 3,073
  • 1
  • 20
  • 30
  • Thanks for taking the time to give an example. I realized that I should simply create a column in the data source for the colors, then load colors into it with the callback. – qsfzy Dec 14 '16 at 19:57
  • Any chance you also know how to change a colorbar from a JS callback? – qsfzy Dec 14 '16 at 21:12
  • I haven't tested the colorbar feature yet. Previous versions of Bokeh didn't have colorbar, so I created one as a second plot, and added some colorbar selection based on matplotlib maps: see https://github.com/pmreyes2/bokeh_utils – Pablo Reyes Dec 14 '16 at 22:04
  • 1
    `ColorBar` has a `color_mapper` property, which is a instance of a `ColorMapper`. You should be able to set the `palette` (or any other) property on the the color mapper, and the color bar will update and redraw itself. – bigreddot Dec 26 '16 at 17:29
1

I've updated the excellent answer provided by Pablo Reyes for Bokeh 2.4.1:

import bokeh
import bokeh.plotting

p = bokeh.plotting.figure(x_range=(0,4), y_range=(0,4), plot_height=200 )
csource = bokeh.models.ColumnDataSource(data=dict(
        x=[1,2,3],
        y=[1,2,1],
        colors1=["#ff0000","#00ff00","#0000ff"],
        colors2=["#ff00ff","#ffff00","#00ffff"]))

cir = p.circle(x="x",y="y",fill_color="colors1",line_color="colors1",
               size=20,source=csource)

cb_cselect = bokeh.models.CustomJS(args=dict(cir=cir,csource=csource), code ="""
    var selected_color = cb_obj.value;
    cir.glyph.line_color.field = selected_color;
    cir.glyph.fill_color.field = selected_color;
    csource.change.emit();
""")

color_select = bokeh.models.Select(title="Select colors", value="colors1", 
                    options = ["colors1","colors2"])

color_select.js_on_change('value', cb_cselect)

layout = bokeh.layouts.gridplot([[p],[color_select]])
bokeh.io.output_file("output.html")
bokeh.io.show(layout)

Essentially, the JavaScript csource.trigger("change") triggered an error in JavaScript:

Uncaught TypeError: csource.trigger is not a function

Replacing it with csource.change.emit() produces the desired result. While source.change.emit() is not well documented, some examples exist here.