bokeh: [BUG] "line_color" not applied from YAML theme file

ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)

Python version      :  3.7.2 (default, Mar 15 2019, 01:06:31) 
IPython version     :  (not installed)
Tornado version     :  6.0.3
Bokeh version       :  1.3.0
BokehJS static path :  /usr/local/lib/python3.7/site-packages/bokeh/server/static
node.js version     :  v10.15.1
npm version         :  6.10.0

OS: Mac OS X Mojave 10.14.6 Browser: Google Chrome Version 76.0.3809.100 (Official Build) (64-bit)

Description of expected behavior and the observed behavior Expected behavior: theme should be applied so line_color should be set to red Observed behavior: line_color is not applied (remains default)

Complete, minimal, self-contained example code that reproduces the issue

main.py

from bokeh.plotting import curdoc, figure

plot = figure()
plot.line(x = [1, 2], y = [1, 2])

curdoc().add_root(plot) 

theme.yaml

attrs: 
    Line:
        line_width: 10
        line_color: "#FF0000"  

Additional info

Both files are in test_yaml directory. The app is run as follows: bokeh serve --show yaml_test

No JS console warnings or error are given.

The plot looks like: Screenshot 2019-08-19 at 12 50 16

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Comments: 18 (18 by maintainers)

Commits related to this issue

Most upvoted comments

@solstag that’s perfect text, I will add that to the docs in a PR soon (unless you or someone else gets to it sooner!)

I actually spoke too hastily. I thought the _pop_colors_and_alpha function was written to coincidentally return the same color as the Line.line_color property default value. But that’s not the case. It just returns the value of get_default_color which is whatever it is, and unrelated to the property default, More importantly means that the plotting API setting its own defaults requires actually setting new property values, and that means that any theme values for those properties are overridden. (Explicitly set values always override theme values which always override property base defaults, definitely do not want to make any exceptions to that precedence rule at all)

Thinking about more things, I am not sure there is a reasonable alternative here:

  • Even if if we wanted to special-case a comparison directly with a Theme object (to avoid setting a value if there is themed one), there is nothing requiring a theme to be set yet at the point in time when p.line is called. Nothing prevents anyone from waiting until the very end of their code to supply a theme, at which point it is too late to make a comparison, because p.line was already called and done.

  • setting a theme for line color actually seems odd. Themes were imagined for: make the background color on all these 5 plots the same, or set the same font on every axis. But lines? Most plots that have lines have more than one, in which case a theme makes no sense, because multiple lines in a plot are almost always different colors. And if you do just have one line on the plot, well, it’s actually less work to just set it in code.

The only meaningful case I can think of is: I have N plots all with just one line each, and I want them to all have the same color. And if theming is really desired in that case instead of setting programmatically, well there is way: call p.add_glyph(source, Line(...)) instead of the higher level p.line

I’m leaning towards closing this with noaction or possibly just a note stating that line color and alpha are not theme-able in conjunction with the bokeh.plotting API. This is just such a corner case it’s hard to justify the amount of hoops that would be required to jump through to change it.

@solstag can you comment on this in relation to your PR https://github.com/bokeh/bokeh/pull/9153 specifically do you know does your PR keep this behaviour?

@osule this is absolutely an artifact of the higher level plotting api. See this line:

https://github.com/bokeh/bokeh/blob/master/bokeh/plotting/helpers.py#L918

When run with the examples, the args passed to it are:

<class ‘bokeh.models.glyphs.Line’> {‘x’: ‘x’, ‘y’: ‘y’} {‘line_color’: ‘#1f77b4’, ‘line_alpha’: 1.0}

That precisely counts as setting those values, which means to override the theme. The problem is that _pop_colors_and_alpha function returns values for color and alpha even when they are not set by the user. It should not do that, at least not for the “normal” glyph.

Uh, OK, interesting devlopemnt. Paring the example down to the minimal:

from bokeh.plotting import curdoc, figure
from bokeh.models import Plot, Range1d, ColumnDataSource, Line

plot = Plot()
plot.x_range = Range1d(0, 3)
plot.y_range = Range1d(0, 3)
source = ColumnDataSource(data=dict(x=[1,2], y=[1,2]))
plot.add_glyph(source, Line(x='x', y='y'))

curdoc().add_root(plot)

WORKS

I actually wonder if this is an interaction with the plotting API, where the automatic creation of a default “selection” glyph would be setting its own values for some properties (which might be considered overriding defaults)

@osule I’d definitely welcome some more investigation around this. There are definitely some bugs to sort out, they may be somewhat tricky to trace but I am happy to try to provide any guidance I can. Offhand some comments:

  • obj._property_values is only ever supposed to contain keys for property values that users have explicitly set via assignments they have themselves made. I.e. it’s supposed to keep track of all the things users have manually asked to be overridden. That’s obviously not the case here, and is the proximate cause of the issue: line_color for the Line object, should not be in obj._property_values

    Good places to trace/debug are _real_set and _get_default

  • I would pare the example down as much as possible. I.e. use Plot with not axes, no tools, etc. this will make this simpler since many of those things also have line_color properties.

  • Two Line objects are being created (!) I am not sure if this is something reasonable I have forgotten about, out a very weird (bad?) bug Nevermind, this is the “normal” glyph and the “selection” glyph that are both created by the plotting API