Editing line_width and line_dash of figure.multi_line

Hi, I’m trying to edit line_width and line_dash after a user changes plotting options in my GUI. I can’t seem to alter these properties after the initialization, hopefully I’m accessing the correct properties? If this looks correct, I’ll work up some complete minimal code. But I suspect it’s just use error?

self.renderer_ml = self.figure.multi_line('x', 'y', source=source,
                                          line_width=line_width,
                                          line_dash=line_dash)

Later in another function called after user changes options:

self.renderer_ml.glyph.line_width = new_line_width
self.renderer_ml.glyph.line_dash = new_line_dash

And then the plot is redrawn, other glyphs in my code are similarly updated and work OK. It just seems like I’m not editing the correct attributes of a multi_line renderer? Perhaps there’s a list of glyphs per line somewhere?

I would expect that to function, so the best thing would be a small, complete reproducer that can be investigated directly.

So it turns out the line_width and line_dash do get updated. However, changing the selection state of the glyph restores the defaults. Is this a bug?

Python 3.6
Mac OS 10.14.6
Safari 13.1
Bokeh 2.0.2

from bokeh.io import show
from bokeh.models import ColumnDataSource, DataTable, TableColumn
from bokeh.plotting import figure
from bokeh.layouts import column

x = [0, 1, 2, 3, 4, 5, 6]
y1 = [i**2 for i in x]
y2 = [i**3 for i in x]

source = ColumnDataSource(dict(x=[x, x], y=[y1, y2]))

fig = figure()

renderer = fig.multi_line('x', 'y', source=source, nonselection_alpha=0, selection_alpha=1)

columns = [TableColumn(field='x', title='X Values'),
           TableColumn(field='y', title='Y Values')]
table = DataTable(source=source, columns=columns)
renderer.glyph.line_width = 10
renderer.glyph.line_dash = 'dashed'

layout = column(fig, table)

show(layout)

I noticed that selection_color sticks for my full code, but I store that in the CDS. So I tried the following, which works. Is the previous code expected behavior though?

from bokeh.io import show
from bokeh.models import ColumnDataSource, DataTable, TableColumn
from bokeh.plotting import figure
from bokeh.layouts import column

x = [0, 1, 2, 3, 4, 5, 6]
y1 = [i**2 for i in x]
y2 = [i**3 for i in x]

source = ColumnDataSource(dict(x=[x, x],
                               y=[y1, y2],
                               line_width=[1, 1],
                               line_dash=['solid', 'solid']))

fig = figure()

renderer = fig.multi_line('x', 'y', source=source, nonselection_alpha=0.3, selection_alpha=1, line_width='line_width')

columns = [TableColumn(field='x', title='X Values'),
           TableColumn(field='y', title='Y Values')]
table = DataTable(source=source, columns=columns)

source.data['line_width'] = [10, 10]
source.data['line_dash'] = ['dashed', 'dashed']

layout = column(fig, table)

show(layout)

Update:
Sorry one more comment. I forgot to include line_dash='line_dash' when initializing multi_line. If I do include it, I get a crash.

from bokeh.io import show
from bokeh.models import ColumnDataSource, DataTable, TableColumn
from bokeh.plotting import figure
from bokeh.layouts import column

x = [0, 1, 2, 3, 4, 5, 6]
y1 = [i**2 for i in x]
y2 = [i**3 for i in x]

source = ColumnDataSource(dict(x=[x, x],
                               y=[y1, y2],
                               line_width=[1, 1],
                               line_dash=['solid', 'solid']))

fig = figure()

renderer = fig.multi_line('x', 'y', source=source, nonselection_alpha=0.3, selection_alpha=1,
                          line_width='line_width', line_dash='line_dash')

columns = [TableColumn(field='x', title='X Values'),
           TableColumn(field='y', title='Y Values')]
table = DataTable(source=source, columns=columns)

source.data['line_width'] = [10, 10]
source.data['line_dash'] = ['dashed', 'dashed']

layout = column(fig, table)

show(layout)
Traceback (most recent call last):
  File "<input>", line 18, in <module>
  File "/Users/ninja/PycharmProjects/DVH-Analytics/venv_bokeh/lib/python3.6/site-packages/bokeh/plotting/_decorators.py", line 54, in wrapped
    return create_renderer(glyphclass, self, **kwargs)
  File "/Users/ninja/PycharmProjects/DVH-Analytics/venv_bokeh/lib/python3.6/site-packages/bokeh/plotting/_renderer.py", line 118, in create_renderer
    glyph=make_glyph(glyphclass, kwargs, glyph_visuals),
  File "/Users/ninja/PycharmProjects/DVH-Analytics/venv_bokeh/lib/python3.6/site-packages/bokeh/plotting/_renderer.py", line 137, in make_glyph
    return glyphclass(**kws)
  File "/Users/ninja/PycharmProjects/DVH-Analytics/venv_bokeh/lib/python3.6/site-packages/bokeh/model.py", line 234, in __init__
    super().__init__(**kwargs)
  File "/Users/ninja/PycharmProjects/DVH-Analytics/venv_bokeh/lib/python3.6/site-packages/bokeh/core/has_props.py", line 247, in __init__
    setattr(self, name, value)
  File "/Users/ninja/PycharmProjects/DVH-Analytics/venv_bokeh/lib/python3.6/site-packages/bokeh/core/has_props.py", line 274, in __setattr__
    super().__setattr__(name, value)
  File "/Users/ninja/PycharmProjects/DVH-Analytics/venv_bokeh/lib/python3.6/site-packages/bokeh/core/property/descriptors.py", line 539, in __set__
    self._internal_set(obj, value, setter=setter)
  File "/Users/ninja/PycharmProjects/DVH-Analytics/venv_bokeh/lib/python3.6/site-packages/bokeh/core/property/descriptors.py", line 760, in _internal_set
    value = self.property.prepare_value(obj, self.name, value)
  File "/Users/ninja/PycharmProjects/DVH-Analytics/venv_bokeh/lib/python3.6/site-packages/bokeh/core/property/bases.py", line 331, in prepare_value
    raise e
  File "/Users/ninja/PycharmProjects/DVH-Analytics/venv_bokeh/lib/python3.6/site-packages/bokeh/core/property/bases.py", line 324, in prepare_value
    self.validate(value)
  File "/Users/ninja/PycharmProjects/DVH-Analytics/venv_bokeh/lib/python3.6/site-packages/bokeh/core/property/either.py", line 109, in validate
    raise ValueError(msg)
ValueError: expected an element of either Enum('solid', 'dashed', 'dotted', 'dotdash', 'dashdot'), Regex('^(\\d+(\\s+\\d+)*)?$') or Seq(Int), got 'line_dash'

Line dash is not vectorizable. If you need different lines with different dash patterns you will need to use multiple calls to line or multi_line.

Edit: it probably could be made so, it just was omitted from the list of vectorizable things very early on for whatever reason, and no one has ever really asked about it.

I don’t think this is a bug. If you hit the reset button you will notice that the style comes back to what you set. The selection_glyph does not inherit the line dash style from the glyph. If you want a specific line dash style (that is different from the default solid line) then you would need to set it, just like you would need to set a different selection_line_color, etc.

Oh I see. I didn’t realize there was a selection_glyph and nonselection_glyph . Adding the following lines to the original code behaves as I was expecting.

renderer.selection_glyph.line_width = 10
renderer.selection_glyph.line_dash = 'dashed'
renderer.nonselection_glyph.line_width = 10
renderer.nonselection_glyph.line_dash = 'dashed'

Thanks Bryan!

1 Like