Source code for panel.pane.echarts

from __future__ import annotations

import json
import sys

from collections import defaultdict
from typing import (
    TYPE_CHECKING, Any, Callable, ClassVar, List, Mapping, Optional,
)

import param

from bokeh.models import CustomJS
from pyviz_comms import JupyterComm

from ..util import lazy_load
from ..viewable import Viewable
from .base import ModelPane

if TYPE_CHECKING:
    from bokeh.document import Document
    from bokeh.model import Model
    from pyviz_comms import Comm


[docs]class ECharts(ModelPane): """ ECharts panes allow rendering echarts.js dictionaries and pyecharts plots. Reference: https://panel.holoviz.org/reference/panes/ECharts.html :Example: >>> pn.extension('echarts') >>> ECharts(some_echart_dict_or_pyecharts_object, height=480, width=640) """ object = param.Parameter(default=None, doc=""" The Echarts object being wrapped. Can be an Echarts dictionary or a pyecharts chart""") options = param.Parameter(default=None, doc=""" An optional dict of options passed to Echarts.setOption. Allows to fine-tune the rendering behavior. For example, you might want to use `options={ "replaceMerge": ['series'] })` when updating the `objects` with a value containing a smaller number of series. """) renderer = param.ObjectSelector(default="canvas", objects=["canvas", "svg"], doc=""" Whether to render as HTML canvas or SVG""") theme = param.ObjectSelector(default="default", objects=["default", "light", "dark"], doc=""" Theme to apply to plots.""") priority: ClassVar[float | bool | None] = None _rename: ClassVar[Mapping[str, str | None]] = {"object": "data"} _rerender_params: ClassVar[List[str]] = [] _updates: ClassVar[bool] = True def __init__(self, object=None, **params): super().__init__(object, **params) self._py_callbacks = defaultdict(lambda: defaultdict(list)) self._js_callbacks = defaultdict(list)
[docs] @classmethod def applies(cls, obj: Any, **params) -> float | bool | None: if isinstance(obj, dict): return 0 elif cls.is_pyecharts(obj): return 0.8 return None
@classmethod def is_pyecharts(cls, obj): if 'pyecharts' in sys.modules: import pyecharts return isinstance(obj, pyecharts.charts.chart.Chart) return False def _process_event(self, event): callbacks = self._py_callbacks.get(event.type, {}) for cb in callbacks.get(None, []): cb(event) if event.query is None: return for cb in callbacks.get(event.query, []): cb(event) def _get_js_events(self, ref): js_events = defaultdict(list) for event, specs in self._js_callbacks.items(): for (query, code, args) in specs: models = { name: viewable._models[ref][0] for name, viewable in args.items() if ref in viewable._models } js_events[event].append({'query': query, 'callback': CustomJS(code=code, args=models)}) return dict(js_events) def _process_param_change(self, params): props = super()._process_param_change(params) if 'data' not in props: return props data = props['data'] or {} if not isinstance(data, dict): w, h = data.width, data.height props['data'] = data = json.loads(data.dump_options()) if not self.height and h: props['height'] = int(h.replace('px', '')) if not self.width and w: props['width'] = int(w.replace('px', '')) else: props['data'] = data if data.get('responsive'): props['sizing_mode'] = 'stretch_both' return props def _get_properties(self, document: Document): props = super()._get_properties(document) props['event_config'] = { event: list(queries) for event, queries in self._py_callbacks.items() } return props def _get_model( self, doc: Document, root: Optional[Model] = None, parent: Optional[Model] = None, comm: Optional[Comm] = None ) -> Model: self._bokeh_model = lazy_load( 'panel.models.echarts', 'ECharts', isinstance(comm, JupyterComm), root ) model = super()._get_model(doc, root, parent, comm) self._register_events('echarts_event', model=model, doc=doc, comm=comm) return model
[docs] def on_event(self, event: str, callback: Callable, query: str | None = None): """ Register anevent handler which triggers when the specified event is triggered. Reference: https://apache.github.io/echarts-handbook/en/concepts/event/ Arguments --------- event: str The name of the event to register a handler on, e.g. 'click'. callback: str | CustomJS The event handler to be executed when the event fires. query: str | None A query that determines when the event fires. """ self._py_callbacks[event][query].append(callback) event_config = {event: list(queries) for event, queries in self._py_callbacks.items()} for ref, (model, _) in self._models.items(): self._apply_update({}, {'event_config': event_config}, model, ref)
[docs] def js_on_event(self, event: str, callback: str | CustomJS, query: str | None = None, **args): """ Register a Javascript event handler which triggers when the specified event is triggered. The callback can be a snippet of Javascript code or a bokeh CustomJS object making it possible to manipulate other models in response to an event. Reference: https://apache.github.io/echarts-handbook/en/concepts/event/ Arguments --------- event: str The name of the event to register a handler on, e.g. 'click'. code: str The event handler to be executed when the event fires. query: str | None A query that determines when the event fires. args: Viewable A dictionary of Viewables to make available in the namespace of the object. """ self._js_callbacks[event].append((query, callback, args)) for ref, (model, _) in self._models.items(): js_events = self._get_js_events(ref) self._apply_update({}, {'js_events': js_events}, model, ref)
def setup_js_callbacks(root_view, root_model): if 'panel.models.echarts' not in sys.modules: return ref = root_model.ref['id'] for pane in root_view.select(ECharts): if ref in pane._models: pane._models[ref][0].js_events = pane._get_js_events(ref) Viewable._preprocessing_hooks.append(setup_js_callbacks)