{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "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) and [pyecharts](https://pyecharts.org/#/) 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 details on other options for customizing the component see the [layout](../../how_to/layout/index.md) and [styling](../../how_to/styling/index.md) how-to guides.\n", "\n", "* **``object``** (dict): An ECharts plot specification expressed as a Python dictionary, which is then converted to JSON. Or a pyecharts chart like `pyecharts.charts.Bar`.\n", "* **``options``** (dict): An optional dict of options passed to [`Echarts.setOption`](https://echarts.apache.org/en/api.html#echartsInstance.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. \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": [ "Lets try the ``ECharts`` pane support for ECharts specs in its raw form (i.e. a dictionary), e.g. here we declare a bar plot:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "echart_bar = {\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_bar, 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": { "tags": [] }, "outputs": [], "source": [ "echart_bar['series'] = [dict(echart_bar['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": { "tags": [] }, "outputs": [], "source": [ "responsive_spec = dict(echart_bar, responsive=True)\n", "\n", "pn.pane.ECharts(responsive_spec, height=400)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The ECharts pane also has support for pyecharts. For example, we can pass a `pyecharts.charts.Bar` chart directly the `ECharts` pane." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "from pyecharts.charts import Bar\n", "\n", "bar1 = pn.widgets.IntSlider(start=1, end=100, value=50)\n", "bar2 = pn.widgets.IntSlider(start=1, end=100, value=50)\n", "\n", "def plot(bar1, bar2):\n", " my_plot= (Bar()\n", " .add_xaxis(['Helicoptors', 'Planes'])\n", " .add_yaxis('Total In Flight', [bar1, bar2])\n", " )\n", " return pn.pane.ECharts(my_plot, width=500, height=250)\n", "pn.Row(\n", " pn.Column(bar1, bar2),\n", " pn.bind(plot, bar1, bar2),\n", ").servable()" ] }, { "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}
{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(value=50, 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": [ "## Events\n", "\n", "The `EChart` object allows you to listen to any event defined in the Javascript API, either by listening to the event in Python using the `on_event` method or by triggering a Javascript callback with the `js_on_event` method.\n", "\n", "For details on what events you can [ECharts events documentation](https://echarts.apache.org/handbook/en/concepts/event).\n", "\n", "### Python\n", "\n", "Let us start with a simple click event we want to listen to from Python. To add an event listener we simple call the `on_event` method with the event type (in this case 'click') and our Python handler." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "echart_pane = pn.pane.ECharts(echart_bar, height=480, width=640)\n", "json = pn.pane.JSON()\n", "\n", "def callback(event):\n", " json.object = event.data\n", "\n", "echart_pane.on_event('click', callback)\n", "\n", "pn.Row(echart_pane, json)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Try clicking on a point on the line. When inspecting the `json.object` after a click you should see something like this:\n", "\n", "```python\n", "{'componentType': 'series',\n", " 'componentSubType': 'line',\n", " 'componentIndex': 0,\n", " 'seriesType': 'line',\n", " 'seriesIndex': 0,\n", " 'seriesId': '\\x00Sales\\x000',\n", " 'seriesName': 'Sales',\n", " 'name': 'shirt',\n", " 'dataIndex': 0,\n", " 'data': 5,\n", " 'value': 5,\n", " 'color': '#5470c6',\n", " 'dimensionNames': ['x', 'y'],\n", " 'encode': {'x': [0], 'y': [1]},\n", " '$vars': ['seriesName', 'name', 'value'],\n", " 'event': {'detail': 1,\n", " 'altKey': False,\n", " 'button': 0,\n", " 'buttons': 0,\n", " 'clientX': 507,\n", " 'clientY': 911,\n", " 'ctrlKey': False,\n", " 'metaKey': False,\n", " 'pageX': 507,\n", " 'pageY': 911,\n", " 'screenX': 3739,\n", " 'screenY': 762,\n", " 'shiftKey': False,\n", " 'target': {'boundingClientRect': {}},\n", " 'currentTarget': {'boundingClientRect': {}},\n", " 'relatedTarget': None},\n", " 'type': 'click'}\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To restrict what types of objects a particular event applies to you can also provide a `query` argument to the `on_event` method. The format of the `query` should be `mainType` or `mainType.subType`, such as:\n", "\n", "- `'series'`: Fire event when clicking on data series\n", "- `'series.line'`: Fire event only when clicking on a line data series.\n", "- `'dataZoom'`: Fire event when clicking on zoom.\n", "- `'xAxis.category'`: Fire event when clicking on a category on the xaxis." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Javascript\n", "\n", "The same concepts apply in Javascript, however here we pass in Javascript code a JS snippet. The namespace allows you to access the event data `cb_data` and the ECharts chart itself as `cb_obj`. In this way you have access to the event and can manipulate the plot yourself:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "echart_pane = pn.pane.ECharts(echart_bar, height=480, width=640)\n", "\n", "echart_pane.js_on_event('click', 'alert(`Clicked on point: ${cb_data.dataIndex}`)')\n", "\n", "echart_pane" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you want to modify another object in response to an event triggered on the chart you can pass additional objects to the `json_on_event` method. The corresponding Bokeh model will then be made available in the callback. As an example here we make the `JSON` pane available so that we can update it on a click event:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "echart_pane = pn.pane.ECharts(echart_bar, height=480, width=640)\n", "json = pn.pane.JSON()\n", "\n", "echart_pane.js_on_event('click', \"\"\"\n", "event = {...cb_data}\n", "delete event.event\n", "json.text = JSON.stringify(event)\n", "\"\"\", json=json)\n", "\n", "pn.Row(echart_pane, json)" ] }, { "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": { "language_info": { "name": "python", "pygments_lexer": "ipython3" } }, "nbformat": 4, "nbformat_minor": 4 }