Change the colormap type of a plot through server callback

I have a object oriented code, with an image. Sometimes the user want’s a log. colormap, sometimes a linear one. Therefore I want to offer a dropdown where the user can select what colorbar to use. However the following minimal example doesn’t change the colormap, but gives me a AttributeError: 'method' object has no attribute 'color_mapper'

from bokeh.plotting import figure, curdoc
from bokeh.models import LinearColorMapper, LogColorMapper, Select
from bokeh.layouts import column
import numpy as np

class Test:
    def __init__(self):
        image = np.arange(0, 100, 1).reshape(10, 10)
        self.figure = figure()
        self.lin_cmap = LinearColorMapper(palette="Turbo256", low=0, high=100)
        self.log_cmap = LogColorMapper(palette="Turbo256", low=1, high=100)
        self.figure.image([image], x=0, y=0, dh=1, dw=1, color_mapper=self.log_cmap)
        self.select = Select(title="colormap", value="linear", options=["linear", "log"])
        self.select.on_change("value", self.change_cmap)

    def change_cmap(self, attr, old, new):
        if new == "log":
            self.figure.image.color_mapper = self.log_cmap
        else:
            self.figure.image.color_mapper = self.lin_cmap

    def plot(self):
        curdoc().add_root(column(self.figure, self.select))

I have also tried other things, like creating self.cmap and assigning that to either the linear or log cmap, but in that case nothing happens.

It will be much simpler and more reliable to create two versions of the plot up front in a layout, and then toggle their visibility appropriately. Alternatively you could also try adding two versions of the glyphs up front to a single plot, and toggle the glyphs’ visibility.

Thanks for your help! Changing the glyph works good and seems more efficient, than replacing the complete glyph.
In case anybody ever googels the same problem, here is a working solution incl. ColorBar.

class Test:
    def __init__(self):
        image = np.arange(0, 10000, 1).reshape(100, 100)
        self.figure = figure()
        self.lin_cmap = LinearColorMapper(palette="Turbo256", low=0, high=np.max(image))
        self.log_cmap = LogColorMapper(palette="Turbo256", low=0.1, high=np.max(image))
        self.lin_image = self.figure.image([image], x=0, y=0, dh=1, dw=1, color_mapper=self.lin_cmap)
        self.log_image = self.figure.image([image], x=0, y=0, dh=1, dw=1, color_mapper=self.log_cmap, visible=False)
        self.lin_bar = ColorBar(color_mapper=self.lin_cmap)
        self.log_bar = ColorBar(color_mapper=self.log_cmap, visible=False)
        self.figure.add_layout(self.log_bar, "below")
        self.figure.add_layout(self.lin_bar, "below")
        self.select = Select(title="colormap", value="linear", options=["linear", "log"])
        self.select.on_change("value", self.change_cmap)

    def change_cmap(self, attr, old, new):
        if new == "log":
            self.log_image.visible = True
            self.lin_image.visible = False
            self.lin_bar.visible = False
            self.log_bar.visible = True
        else:
            self.log_image.visible = False
            self.lin_image.visible = True
            self.lin_bar.visible = True
            self.log_bar.visible = False

    def plot(self):
        curdoc().add_root(column(self.figure, self.select))


test = Test()
test.plot()

1 Like