Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Events support for Panel Echarts #2147

Closed
JackDapid opened this issue Apr 8, 2021 · 12 comments
Closed

Events support for Panel Echarts #2147

JackDapid opened this issue Apr 8, 2021 · 12 comments
Labels
type: enhancement Minor feature or improvement to an existing feature

Comments

@JackDapid
Copy link
Contributor

As a big fan of https://awesome-panel.org/echarts by @MarcSkovMadsen and https://panel.holoviz.org/reference/panes/ECharts.html I am using echarts a lot and got frustrated that they seemed to have dropped events / JS callback support from pyecharts? - see pyecharts/pyecharts#1671
Without 'interactivity" the nice echarts are only half useful.

Clearly forking an pyecharts with bringing in that support is a n option, but I am looking for a discussion if there are some other ideas that could benefit other Panel echarts users.

@MarcSkovMadsen
Copy link
Collaborator

One thing is that you could turn this into a feature request for events support for the Panel Echarts pane :-)

Additional Context

Regarding the api for adding support for events inspiration could be drawn from the panel-highcharts package I recently made. There are lots of event but I setup a "clever" mechanism to subscribe to the ones I need.

For example for the Network example you subscribe using

image

and get something like shown here

https://twitter.com/i/status/1378418244978106374

@JackDapid
Copy link
Contributor Author

Hi @MarcSkovMadsen thank you. panel-highcharts is really nice, I didn't see it on awesome-panel webpage before, but learned now to follow you on twitter for such news :-).
Ok I see I was misinterpreting:

The Echarts object being wrapped. Can be an Echarts dictionary or a pyecharts chart""")

that Panel Echarts pane is using pyecharts, but now (awake!) I see it is not and I really like your subscription mechanism. WIll have some time hopefully over the weekend to look in details to panel-highcharts and Panel Echarts pane to see if I can build a minimal demo following the mechanism.

@JackDapid
Copy link
Contributor Author

I checked panel-highcharts - really nice @MarcSkovMadsen - can't use it for the current project, but sure I will find a good use for one of the next projects.

For events in panel.Echarts happy to try to implement something similar, but I am a liitle stuck with panel model logic (see below), so currently I use a workaround inspired by @nghenzi from: #1883

Workaround

  1. create a Tree via pyecharts 'tree = Tree(...)'
  2. get a html vis pyecharts 'html = tree.render_embed()'
  3. adding the JS to the <script> manually (as pyecharts dropped support for 'mychart.on()')
var url = 'https://www.TEST.de/AssetDetail.aspx?AssetId=';
function openLink(e) {
    if ("data" in e) {
        if ("id" in e.data) {
            console.log(e.data.id)
            window.open(url + e.data.id, '_blank').focus();
        }
    }
}
myChart.on('click', 'series.tree', e => openLink(e));
  1. render via 'pn.pane.HTML(html)'

WORKS, but not really nice.

Understanding echarts in Panel

  1. How the echarts pane
    def _get_model(self, doc, root=None, parent=None, comm=None):
    creates an echart model via https://github.com/holoviz/panel/blob/master/panel/models/echarts.py I understood well
  2. Already stuck? YES - I don't get how the model mechanism finds its way, I assume? via some ._get_model() to https://github.com/holoviz/panel/blob/master/panel/models/echarts.ts - so pretty lost what at the end transforms the JSON like object to an echarts understandable (true/false and not True/False!) almost like JSON that could than be enriched with JS events subscriptions (following the events example and notation of your example) by adding 'myChart.on(..,)' and eventually in a similar way functions like openLink(e).

@MarcSkovMadsen
Copy link
Collaborator

MarcSkovMadsen commented Apr 12, 2021

Hi @JackDapid

I took a look at the Echarts Events.

My suggestion to get this start would be start a PR called echarts-events or similar and

Echarts Pane

In the ECharts pane add two parameters

event_config=param.Dict()

Would configure the events. For example setting

chart.event_config = {
    'click': None,
    'legendselectchanged: None,
    'click': 'series.line',
    'mouseover', {'seriesIndex': 1, 'name': 'xx'},
}

would correspond to

myChart.on('click', function (params) {
    console.log(params);
});

myChart.on('legendselectchanged', function (params) {
    console.log(params);
});

chart.on('click', 'series.line', function (params) {
    console.log(params);
});

chart.on('mouseover', {seriesIndex: 1, name: 'xx'}, function (params) {
    console.log(params);
});

event=param.Dict()

This would be where the events are output in Python/ on the server.

ECharts Python Bokeh Model

In the ECharts Bokeh Model add the corresponding properties

event_config=Dict(String, Any)
event=Dict(String,Any)

ECharts TypeScript Bokeh Model

In the ECharts TypeScript Bokeh Model start from the bottom by adding the event_config and event properties by copy-pasting the existing data property.

image

When done try to test that you can panel build panel successfully and still run some echarts examples.

You will then need to handle creating and reacting to events via the render and connect_signals functions. Keep the iterations small, test that things work and learn. Use console.log a lot to understand what is going on :-)

If you push your PR and ask questions there it will be very easy to help out.

Sending back the params

Record which event is being reacted to

You will have to enable the user on the Python side to understand which event type that is arriving. You will probably just add something like

event: {'click', 'series.line'} to the params/ event you send back.

filterEventData

The EChart events will give you some params. They will probably contain lots of information that should not be send back to the server for efficiency reasons. In the Panel HighBase Model I implemented a filterEventData function to clean up and only send back the most relevant information. You could draw inspiration from that.

image

Sending back identical events

When for example hovering or clicking a point the same params may be triggered. Normally bokeh/ param does not trigger events when properties/ parameters are re-assigned the same value. Thus in the HigBase model I add a uuid value to make the params/ event unique.

Panel Development

You can find a description of how to setup a Panel development environment here.

Happy coding 👍

@JackDapid
Copy link
Contributor Author

Hi @MarcSkovMadsen,

thanks and will start like that. I think we have been talking about two different things - maybe due to my naive assumption? I was expecting that the 'Panel Echarts Pane' was transforming the data object at some point fully into JS, pretty much like pyecharts html = tree.render_embed() and pn.pane.HTML(html) and than all would run on the client side.
But you mentioning:

You will have to enable the user on the Python side to understand which event type that is arriving

and:

lots of information that should not be send back to the server

I think I understood now what you mean by "clever" way, that fully allows to interact with events on the python level and not just allowing to add some JS functions to the JS on the server side. like below.

But I am certain that I now understand better what you meant and agree that makes sense to concentrate on that as the initial first step I had in mind of enriching with client side JS only would not add functionality more than what html = tree.render_embed() and pn.pane.HTML(html) already allows. 👍

    <script>
        var chart_80fafe164d1944fd8bc752a1a03daa15 = echarts.init(
            document.getElementById('80fafe164d1944fd8bc752a1a03daa15'), 'white', {renderer: 'canvas'});

<!-- START ADDITION FOR JS EVENT FUNCTIONALITY -->
            var url = 'https://www.TEST.de/AssetDetail.aspx?AssetId=';
            function openLink(e) {
                if ("data" in e) {
                    if ("id" in e.data) {
                        console.log(e.data.id)
                        window.open(url + e.data.id, '_blank').focus();
                    }
                }
            }
            chart_80fafe164d1944fd8bc752a1a03daa15.on('click', 'series.tree', e => openLink(e));
<!-- END ADDITION FOR JS EVENT FUNCTIONALITY -->

        var option_80fafe164d1944fd8bc752a1a03daa15 = {
    "animation": true,
    "series": [
        {
            "type": "tree",
            "data": [
                {
                    "name": "Aircraft",
                    "children": [
                        {
                            "name": "\u0422\u0440\u0435\u043d\u0430\u0436\u0435\u0440 / FSTD",
                            "value": 791
                        }
                    ]
                }
            ],
            "symbol": "emptyCircle",
            "symbolSize": 7,
...
            "label": {
                "show": true,
                "position": "top",
                "margin": 8
            },
            "leaves": {
                "label": {
                    "show": true,
                    "position": "top",
                    "margin": 8
                }
            }
        }
    ],
};
        chart_80fafe164d1944fd8bc752a1a03daa15.setOption(option_80fafe164d1944fd8bc752a1a03daa15);
    </script>

@JackDapid
Copy link
Contributor Author

Hi @MarcSkovMadsen, Ok I have a first minimal version, working now on that one can also create some JS handlers only. Was really stupid of me to miss the line this._chart.setOption(this.model.data);:

this._chart.setOption(this.model.data);

I would have expected something more complicated (e.g. some parsing, or True->true, etc), so I guess I was scanning too fast over it.

Ready to push and create a PR. I guess I would need some rights to push a new branch and request a PR for it? Sorry switched to GitLab quite some time ago, so sorry if I missed that there is another way.

@JackDapid
Copy link
Contributor Author

Following your event_config logic I implemented two pure JS events:

chart.event_config = {
    # Opens by an Echarts event a new browser tab with the url composed of the base_url and an identifier from the series data
    'click': {'query': 'series.tree', 'base_url': 'https://www.TEST.de/AssetDetail.aspx?AssetId=', 'identifier': 'id'},
    # Evaluates by an Echarts event a Java Script handler
    'click': {'query': 'series.tree', 'handler': 'e => console.log("I got an event:", e)'}
    'click': None,
    'legendselectchanged: None,
    'click': 'series.line',
    'mouseover', {'seriesIndex': 1, 'name': 'xx'},
}

I hope that use_cases and notation are obvious, but happy if there are some improvement proposals.

@MarcSkovMadsen
Copy link
Collaborator

Sounds great.

Regarding how to open the PR. I would follow the steps in https://jarv.is/notes/how-to-pull-request-fork-github/.

@JackDapid
Copy link
Contributor Author

Thanks, than I propose updating: https://panel.holoviz.org/developer_guide/index.html#cloning-the-repository as it explicitly mentions cloning from the original repo and not the own forked for contributing, can make a proposal for that. Move now communication to the PR

Sounds great.

Regarding how to open the PR. I would follow the steps in https://jarv.is/notes/how-to-pull-request-fork-github/.

@JackDapid
Copy link
Contributor Author

Just for the archives setting up the development environment not so easy on OS X 10.15.7 as described here: https://panel.holoviz.org/developer_guide/index.html due to often described nodejs older version issue as well as a messed up? or just used for many (too many dependencies?) projects before conda base environment that with conda install -c pyviz "pyctdev>0.5.0" tries to resolve for hours , so here my receipt:

YES, the double last command is no mistake as it fails the first time with:

[...]
Preparing transaction: ...working... done
Verifying transaction: ...working... done
Executing transaction: ...working... done
TaskError - taskid:develop_install
CmdAction Error creating command string
Traceback (most recent call last):
  File "/Users/ddobos/anaconda3/envs/panel_dev/lib/python3.7/site-packages/pyctdev/util.py", line 179, in _get_setup_metadata
    from setup import meta
  File "/Users/ddobos/Desktop/NEW/panel/setup.py", line 9, in <module>
    from setuptools.command.develop import develop
  File "/Users/ddobos/anaconda3/envs/panel_dev/lib/python3.7/site-packages/setuptools/command/develop.py", line 8, in <module>
    from setuptools.extern import six
ImportError: cannot import name 'six' from 'setuptools.extern' (/Users/ddobos/anaconda3/envs/panel_dev/lib/python3.7/site-packages/setuptools/extern/__init__.py)
[...]

So here I get it working:

  conda create -n panel_env python=3.7
  conda activate panel_env
  conda install -c pyviz "pyctdev>0.5.0"
  doit env_create -c pyviz/label/dev -c conda-forge --name=panel_dev --python=3.7
  conda deactivate
  conda activate panel_dev
  conda install -c conda-forge nodejs
  conda update -n panel_dev --all
  doit develop_install -c pyviz/label/dev -c conda-forge -c bokeh -o build -o tests -o recommended
  doit develop_install -c pyviz/label/dev -c conda-forge -c bokeh -o build -o tests -o recommended

@philippjfr philippjfr added the type: enhancement Minor feature or improvement to an existing feature label Nov 8, 2021
@philippjfr philippjfr added this to the 0.13.0 milestone Nov 8, 2021
@philippjfr philippjfr modified the milestones: v0.13.0, next Mar 31, 2022
@hakantekbasgit
Copy link

I don't know how many users are preferring echarts. but I believe it is easy to create nice dashboard with echarts. if we can add this feature to echarts, it will be great.

@philippjfr
Copy link
Member

This was implemented in #2174

@philippjfr philippjfr removed this from the next milestone Aug 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement Minor feature or improvement to an existing feature
Projects
None yet
Development

No branches or pull requests

4 participants