BokehJS: How can I change/update the theme of the graph more than once?

I’m trying to create a function in JavaScript that will update the theme of my Bokeh plot.

I use this to set the theme of my plot at the start of the function that creates the plot

Bokeh.require("core/properties").use_theme(Bokeh.Themes.dark_minimal); //Set theme for graph

The purpose of what I want is because users will print the graph and different themes will be useful for accessibility.

It seems if I use the same line of code as above, it will only set the theme for elements yet to be created.
Eg.

  1. Set theme to dark minimal
  2. make plot
  3. set theme to light minimal
  4. make the legend for the plot

The result of the above is a plot with dark minimal theme and a legend with a light minimal theme (although legend looks a bit broken)

What I want is to be able to create the plot with dark minimal theme and then later after the plot is generated, be able to change the theme to whatever I want.

I’m using BokehJS 3.0.3
I can add a code example if neccessary but don’t think it should be needed.

Is this possible to do?

@Bryan Would appreciate any insight to whether this is possible. Thanks

More or less, themes only install a different set of default values before first-use, so short answer is not at present. There is a tangentially related discussion here (focused on light/dark mode)

cc @mateusz please correct if I have anything out of date regarding the current status. AFAIK we would need to add a mechanism to force a “global re-evaluate” in order that switching themes can have an effect.

Maybe this solution will help you:

// Define a dictionary of theme names and their associated theme functions
const themes = {
  'dark': applyDarkTheme
};

// Define the dark theme function that takes a Bokeh figure object as an argument
function applyDarkTheme(fig) {
  // Set the individual attributes on the figure to give it a dark theme
  fig.background_fill_color = "#282828";
  fig.border_fill_color = "#282828";
  fig.xaxis.axis_line_color = "white";
  fig.yaxis.axis_line_color = "white";
  fig.xaxis.major_tick_line_color = "white";
  fig.yaxis.major_tick_line_color = "white";
  fig.xaxis.minor_tick_line_color = "white";
  fig.yaxis.minor_tick_line_color = "white";
  fig.xaxis.major_label_text_color = "white";
  fig.yaxis.major_label_text_color = "white";
  fig.xaxis.minor_label_text_color = "white";
  fig.yaxis.minor_label_text_color = "white";
  fig.xgrid.grid_line_color = "#8c8c8c";
  fig.ygrid.grid_line_color = "#8c8c8c";
  fig.xgrid.grid_line_alpha = 0.3;
  fig.ygrid.grid_line_alpha = 0.3;
  fig.title.text_color = "white";
}

// Define the function that applies the specified theme to all the root objects in all the Bokeh documents
function changeTheme(themeName) {
  // Get the theme function associated with the specified theme name
  const themeFunc = themes[themeName];
  // If the theme function exists, apply it to all the root objects in all the Bokeh documents
  if (themeFunc) {
    for (let doc of Bokeh.documents) {
      for (let root of doc.roots()) {
        themeFunc(root);
      }
    }
  } else {
    // If the theme function does not exist, log an error message to the console
    console.error(`Theme "${themeName}" not found`);
  }
}

To change the theme :
changeTheme('dark')

before


after:

2 Likes

AMAZING! That’s exactly what I wanted @Alex_Verdaguer Thank you very much.

Although there is one more problem: The legend doesnt get updated with the new theme, was confused for a sec writing this comment, but solved it really easily actually, just added these lines to the theme function:

fig.legend.text_color = "white";
fig.legend.border_line_color = "black";
fig.legend.border_line_alpha = 0.3;
fig.legend.background_fill_color = "black";
fig.legend.background_fill_alpha = 0.3;

Properties at: Legend properties

I thought that because calling use_theme() wouldn’t update the graph that even updating separate properties wouldn’t.
Again, Thanks very very much.

1 Like

Sure if this work that is great but it should be clear this is a bandaid, at best, since it does not recursively traverse the object graph and assumes one figure at the root.

2 Likes

Thanks for the warning. I think this solution is good enough for our use case. Thanks for your answers

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