diff --git a/holoviews/plotting/bokeh/chart.py b/holoviews/plotting/bokeh/chart.py index b7c2d63246..73d77d44a5 100644 --- a/holoviews/plotting/bokeh/chart.py +++ b/holoviews/plotting/bokeh/chart.py @@ -15,12 +15,12 @@ from ...core.options import abbreviated_exception from ...operation import interpolate_curve from ..util import (compute_sizes, get_sideplot_ranges, match_spec, - map_colors, get_min_distance) + map_colors, get_min_distance, rgb2hex) from .element import (ElementPlot, ColorbarPlot, LegendPlot, line_properties, fill_properties) from .path import PathPlot, PolygonPlot -from .util import (get_cmap, mpl_to_bokeh, update_plot, rgb2hex, - bokeh_version, expand_batched_style, filter_batched_data) +from .util import (get_cmap, mpl_to_bokeh, update_plot, bokeh_version, + expand_batched_style, filter_batched_data) class PointPlot(LegendPlot, ColorbarPlot): @@ -630,7 +630,9 @@ def update_frame(self, key, ranges=None, plot=None, element=None): self.current_key = key self.current_frame = element - self.style = self.lookup_options(element, 'style') + max_cycles = len(self.style._options) + self.style = self.lookup_options(style_element, 'style').max_cycles(max_cycles) + self.set_param(**self.lookup_options(element, 'plot').options) ranges = self.compute_ranges(self.hmap, key, ranges) ranges = match_spec(element, ranges) diff --git a/holoviews/plotting/bokeh/element.py b/holoviews/plotting/bokeh/element.py index edc9a0a502..e084cd1839 100644 --- a/holoviews/plotting/bokeh/element.py +++ b/holoviews/plotting/bokeh/element.py @@ -772,8 +772,14 @@ def update_frame(self, key, ranges=None, plot=None, element=None, empty=False): if not element or (not self.dynamic and self.static): return - style_element = element.last if self.batched else element - self.style = self.lookup_options(style_element, 'style') + if self.batched: + style_element = element.last + max_cycles = None + else: + style_element = element + max_cycles = len(self.style._options) + style = self.lookup_options(style_element, 'style') + self.style = style.max_cycles(max_cycles) if max_cycles else style ranges = self.compute_ranges(self.hmap, key, ranges) self.set_param(**self.lookup_options(style_element, 'plot').options) diff --git a/holoviews/plotting/bokeh/path.py b/holoviews/plotting/bokeh/path.py index 6e8644e057..b13e715d78 100644 --- a/holoviews/plotting/bokeh/path.py +++ b/holoviews/plotting/bokeh/path.py @@ -6,10 +6,9 @@ from bokeh.models import HoverTool from ...core import util -from ..util import map_colors +from ..util import map_colors, rgb2hex from .element import ElementPlot, ColorbarPlot, line_properties, fill_properties -from .util import (get_cmap, rgb2hex, expand_batched_style, - filter_batched_data) +from .util import get_cmap, expand_batched_style, filter_batched_data class PathPlot(ElementPlot): diff --git a/holoviews/plotting/bokeh/util.py b/holoviews/plotting/bokeh/util.py index b2d9b87171..3ecb776f30 100644 --- a/holoviews/plotting/bokeh/util.py +++ b/holoviews/plotting/bokeh/util.py @@ -29,7 +29,7 @@ from ...core.overlay import Overlay from ...core.util import basestring, unique_array -from ..util import dim_axis_label +from ..util import dim_axis_label, rgb2hex # Conversion between matplotlib and bokeh markers markers = {'s': {'marker': 'square'}, @@ -61,14 +61,6 @@ 'LinearAxis', 'ColumnDataSource'] -def rgb2hex(rgb): - """ - Convert RGB(A) tuple to hex. - """ - if len(rgb) > 3: - rgb = rgb[:-1] - return "#{0:02x}{1:02x}{2:02x}".format(*(int(v*255) for v in rgb)) - def rgba_tuple(rgba): """ diff --git a/holoviews/plotting/util.py b/holoviews/plotting/util.py index fae17fc3cf..f35534f491 100644 --- a/holoviews/plotting/util.py +++ b/holoviews/plotting/util.py @@ -356,6 +356,15 @@ def get_min_distance(element): return distances[distances>0].min() +def rgb2hex(rgb): + """ + Convert RGB(A) tuple to hex. + """ + if len(rgb) > 3: + rgb = rgb[:-1] + return "#{0:02x}{1:02x}{2:02x}".format(*(int(v*255) for v in rgb)) + + # linear_kryw_0_100_c71 (aka "fire"): # A perceptually uniform equivalent of matplotlib's "hot" colormap, from # http://peterkovesi.com/projects/colourmaps diff --git a/tests/testplotinstantiation.py b/tests/testplotinstantiation.py index 7ccda4df09..5c3821ceda 100644 --- a/tests/testplotinstantiation.py +++ b/tests/testplotinstantiation.py @@ -12,7 +12,8 @@ import param import numpy as np from holoviews import (Dimension, Overlay, DynamicMap, Store, Dataset, - NdOverlay, GridSpace, HoloMap, Layout, Cycle) + NdOverlay, GridSpace, HoloMap, Layout, Cycle, + Palette) from holoviews.core.util import pd from holoviews.element import (Curve, Scatter, Image, VLine, Points, HeatMap, QuadMesh, Spikes, ErrorBars, @@ -21,6 +22,7 @@ from holoviews.element.comparison import ComparisonTestCase from holoviews.streams import PositionXY, PositionX from holoviews.plotting import comms +from holoviews.plotting.util import rgb2hex # Standardize backend due to random inconsistencies try: @@ -83,6 +85,7 @@ def __exit__(self, *args): self.stream.seek(0) + class TestMPLPlotInstantiation(ComparisonTestCase): def setUp(self): @@ -358,6 +361,18 @@ def test_batched_points_size_and_color(self): self.assertEqual(plot.handles['source'].data['color'], color) self.assertEqual(plot.handles['source'].data['size'], size) + def test_cyclic_palette_curves(self): + palette = Palette('Set1') + opts = dict(color=palette) + hmap = HoloMap({i: NdOverlay({j: Curve(np.random.rand(3))(style=opts) + for j in range(3)}) + for i in range(3)}) + colors = palette[3].values + plot = bokeh_renderer.get_plot(hmap) + for subp, color in zip(plot.subplots.values(), colors): + self.assertEqual(subp.handles['glyph'].line_color, rgb2hex(color)) + + def test_batched_points_line_color_and_color(self): opts = {'NdOverlay': dict(plot=dict(legend_limit=0)), 'Points': dict(style=dict(line_color=Cycle(values=['red', 'blue'])))}