diff --git a/holoviews/plotting/bokeh/element.py b/holoviews/plotting/bokeh/element.py index 5f4addd6d7..7b8ecfe5d2 100644 --- a/holoviews/plotting/bokeh/element.py +++ b/holoviews/plotting/bokeh/element.py @@ -261,6 +261,8 @@ def _get_hover_data(self, data, element, empty=False): dim = util.dimension_sanitizer(d.name) if dim not in data: data[dim] = element.dimension_values(d) + elif isinstance(data[dim], np.ndarray) and data[dim].dtype.kind == 'M': + data[dim+'_dt_strings'] = [d.pprint_value(v) for v in data[dim]] for k, v in self.overlay_dims.items(): dim = util.dimension_sanitizer(k.name) @@ -573,7 +575,6 @@ def _categorize_data(self, data, cols, dims): (isinstance(column, list) or column.dtype.kind not in 'SU')): data[col] = [dims[i].pprint_value(v) for v in column] - def _get_factors(self, element): """ Get factors for categorical axes. @@ -664,6 +665,27 @@ def _execute_hooks(self, element): self.warning("Plotting hook %r could not be applied:\n\n %s" % (hook, e)) + def _postprocess_hover(self, renderer, source): + """ + Attaches renderer to hover tool and processes tooltips to + ensure datetime data is displayed correctly. + """ + hover = self.handles.get('hover') + if hover is None: + return + hover.renderers = [renderer] + + # If datetime column is in the data replace hover formatter + for k, v in source.data.items(): + if k+'_dt_strings' in source.data: + tooltips = [] + for name, formatter in hover.tooltips: + if formatter == '@{%s}' % k: + formatter = '@{%s_dt_strings}' % k + tooltips.append((name, formatter)) + hover.tooltips = tooltips + + def initialize_plot(self, ranges=None, plot=None, plots=None, source=None): """ Initializes a new plot object with the last available frame. @@ -709,8 +731,7 @@ def initialize_plot(self, ranges=None, plot=None, plots=None, source=None): if isinstance(renderer, Renderer): self.handles['glyph_renderer'] = renderer - if 'hover' in self.handles: - self.handles['hover'].renderers = [renderer] + self._postprocess_hover(renderer, source) # Update plot, source and glyph with abbreviated_exception(): diff --git a/tests/testplotinstantiation.py b/tests/testplotinstantiation.py index 787266885d..a6a4bb1f7e 100644 --- a/tests/testplotinstantiation.py +++ b/tests/testplotinstantiation.py @@ -465,6 +465,20 @@ def _test_hover_info(self, element, tooltips, line_policy='nearest'): self.assertEqual(hover[0].tooltips, tooltips) self.assertEqual(hover[0].line_policy, line_policy) + def test_points_overlay_datetime_hover(self): + obj = NdOverlay({i: Points((list(pd.date_range('2016-01-01', '2016-01-31')), range(31))) for i in range(5)}, + kdims=['Test']) + opts = {'Points': {'tools': ['hover']}} + obj = obj(plot=opts) + self._test_hover_info(obj, [('Test', '@{Test}'), ('x', '@{x_dt_strings}'), ('y', '@{y}')]) + + def test_curve_overlay_datetime_hover(self): + obj = NdOverlay({i: Curve((list(pd.date_range('2016-01-01', '2016-01-31')), range(31))) for i in range(5)}, + kdims=['Test']) + opts = {'Curve': {'tools': ['hover']}} + obj = obj(plot=opts) + self._test_hover_info(obj, [('Test', '@{Test}'), ('x', '@{x_dt_strings}'), ('y', '@{y}')]) + def test_points_overlay_hover_batched(self): obj = NdOverlay({i: Points(np.random.rand(10,2)) for i in range(5)}, kdims=['Test'])