flexxui / flexx

Write desktop and web apps in pure Python
http://flexx.readthedocs.io
BSD 2-Clause "Simplified" License
3.25k stars 257 forks source link

How to integrate with pyechart? thanks. #623

Closed ScottHuangZL closed 4 years ago

ScottHuangZL commented 4 years ago

PyEchart leverage Baidu eChart which is great visualization lib https://github.com/pyecharts/pyecharts https://pyecharts.org/#/en-us/

How can I integrate the pyechart into Flexx? In other word, a fancy flexx app to show eChart, thanks.

ScottHuangZL commented 4 years ago

I try create a widget like, it can show the text Echart with id main.

class EChart(flx.Widget):
    def _render_dom(self):
        # Use this to determine the content. This method may return a
        # string, a list of virtual nodes, or a single virtual node
        # (which must match the type produced in _create_dom()).

        # <div id="main" style="width: 600px;height:400px;"></div>
        return [flx.create_element('div', {'id':"main",'style':"width: 600px;height:400px;"},
                    'EChart'),
                ]

Now I need to invoke EChart JS to show charts. Could you advise how to convert below EChart sample to Flexx? thanks.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>ECharts</title>
    <!-- including ECharts file -->
    <script src="echarts.js"></script>
</head>
<body>
    <!-- prepare a DOM container with width and height -->
    <div id="main" style="width: 600px;height:400px;"></div>
    <script type="text/javascript">
        // based on prepared DOM, initialize echarts instance
        var myChart = echarts.init(document.getElementById('main'));

        // specify chart configuration item and data
        var option = {
            title: {
                text: 'ECharts entry example'
            },
            tooltip: {},
            legend: {
                data:['Sales']
            },
            xAxis: {
                data: ["shirt","cardign","chiffon shirt","pants","heels","socks"]
            },
            yAxis: {},
            series: [{
                name: 'Sales',
                type: 'bar',
                data: [5, 20, 36, 10, 10, 20]
            }]
        };

        // use configuration item and data specified to show chart
        myChart.setOption(option);
    </script>
</body>
</html>
ScottHuangZL commented 4 years ago

You samples have how to use Bootstrap, but just show how to use CSS style only.

Can you provide a sample for how to wrap a good JS library to FLexx widgets? For example, how to wrap famous EChart JS to Flexx EChart Widget? thanks.

The end user such as me who just start use Flexx would benefit from it, and can create other widgets according this guide.

Thanks.

almarklein commented 4 years ago

I'm not familiar with EChart, and I don't have a lot of time to work on Flexx right now. But I am happy to answer any specific questions to making it work.

As a starting point you may want to look at how plotly inregration works:

ScottHuangZL commented 4 years ago

Sound good. Let me try it, thanks.

ScottHuangZL commented 4 years ago

I try to according jquery/bootstrap sample to create below widget. However, I encounter JS error at _rendor_dom function. Can you advise how to call JS? Thanks. Use RawJS()?

Below are the JS I would like to invoke.

// based on prepared DOM, initialize echarts instance
        var myChart = echarts.init(document.getElementById('main'));

        // specify chart configuration item and data
        var option = {
            title: {
                text: 'ECharts entry example'
            },
            tooltip: {},
            legend: {
                data:['Sales']
            },
            xAxis: {
                data: ["shirt","cardign","chiffon shirt","pants","heels","socks"]
            },
            yAxis: {},
            series: [{
                name: 'Sales',
                type: 'bar',
                data: [5, 20, 36, 10, 10, 20]
            }]
        };

        // use configuration item and data specified to show chart
        myChart.setOption(option);

Below are the code I try to write:

from flexx import flx
from pscript import RawJS

flx.assets.associate_asset(__name__, 'https://cdnjs.cloudflare.com/ajax/libs/echarts/4.6.0/echarts.min.js')

class Example(flx.Widget):
    options = flx.DictProp({
            'title': {
                'text': 'ECharts entry example'
            },
            'tooltip': {},
            'legend': {
                'data':['Sales']
            },
            'xAxis': {
                'data': ["shirt","cardign","chiffon shirt","pants","heels","socks"]
            },
            'yAxis': {},
            'series': [{
                'name': 'Sales',
                'type': 'bar',
                'data': [5, 20, 36, 10, 10, 20]
            }]
        }
        ,settable = True)

    @flx.action
    def change_echart(self):
        self._mutate_options({
            'title': {
                'text': 'ECharts entry example - updated'
            },
            'tooltip': {},
            'legend': {
                'data':['Sales']
            },
            'xAxis': {
                'data': ["shirt","cardign","chiffon shirt","pants","heels","socks"]
            },
            'yAxis': {},
            'series': [{
                'name': 'Sales',
                'type': 'bar',
                'data': [1, 2, 3, 4, 5, 6]
            }]
        })

    def _create_dom(self):
        # global window
        # node = window.document.createElement('input')
        # RawJS('$')(node).datepicker()
        print("create dom...")
        node = flx.create_element('div',
                    {
                    'id':'main',
                    'style':'width: 600px;height:400px;'
                    },
                    'Hello Flexx Echart at Create Dom!'),
        print("after crate node")
        return node

    def _render_dom(self):
        global window
        print("Start get element")
        myChartElement = window.document.getElementById('main')
        print("Start init eChart")
        **# Next line would error, why?**
        myChart = window.echarts.init(window.document.getElementById('main'))
        print("End init eChart")

        **#How to call below JS????**
        # RawJS("global window; var myChart = echarts.init(window.document.getElementById('main'));")
        # RawJS("""var option = {
        #     title: {
        #         text: 'ECharts entry example'
        #     },
        #     tooltip: {},
        #     legend: {
        #         data:['Sales']
        #     },
        #     xAxis: {
        #         data: ["shirt","cardign","chiffon shirt","pants","heels","socks"]
        #     },
        #     yAxis: {},
        #     series: [{
        #         name: 'Sales',
        #         type: 'bar',
        #         data: [5, 20, 36, 10, 10, 20]
        #     }]
        # };""")
        # RawJS("myChart.setOption(option);")

        print("render dom...")
        return [flx.create_element('span', {},
                    'Hello EChart World at Rendor Dom!',repr(self.options)),

                flx.create_element('br'),
                flx.create_element('button', {'onclick': self.change_echart},
                    'Change option')
                ]

if __name__ == '__main__':
    m = flx.launch(Example, 'chrome-app')
    flx.run()
ScottHuangZL commented 4 years ago

Issue resolved with below code:

from flexx import flx
from pscript import RawJS

import os

# use local assests, I download the echarts.min.js and put in local folder
BASE_DIR = os.getcwd()
with open(BASE_DIR + '/static/echarts/echarts.min.js',encoding="utf-8") as f:
    script = f.read()
flx.assets.associate_asset(__name__, 'echart_script.js', script)
# or use the online CDN. For desktop app, would better use local assets
# flx.assets.associate_asset(__name__, 'https://cdnjs.cloudflare.com/ajax/libs/echarts/4.6.0/echarts.min.js')

class Example(flx.Widget):

    echart_id = "echart_main_default_id"
    echart_options = flx.DictProp({},settable=True,doc="""the echarts options""");

    my_default_option = {
                            'title': {
                                'text': 'ECharts entry example'
                            },
                            'tooltip': {},
                            'legend': {
                                'data':['Sales']
                            },
                            'xAxis': {
                                'data': ["shirt","cardign","chiffon shirt","pants","heels","socks"]
                            },
                            'yAxis': {},
                            'series': [{
                                'name': 'Sales',
                                'type': 'bar',
                                'data': [5, 20, 36, 10, 10, 20]}]
                        };

    @flx.action
    def change_chart(self):
        global myChart
        self.my_option = self.my_default_option
        self.my_option['series'][0]['data'] = [1, 2, 3, 4, 5, 6]
        # print(repr(self.my_option))
        self._mutate_echart_options(self.my_option)
        myChart.setOption(self.echart_options)

    @flx.action
    def reset_chart(self):
        global myChart
        self.my_option = self.my_default_option
        self.my_option['series'][0]['data'] = [5, 20, 36, 10, 10, 20]
        # print(repr(self.my_option))
        self._mutate_echart_options(self.my_option)
        myChart.setOption(self.echart_options)

    def _render_dom(self):
        node =  [
                    flx.create_element('div',
                    {
                    'id':self.echart_id,
                    'style':'width: 600px;height:400px;'
                    },
                    'Hello Flexx Echart at Create Dom!'),
                    flx.create_element('script',{
                    'type':'text/javascript'
                    },"""
                            // based on prepared DOM, initialize echarts instance
                            var myChart = echarts.init(document.getElementById('"""+self.echart_id+"""'));
                    """),

                    flx.create_element('button', {'onclick': self.change_chart},
                    'Change Chart'),

                    flx.create_element('button', {'onclick': self.reset_chart},
                    'Reset Chart'),

                ]
        self.reset_chart()
        return node;

if __name__ == '__main__':
    app = flx.App(Example)
    app.launch("chrome-app")
    flx.run()