Skip to content

Commit

Permalink
Ensure bokeh legend fields are handled correctly (#4048)
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr authored Oct 8, 2019
1 parent 2f8b940 commit e2683bd
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 20 deletions.
20 changes: 16 additions & 4 deletions holoviews/plotting/bokeh/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -1127,8 +1127,11 @@ def _update_glyph(self, renderer, properties, mapping, glyph, source, data):
allowed_properties = glyph.properties()
properties = mpl_to_bokeh(properties)
merged = dict(properties, **mapping)
legend_prop = 'legend_label' if bokeh_version >= '1.3.5' else 'legend'
legend = merged.pop(legend_prop, None)
legend_props = ('legend_field', 'legend_label') if bokeh_version >= '1.3.5' else ('legend',)
for lp in legend_props:
legend = merged.pop(lp, None)
if legend is not None:
break
columns = list(source.data.keys())
glyph_updates = []
for glyph_type in ('', 'selection_', 'nonselection_', 'hover_', 'muted_'):
Expand Down Expand Up @@ -1170,7 +1173,16 @@ def _update_glyph(self, renderer, properties, mapping, glyph, source, data):
for leg in self.state.legend:
for item in leg.items:
if renderer in item.renderers:
item.label = legend
if isinstance(legend, dict):
label = legend
elif lp != 'legend':
prop = 'value' if 'label' in lp else 'field'
label = {prop: legend}
elif isinstance(item.label, dict):
label = {list(item.label)[0]: legend}
else:
label = {'value': legend}
item.label = label

for glyph, update in glyph_updates:
glyph.update(**update)
Expand Down Expand Up @@ -1758,7 +1770,7 @@ def _get_color_data(self, element, ranges, style, name='color', factors=None, co
data[field] = cdata
if factors is not None and self.show_legend:
legend_prop = 'legend_field' if bokeh_version >= '1.3.5' else 'legend'
mapping[legend_prop] = {'field': field}
mapping[legend_prop] = field
mapping[name] = {'field': field, 'transform': mapper}

return data, mapping
Expand Down
29 changes: 14 additions & 15 deletions holoviews/tests/plotting/bokeh/testoverlayplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ class TestLegends(TestBokehPlot):
def test_overlay_legend(self):
overlay = Curve(range(10), label='A') * Curve(range(10), label='B')
plot = bokeh_renderer.get_plot(overlay)
legend_labels = [l.label for l in plot.state.legend[0].items]
legend_labels = [l.label['value'] for l in plot.state.legend[0].items]
self.assertEqual(legend_labels, ['A', 'B'])

def test_dynamic_subplot_remapping(self):
Expand All @@ -220,7 +220,7 @@ def cb(X):
plot = bokeh_renderer.get_plot(dmap)
plot.update((3,))
legend_labels = [item.label for item in plot.state.legend[0].items]
self.assertEqual(legend_labels, ['1', '2'])
self.assertEqual(legend_labels, [{'value': '1'}, {'value': '2'}])
colors = Cycle().values
for i, (subplot, color) in enumerate(zip(plot.subplots.values(), colors[3:])):
self.assertEqual(subplot.handles['glyph'].line_color, color)
Expand All @@ -232,52 +232,52 @@ def test_holomap_legend_updates(self):
for i in range(3)})
plot = bokeh_renderer.get_plot(hmap)
legend_labels = [item.label for item in plot.state.legend[0].items]
self.assertEqual(legend_labels, ['C', 'B'])
self.assertEqual(legend_labels, [{'value': 'C'}, {'value': 'B'}])
plot.update((1,))
legend_labels = [item.label for item in plot.state.legend[0].items]
self.assertEqual(legend_labels, ['B', 'D'])
self.assertEqual(legend_labels, [{'value': 'B'}, {'value': 'D'}])
plot.update((2,))
legend_labels = [item.label for item in plot.state.legend[0].items]
self.assertEqual(legend_labels, ['B', 'E'])
self.assertEqual(legend_labels, [{'value': 'B'}, {'value': 'E'}])

def test_holomap_legend_updates_varying_lengths(self):
hmap = HoloMap({i: Overlay([Curve([1, 2, j], label=chr(65+j)) for j in range(i)]) for i in range(1, 4)})
plot = bokeh_renderer.get_plot(hmap)
legend_labels = [item.label for item in plot.state.legend[0].items]
self.assertEqual(legend_labels, ['A'])
self.assertEqual(legend_labels, [{'value': 'A'}])
plot.update((2,))
legend_labels = [item.label for item in plot.state.legend[0].items]
self.assertEqual(legend_labels, ['A', 'B'])
self.assertEqual(legend_labels, [{'value': 'A'}, {'value': 'B'}])
plot.update((3,))
legend_labels = [item.label for item in plot.state.legend[0].items]
self.assertEqual(legend_labels, ['A', 'B', 'C'])
self.assertEqual(legend_labels, [{'value': 'A'}, {'value': 'B'}, {'value': 'C'}])

def test_dynamicmap_legend_updates(self):
hmap = HoloMap({i: Curve([1, 2, 3], label=chr(65+i+2)) * Curve([1, 2, 3], label='B')
for i in range(3)})
dmap = Dynamic(hmap)
plot = bokeh_renderer.get_plot(dmap)
legend_labels = [item.label for item in plot.state.legend[0].items]
self.assertEqual(legend_labels, ['C', 'B'])
self.assertEqual(legend_labels, [{'value': 'C'}, {'value': 'B'}])
plot.update((1,))
legend_labels = [item.label for item in plot.state.legend[0].items]
self.assertEqual(legend_labels, ['D', 'B'])
self.assertEqual(legend_labels, [{'value': 'D'}, {'value': 'B'}])
plot.update((2,))
legend_labels = [item.label for item in plot.state.legend[0].items]
self.assertEqual(legend_labels, ['E', 'B'])
self.assertEqual(legend_labels, [{'value': 'E'}, {'value': 'B'}])

def test_dynamicmap_legend_updates_add_dynamic_plots(self):
hmap = HoloMap({i: Overlay([Curve([1, 2, j], label=chr(65+j)) for j in range(i)]) for i in range(1, 4)})
dmap = Dynamic(hmap)
plot = bokeh_renderer.get_plot(dmap)
legend_labels = [item.label for item in plot.state.legend[0].items]
self.assertEqual(legend_labels, ['A'])
self.assertEqual(legend_labels, [{'value': 'A'}])
plot.update((2,))
legend_labels = [item.label for item in plot.state.legend[0].items]
self.assertEqual(legend_labels, ['A', 'B'])
self.assertEqual(legend_labels, [{'value': 'A'}, {'value': 'B'}])
plot.update((3,))
legend_labels = [item.label for item in plot.state.legend[0].items]
self.assertEqual(legend_labels, ['A', 'B', 'C'])
self.assertEqual(legend_labels, [{'value': 'A'}, {'value': 'B'}, {'value': 'C'}])

def test_dynamicmap_ndoverlay_shrink_number_of_items(self):
selected = Stream.define('selected', items=3)()
Expand All @@ -287,4 +287,3 @@ def callback(items):
plot = bokeh_renderer.get_plot(dmap)
selected.event(items=2)
self.assertEqual(len([r for r in plot.state.renderers if r.visible]), 2)

3 changes: 2 additions & 1 deletion holoviews/tests/plotting/bokeh/testpathplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,8 @@ def test_path_continuously_varying_color_legend(self):
path = Path(data, vdims="cat").opts(color="cat", cmap=dict(zip(levels, colors)), line_width=4, show_legend=True)
plot = bokeh_renderer.get_plot(path)
item = plot.state.legend[0].items[0]
self.assertEqual(item.label, 'color_str__')
legend = {'field': 'color_str__'}
self.assertEqual(item.label, legend)
self.assertEqual(item.renderers, [plot.handles['glyph_renderer']])


Expand Down

0 comments on commit e2683bd

Please sign in to comment.