-
-
Notifications
You must be signed in to change notification settings - Fork 531
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add ECharts pane [doc-build] (#1484)
- Loading branch information
1 parent
51474e5
commit 043a25b
Showing
7 changed files
with
363 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"import panel as pn\n", | ||
"pn.extension('echarts')" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"The ``ECharts`` pane renders [Apache ECharts](https://echarts.apache.org/en/index.html) plots inside Panel. Note that to use the ``ECharts`` pane in the notebook the Panel extension has to be loaded with 'echarts' as an argument to ensure that echarts.js is initialized. \n", | ||
"\n", | ||
"#### Parameters:\n", | ||
"\n", | ||
"For layout and styling related parameters see the [customization user guide](../../user_guide/Customization.ipynb).\n", | ||
"\n", | ||
"* **``object``** (dict): A ECharts plot specification expressed as a Python dictionary, which is then converted to JSON\n", | ||
"* **``renderer``** (str): Whether to render with HTML 'canvas' (default) or 'svg'\n", | ||
"* **``theme``** (str): Theme to apply to plots (one of 'default', 'dark', 'light')\n", | ||
"___" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"The ``ECharts`` pane supports ECharts specs which may be provided in a raw form (i.e. a dictionary), e.g. here we declare a bar plot:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"echart = {\n", | ||
" 'title': {\n", | ||
" 'text': 'ECharts entry example'\n", | ||
" },\n", | ||
" 'tooltip': {},\n", | ||
" 'legend': {\n", | ||
" 'data':['Sales']\n", | ||
" },\n", | ||
" 'xAxis': {\n", | ||
" 'data': [\"shirt\",\"cardign\",\"chiffon shirt\",\"pants\",\"heels\",\"socks\"]\n", | ||
" },\n", | ||
" 'yAxis': {},\n", | ||
" 'series': [{\n", | ||
" 'name': 'Sales',\n", | ||
" 'type': 'bar',\n", | ||
" 'data': [5, 20, 36, 10, 10, 20]\n", | ||
" }],\n", | ||
"};\n", | ||
"echart_pane = pn.pane.ECharts(echart, height=480, width=640)\n", | ||
"echart_pane" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"Like all other panes, the ``ECharts`` pane ``object`` can be updated, either in place and triggering an update:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"echart['series'] = [dict(echart['series'][0], type= 'line')]\n", | ||
"echart_pane.param.trigger('object')" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"Vega specification can also be responsively sized by declaring the width or height to match the container:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"responsive_spec = dict(echart, responsive=True)\n", | ||
"\n", | ||
"pn.pane.ECharts(responsive_spec, height=400)" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"The ECharts library supports a wide range of chart types and since the plots are expressed using JSON datastructures we can easily update the data and then emit change events to update the charts:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"gauge = {\n", | ||
" 'tooltip': {\n", | ||
" 'formatter': '{a} <br/>{b} : {c}%'\n", | ||
" },\n", | ||
" 'series': [\n", | ||
" {\n", | ||
" 'name': 'Gauge',\n", | ||
" 'type': 'gauge',\n", | ||
" 'detail': {'formatter': '{value}%'},\n", | ||
" 'data': [{'value': 50, 'name': 'Value'}]\n", | ||
" }\n", | ||
" ]\n", | ||
"};\n", | ||
"gauge_pane = pn.pane.ECharts(gauge, width=400, height=400)\n", | ||
"\n", | ||
"slider = pn.widgets.IntSlider(start=0, end=100)\n", | ||
"\n", | ||
"slider.jscallback(args={'gauge': gauge_pane}, value=\"\"\"\n", | ||
"gauge.data.series[0].data[0].value = cb_obj.value\n", | ||
"gauge.properties.data.change.emit()\n", | ||
"\"\"\")\n", | ||
"\n", | ||
"pn.Column(slider, gauge_pane)" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"### Controls\n", | ||
"\n", | ||
"The `EChart` pane exposes a number of options which can be changed from both Python and Javascript. Try out the effect of these parameters interactively:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"pn.Row(gauge_pane.controls(jslink=True), gauge_pane)" | ||
] | ||
} | ||
], | ||
"metadata": { | ||
"kernelspec": { | ||
"display_name": "Python 3.7.5 64-bit ('gv_dev': conda)", | ||
"language": "python", | ||
"name": "python37564bitgvdevcondacc2b7e051cc74b569c7fcfe099557b10" | ||
}, | ||
"language_info": { | ||
"codemirror_mode": { | ||
"name": "ipython", | ||
"version": 3 | ||
}, | ||
"file_extension": ".py", | ||
"mimetype": "text/x-python", | ||
"name": "python", | ||
"nbconvert_exporter": "python", | ||
"pygments_lexer": "ipython3", | ||
"version": "3.7.5" | ||
}, | ||
"widgets": { | ||
"application/vnd.jupyter.widget-state+json": { | ||
"state": {}, | ||
"version_major": 2, | ||
"version_minor": 0 | ||
} | ||
} | ||
}, | ||
"nbformat": 4, | ||
"nbformat_minor": 4 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
""" | ||
Defines custom ECharts bokeh model to render Vega json plots. | ||
""" | ||
from bokeh.core.properties import Any, Dict, Enum, String | ||
from bokeh.models import LayoutDOM | ||
|
||
|
||
class ECharts(LayoutDOM): | ||
""" | ||
A Bokeh model that wraps around an ECharts plot and renders it | ||
inside a Bokeh. | ||
""" | ||
|
||
__javascript__ = ["https://cdn.jsdelivr.net/npm/echarts@4.8.0/dist/echarts.min.js"] | ||
|
||
__js_skip__ = { | ||
'echarts': __javascript__[:1]} | ||
|
||
__js_require__ = { | ||
'baseUrl': 'https://cdn.jsdelivr.net/npm/', | ||
'paths': { | ||
"echarts": "echarts@4.8.0/dist/echarts.min" | ||
}, | ||
'exports': {} | ||
} | ||
|
||
data = Dict(String, Any) | ||
|
||
renderer = Enum("canvas", "svg") | ||
|
||
theme = Enum("default", "light", "dark") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import * as p from "@bokehjs/core/properties" | ||
import {HTMLBox, HTMLBoxView} from "@bokehjs/models/layouts/html_box" | ||
|
||
export class EChartsView extends HTMLBoxView { | ||
model: ECharts | ||
_chart: any | ||
|
||
connect_signals(): void { | ||
super.connect_signals() | ||
this.connect(this.model.properties.data.change, () => this._plot()) | ||
const {width, height, renderer, theme} = this.model.properties | ||
this.on_change([width, height], () => this._resize()) | ||
this.on_change([theme, renderer], () => this._rerender()) | ||
} | ||
|
||
render(): void { | ||
super.render() | ||
const config = {width: this.model.width, height: this.model.height, renderer: this.model.renderer} | ||
this._chart = (window as any).echarts.init(this.el, this.model.theme, config); | ||
this._plot() | ||
} | ||
|
||
after_layout(): void { | ||
super.after_layout() | ||
this._chart.resize() | ||
} | ||
|
||
_plot(): void { | ||
if ((window as any).echarts == null) | ||
return | ||
this._chart.setOption(this.model.data); | ||
} | ||
|
||
_rerender(): void { | ||
(window as any).echarts.dispose(this._chart); | ||
this.render(); | ||
} | ||
|
||
_resize(): void { | ||
this._chart.resize({width: this.model.width, height: this.model.height}); | ||
} | ||
} | ||
|
||
export namespace ECharts { | ||
export type Attrs = p.AttrsOf<Props> | ||
export type Props = HTMLBox.Props & { | ||
data: p.Property<any> | ||
renderer: p.Property<string> | ||
theme: p.Property<string> | ||
} | ||
} | ||
|
||
export interface ECharts extends ECharts.Attrs {} | ||
|
||
export class ECharts extends HTMLBox { | ||
properties: ECharts.Props | ||
|
||
constructor(attrs?: Partial<ECharts.Attrs>) { | ||
super(attrs) | ||
} | ||
|
||
static __module__ = "panel.models.echarts" | ||
|
||
static init_ECharts(): void { | ||
this.prototype.default_view = EChartsView | ||
|
||
this.define<ECharts.Props>({ | ||
data: [ p.Any ], | ||
theme: [ p.String, "default" ], | ||
renderer: [ p.String, "canvas"] | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
from __future__ import absolute_import, division, unicode_literals | ||
|
||
import sys | ||
|
||
import param | ||
|
||
from pyviz_comms import JupyterComm | ||
|
||
from ..viewable import Layoutable | ||
from .base import PaneBase | ||
|
||
|
||
class ECharts(PaneBase): | ||
""" | ||
ECharts panes allow rendering echarts.js plots. | ||
""" | ||
|
||
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 = 0 | ||
|
||
_rename = {"object": "data"} | ||
|
||
_rerender_params = [] | ||
|
||
_updates = True | ||
|
||
@classmethod | ||
def applies(cls, obj): | ||
return isinstance(obj, dict) | ||
|
||
@classmethod | ||
def _get_dimensions(cls, json, props): | ||
if json is None: | ||
return | ||
responsive = json.get('responsive') | ||
if responsive: | ||
props['sizing_mode'] = 'stretch_both' | ||
else: | ||
props['sizing_mode'] = 'fixed' | ||
|
||
def _get_model(self, doc, root=None, parent=None, comm=None): | ||
if 'panel.models.echarts' not in sys.modules: | ||
if isinstance(comm, JupyterComm): | ||
self.param.warning('EChart was not imported on instantiation ' | ||
'and may not render in a notebook. Restart ' | ||
'the notebook kernel and ensure you load ' | ||
'it as part of the extension using:' | ||
'\n\npn.extension(\'echart\')\n') | ||
from ..models.echarts import ECharts | ||
else: | ||
ECharts = getattr(sys.modules['panel.models.echarts'], 'ECharts') | ||
|
||
props = self._process_param_change(self._init_properties()) | ||
self._get_dimensions(self.object, props) | ||
data = {} if self.object is None else dict(self.object) | ||
model = ECharts(data=data, **props) | ||
if root is None: | ||
root = model | ||
self._models[root.ref['id']] = (model, parent) | ||
return model | ||
|
||
def _update(self, ref=None, model=None): | ||
props = {p : getattr(self, p) for p in list(Layoutable.param) | ||
if getattr(self, p) is not None} | ||
self._get_dimensions(self.object, props) | ||
props['data'] = {} if self.object else dict(self.object) | ||
model.update(**props) |