Source code for holoviews.plotting.plotly.renderer

import base64
from io import BytesIO

import panel as pn
import param
from param.parameterized import bothmethod

from ...core import HoloMap
from ...core.options import Store
from ..renderer import HTML_TAGS, MIME_TYPES, Renderer
from .callbacks import callbacks
from .util import clean_internal_figure_properties

with param.logging_level('CRITICAL'):
    import plotly.graph_objs as go


def _PlotlyHoloviewsPane(fig_dict, **kwargs):
    """
    Custom Plotly pane constructor for use by the HoloViews Pane.
    """

    # Remove internal HoloViews properties
    clean_internal_figure_properties(fig_dict)

    config = fig_dict.pop('config', {})
    if config.get('responsive'):
        kwargs['sizing_mode'] = 'stretch_both'
    plotly_pane = pn.pane.Plotly(fig_dict, viewport_update_policy='mouseup',
                                 config=config, **kwargs)

    # Register callbacks on pane
    for callback_cls in callbacks.values():
        for callback_prop in callback_cls.callback_properties:
            plotly_pane.param.watch(
                lambda event, cls=callback_cls, prop=callback_prop:
                    cls.update_streams_from_property_update(
                        prop, event.new, event.obj.object
                    ),
                callback_prop,
            )
    return plotly_pane


[docs]class PlotlyRenderer(Renderer): backend = param.String(default='plotly', doc="The backend name.") fig = param.ObjectSelector(default='auto', objects=['html', 'png', 'svg', 'auto'], doc=""" Output render format for static figures. If None, no figure rendering will occur. """) holomap = param.ObjectSelector(default='auto', objects=['scrubber','widgets', 'gif', None, 'auto'], doc=""" Output render multi-frame (typically animated) format. If None, no multi-frame rendering will occur.""") mode_formats = {'fig': ['html', 'png', 'svg'], 'holomap': ['widgets', 'scrubber', 'gif', 'auto']} widgets = ['scrubber', 'widgets'] _loaded = False _render_with_panel = True
[docs] @bothmethod def get_plot_state(self_or_cls, obj, doc=None, renderer=None, **kwargs): """ Given a HoloViews Viewable return a corresponding figure dictionary. Allows cleaning the dictionary of any internal properties that were added """ fig_dict = super().get_plot_state(obj, renderer, **kwargs) config = fig_dict.get('config', {}) # Remove internal properties (e.g. '_id', '_dim') clean_internal_figure_properties(fig_dict) # Run through Figure constructor to normalize keys # (e.g. to expand magic underscore notation) fig_dict = go.Figure(fig_dict).to_dict() fig_dict['config'] = config # Remove template fig_dict.get('layout', {}).pop('template', None) return fig_dict
def _figure_data(self, plot, fmt, as_script=False, **kwargs): if fmt == 'gif': import plotly.io as pio from PIL import Image from plotly.io.orca import ensure_server, shutdown_server, status running = status.state == 'running' if not running: ensure_server() nframes = len(plot) frames = [] for i in range(nframes): plot.update(i) img_bytes = BytesIO() figure = go.Figure(self.get_plot_state(plot)) img = pio.to_image(figure, 'png', validate=False) img_bytes.write(img) frames.append(Image.open(img_bytes)) if not running: shutdown_server() 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 in ('png', 'svg'): import plotly.io as pio # Wrapping plot.state in go.Figure here performs validation # and applies any default theme. figure = go.Figure(self.get_plot_state(plot)) data = pio.to_image(figure, fmt) if fmt == 'svg': data = data.decode('utf-8') else: raise ValueError(f"Unsupported format: {fmt}") if as_script: 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='') return div return data
[docs] @classmethod def plot_options(cls, obj, percent_size): factor = percent_size / 100.0 obj = obj.last if isinstance(obj, HoloMap) else obj plot = Store.registry[cls.backend].get(type(obj), None) options = plot.lookup_options(obj, 'plot').options width = options.get('width', plot.width) * factor height = options.get('height', plot.height) * factor return dict(options, width=int(width), height=int(height))
[docs] @classmethod def load_nb(cls, inline=True): """ Loads the plotly notebook resources. """ import panel.models.plotly # noqa cls._loaded = True if 'plotly' not in getattr(pn.extension, '_loaded_extensions', ['plotly']): pn.extension._loaded_extensions.append('plotly')
def _activate_plotly_backend(renderer): if renderer == "plotly": pn.pane.HoloViews._panes["plotly"] = _PlotlyHoloviewsPane Store._backend_switch_hooks.append(_activate_plotly_backend)