Echarts with custom JS

Wondering if anyone else is running into a similar issue. I am trying to wrap an pyechart example in a panel pane, but when the example includes JS passthrough code I run into a JSON decoding error.

Outside of panel, pyecharts can generate the HTML just fine.
This example is pulled from Document

import panel as pn
pn.extension('echarts')

from pyecharts import options as opts
from pyecharts.charts import Bar
from pyecharts.commons.utils import JsCode
from pyecharts.globals import ThemeType

list2 = [
    {"value": 12, "percent": 12 / (12 + 3)},
    {"value": 23, "percent": 23 / (23 + 21)},
    {"value": 33, "percent": 33 / (33 + 5)},
    {"value": 3, "percent": 3 / (3 + 52)},
    {"value": 33, "percent": 33 / (33 + 43)},
]

list3 = [
    {"value": 3, "percent": 3 / (12 + 3)},
    {"value": 21, "percent": 21 / (23 + 21)},
    {"value": 5, "percent": 5 / (33 + 5)},
    {"value": 52, "percent": 52 / (3 + 52)},
    {"value": 43, "percent": 43 / (33 + 43)},
]

c = (
    Bar(init_opts=opts.InitOpts(theme=ThemeType.LIGHT))
    .add_xaxis([1, 2, 3, 4, 5])
    .add_yaxis("product1", list2, stack="stack1", category_gap="50%")
    .add_yaxis("product2", list3, stack="stack1", category_gap="50%")
    .set_series_opts(
        label_opts=opts.LabelOpts(
            position="right",
            formatter=JsCode(
                "function(x){return Number(x.data.percent * 100).toFixed() + '%';}"
            ),
        )
    ))

c.render("stack_bar_percent.html")   # This works fine without panel

pn.pane.ECharts(c) # This runs into a JSON decode error
1 Like

I’ve added this as feature request to Panel Support JsCode in ECharts when using dicts or pyecharts objects · Issue #2634 · holoviz/panel · GitHub.

Workaround

There is a workaround though. You can render pyecharts your self and put the html output in a HTML pane. Or even the _template of a ReactiveHTML element.

import panel as pn

pn.extension("echarts")

from pyecharts import options as opts
from pyecharts.charts import Bar
from pyecharts.commons.utils import JsCode
from pyecharts.globals import ThemeType
import uuid

list2 = [
    {"value": 12, "percent": 12 / (12 + 3)},
    {"value": 23, "percent": 23 / (23 + 21)},
    {"value": 33, "percent": 33 / (33 + 5)},
    {"value": 3, "percent": 3 / (3 + 52)},
    {"value": 33, "percent": 33 / (33 + 43)},
]

list3 = [
    {"value": 3, "percent": 3 / (12 + 3)},
    {"value": 21, "percent": 21 / (23 + 21)},
    {"value": 5, "percent": 5 / (33 + 5)},
    {"value": 52, "percent": 52 / (3 + 52)},
    {"value": 43, "percent": 43 / (33 + 43)},
]

c = (
    Bar(init_opts=opts.InitOpts(theme=ThemeType.LIGHT))
    .add_xaxis([1, 2, 3, 4, 5])
    .add_yaxis("product1", list2, stack="stack1", category_gap="50%")
    .add_yaxis("product2", list3, stack="stack1", category_gap="50%")
    .set_series_opts(
        label_opts=opts.LabelOpts(
            position="right",
            formatter=JsCode("function(x){return Number(x.data.percent * 100).toFixed() + '%';}"),
        )
    )
)


def pyecharts_to_html(chart, chart_id=None):
    if not chart_id:
        chart_id = str(uuid.uuid4())
    chart_id = chart_id.replace("-", "_").replace(" ", "_")

    return f"""
    <div id="{chart_id}" style="height:100%;width:100%"></div>
    <script>
    element_{chart_id}=document.getElementById("{chart_id}")
    options_{chart_id}={c.dump_options()}
    chart_{chart_id} = echarts.init(element_{chart_id}, 'light', {{renderer: 'canvas'}});
    chart_{chart_id}.setOption(options_{chart_id});
    </script>
    """


echarts_html = pyecharts_to_html(c)
echart_pane = pn.pane.HTML(echarts_html, height=400, sizing_mode="stretch_width")

pn.template.FastListTemplate(
    site="Awesome Panel", title="PyEcharts with JsCode", main=[echart_pane],
    header_background="#03b6fc", header_accent_base_color="white",
).servable()

Thank you @Marc for both the feature request and the workaround!

1 Like

I have discovered using Echarts in Panel. It is more cumbersome to work with than Holoviews, but it is more powerful in configurability. I would very much like to understand how to use the function() I see embedded in the dict examples while embedding the charts in Panel. I think this would allow me to keep the JS code to a minimum (analogous to lambda functions) and continue to do the heavy lifting in Python. Is there any Panel documentation on how to use an Echarts dict with custom JS instead of using the render output from pyecharts, or is this capability an enhancement for the future based on the referenced github post? It is not clear to me how to use an Echarts dict in a ReactiveHTML element.

Thanks for any pointers!