Source code for holoviews.plotting.bokeh.renderer

import base64
import logging
from io import BytesIO

import bokeh
import param
from bokeh.document import Document
from bokeh.io import curdoc
from bokeh.models import Model
from bokeh.themes.theme import Theme
from panel.io.notebook import render_mimebundle
from panel.io.state import state
from param.parameterized import bothmethod

from ...core import HoloMap, Store
from ..plot import Plot
from ..renderer import HTML_TAGS, MIME_TYPES, Renderer
from .util import compute_plot_size

default_theme = Theme(json={
    'attrs': {
        'Title': {'text_color': 'black', 'text_font_size': '12pt'}
    }
})


[docs]class BokehRenderer(Renderer): backend = param.String(default='bokeh', doc="The backend name.") fig = param.ObjectSelector(default='auto', objects=['html', 'json', 'auto', 'png'], doc=""" Output render format for static figures. If None, no figure rendering will occur. """) holomap = param.ObjectSelector(default='auto', objects=['widgets', 'scrubber', None, 'gif', 'auto'], doc=""" Output render multi-frame (typically animated) format. If None, no multi-frame rendering will occur.""") theme = param.ClassSelector(default=default_theme, class_=(Theme, str), allow_None=True, doc=""" The applicable Bokeh Theme object (if any).""") webgl = param.Boolean(default=True, doc=""" Whether to render plots with WebGL if available""") # Defines the valid output formats for each mode. mode_formats = {'fig': ['html', 'auto', 'png'], 'holomap': ['widgets', 'scrubber', 'gif', 'auto', None]} _loaded = False _render_with_panel = True @bothmethod def _save_prefix(self_or_cls, ext): "Hook to prefix content for instance JS when saving HTML" return
[docs] @bothmethod def get_plot(self_or_cls, obj, doc=None, renderer=None, **kwargs): """ Given a HoloViews Viewable return a corresponding plot instance. Allows supplying a document attach the plot to, useful when combining the bokeh model with another plot. """ plot = super().get_plot(obj, doc, renderer, **kwargs) if plot.document is None: plot.document = Document() if self_or_cls.notebook_context else curdoc() if self_or_cls.theme: plot.document.theme = self_or_cls.theme return plot
def _figure_data(self, plot, fmt, doc=None, as_script=False, **kwargs): """ Given a plot instance, an output format and an optional bokeh document, return the corresponding data. If as_script is True, the content will be split in an HTML and a JS component. """ model = plot.state if doc is None: doc = plot.document else: plot.document = doc for m in model.references(): m._document = None doc.theme = self.theme doc.add_root(model) # Bokeh raises warnings about duplicate tools and empty subplots # but at the holoviews level these are not issues logger = logging.getLogger(bokeh.core.validation.check.__file__) logger.disabled = True data = None if fmt == 'gif': from bokeh.io.export import get_screenshot_as_png from bokeh.io.webdriver import webdriver_control if state.webdriver is None: webdriver = webdriver_control.create() else: webdriver = state.webdriver nframes = len(plot) frames = [] for i in range(nframes): plot.update(i) img = get_screenshot_as_png(plot.state, driver=webdriver) frames.append(img) if state.webdriver is not None: webdriver.close() bio = BytesIO() duration = (1./self.fps)*1000 frames[0].save(bio, format='GIF', append_images=frames[1:], save_all=True, duration=duration, loop=0) bio.seek(0) data = bio.read() elif fmt == 'png': from bokeh.io.export import get_screenshot_as_png img = get_screenshot_as_png(plot.state, driver=state.webdriver) imgByteArr = BytesIO() img.save(imgByteArr, format='PNG') data = imgByteArr.getvalue() else: div = render_mimebundle(plot.state, doc, plot.comm)[0]['text/html'] if as_script and fmt in ['png', 'gif']: b64 = base64.b64encode(data).decode("utf-8") (mime_type, tag) = MIME_TYPES[fmt], HTML_TAGS[fmt] src = HTML_TAGS['base64'].format(mime_type=mime_type, b64=b64) div = tag.format(src=src, mime_type=mime_type, css='') plot.document = doc if as_script or data is None: return div else: return data
[docs] @classmethod def plot_options(cls, obj, percent_size): """ Given a holoviews object and a percentage size, apply heuristics to compute a suitable figure size. For instance, scaling layouts and grids linearly can result in unwieldy figure sizes when there are a large number of elements. As ad hoc heuristics are used, this functionality is kept separate from the plotting classes themselves. Used by the IPython Notebook display hooks and the save utility. Note that this can be overridden explicitly per object using the fig_size and size plot options. """ obj = obj.last if isinstance(obj, HoloMap) else obj plot = Store.registry[cls.backend].get(type(obj), None) if not hasattr(plot, 'width') or not hasattr(plot, 'height'): from .plot import BokehPlot plot = BokehPlot options = plot.lookup_options(obj, 'plot').options width = options.get('width', plot.width) height = options.get('height', plot.height) if width is not None: options['width'] = int(width) if height is not None: options['height'] = int(height) return dict(options)
[docs] @bothmethod def get_size(self_or_cls, plot): """ Return the display size associated with a plot before rendering to any particular format. Used to generate appropriate HTML display. Returns a tuple of (width, height) in pixels. """ if isinstance(plot, Plot): plot = plot.state elif not isinstance(plot, Model): raise ValueError('Can only compute sizes for HoloViews ' 'and bokeh plot objects.') return compute_plot_size(plot)
[docs] @classmethod def load_nb(cls, inline=True): cls._loaded = True