天天看点

【Web开发】Python实现Web图表功能(pyecharts,Flask)

<font color=purple face=华文行楷 size="5">"柳丝榆荚自芳菲,不管桃飘与李飞;"

1、简介

A Python Echarts Plotting Library.

Apache Echarts 是一个由百度开源的数据可视化,凭借着良好的交互性,精巧的图表设计,得到了众多开发者的认可。而 Python 是一门富有表达力的语言,很适合用于数据处理。当数据分析遇上数据可视化时,pyecharts 诞生了。

【Web开发】Python实现Web图表功能(pyecharts,Flask)

2、Flask + pyecharts

如何在 Flask 中使用 pyecharts。

2.1 Flask 模板渲染

$ mkdir pyecharts-flask-demo
$ cd pyecharts-flask-demo
$ mkdir templates
           

将 pyecharts 模板,位于 pyecharts.render.templates 拷贝至刚新建的 templates 文件夹。

  • server.py
from flask import Flask
from jinja2 import Environment, FileSystemLoader
from pyecharts.globals import CurrentConfig
from markupsafe import Markup

# 关于 CurrentConfig,可参考 [基本使用-全局变量]
CurrentConfig.GLOBAL_ENV = Environment(loader=FileSystemLoader("./templates"))

from pyecharts import options as opts
from pyecharts.charts import Bar


app = Flask(__name__, static_folder="templates")


def bar_base() -> Bar:
    c = (
        Bar()
        .add_xaxis(["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"])
        .add_yaxis("商家A", [5, 20, 36, 10, 75, 90])
        .add_yaxis("商家B", [15, 25, 16, 55, 48, 8])
        .set_global_opts(title_opts=opts.TitleOpts(title="Bar-基本示例", subtitle="爱看书的小沐"))
    )
    return c

@app.route("/")
def index():
    c = bar_base()
    return Markup(c.render_embed())

if __name__ == "__main__":
    app.run()
           
【Web开发】Python实现Web图表功能(pyecharts,Flask)

2.2 Flask 前后端分离

前后端分离可以使用动态更新数据,增量更新数据等功能。

新建一个 HTML 文件。

新建 HTML 文件保存位于项目根目录的 templates 文件夹,这里以如下 index.html 为例. 主要用到了 jquery 和 pyecharts 管理的 echarts.min.js 依赖。

  • index.html:
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>动态更新数据</title>
    <script src="https://cdn.bootcss.com/jquery/3.0.0/jquery.min.js"></script>
    <script type="text/javascript" src="https://assets.pyecharts.org/assets/echarts.min.js"></script>
</head>
<body>
    <div id="bar" style="width:1000px; height:600px;"></div>
    <script>
      (
        function () {
            var result_json = '{{ result_json|tojson }}';
            // var result = JSON.parse(result_json);
            var chart = echarts.init(document.getElementById('bar'), 'gray', {renderer: 'canvas'});
            $.ajax({
                type: "GET",
                url: "http://127.0.0.1:5000/barChart",
                dataType: 'json',
                data: {result: result_json},
                success: function (result) {
                    chart.setOption(result);
                }
            });
        }
    )
    </script>
</body>
</html>
           
  • app.py:
from random import randrange
from flask import Flask, render_template
from pyecharts import options as opts
from pyecharts.charts import Bar

app = Flask(__name__, static_folder="templates")

def bar_base() -> Bar:
    c = (
        Bar()
        .add_xaxis(["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"])
        .add_yaxis("商家A", [randrange(0, 100) for _ in range(6)])
        .add_yaxis("商家B", [randrange(0, 100) for _ in range(6)])
        .set_global_opts(title_opts=opts.TitleOpts(title="Bar-基本示例", subtitle="我是副标题"))
    )
    return c


@app.route("/")
def index():
    return render_template("index.html")


@app.route("/barChart")
def get_bar_chart():
    c = bar_base()
    return c.dump_options_with_quotes()


if __name__ == "__main__":
    app.run()
           

2.3 定时全量更新图表

前端主动向后端进行数据刷新

定时刷新的核心在于 HTML 的 setInterval 方法。

  • index.html:
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Awesome-pyecharts</title>
    <script src="https://cdn.bootcss.com/jquery/3.0.0/jquery.min.js"></script>
    <script type="text/javascript" src="https://assets.pyecharts.org/assets/echarts.min.js"></script>

</head>
<body>
    <div id="bar" style="width:1000px; height:600px;"></div>
    <script>
        var chart = echarts.init(document.getElementById('bar'), 'white', {renderer: 'canvas'});

        $(
            function () {
                fetchData(chart);
                setInterval(fetchData, 2000);
            }
        );

        function fetchData() {
            $.ajax({
                type: "GET",
                url: "http://127.0.0.1:5000/barChart",
                dataType: 'json',
                success: function (result) {
                    chart.setOption(result);
                }
            });
        }
    </script>
</body>
</html>
           
  • app.py:

    代码同上。

2.4 定时增量更新图表

  • index.html:
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Awesome-pyecharts</title>
    <script src="https://cdn.bootcss.com/jquery/3.0.0/jquery.min.js"></script>
    <script type="text/javascript" src="https://assets.pyecharts.org/assets/echarts.min.js"></script>

</head>
<body>
    <div id="bar" style="width:1000px; height:600px;"></div>
    <script>
        var chart = echarts.init(document.getElementById('bar'), 'white', {renderer: 'canvas'});
        var old_data = [];
        $(
            function () {
                fetchData(chart);
                setInterval(getDynamicData, 2000);
            }
        );

        function fetchData() {
            $.ajax({
                type: "GET",
                url: "http://127.0.0.1:5000/lineChart",
                dataType: "json",
                success: function (result) {
                    chart.setOption(result);
                    old_data = chart.getOption().series[0].data;
                }
            });
        }

        function getDynamicData() {
            $.ajax({
                type: "GET",
                url: "http://127.0.0.1:5000/lineDynamicData",
                dataType: "json",
                success: function (result) {
                    old_data.push([result.name, result.value]);
                    chart.setOption({
                        series: [{data: old_data}]
                    });
                }
            });
        }

    </script>
</body>
</html>
           
  • app.py:
from random import randrange

from flask.json import jsonify
from flask import Flask, render_template

from pyecharts import options as opts
from pyecharts.charts import Line


app = Flask(__name__, static_folder="templates")


def line_base() -> Line:
    line = (
        Line()
        .add_xaxis(["{}".format(i) for i in range(10)])
        .add_yaxis(
            series_name="",
            y_axis=[randrange(50, 80) for _ in range(10)],
            is_smooth=True,
            label_opts=opts.LabelOpts(is_show=False),
        )
        .set_global_opts(
            title_opts=opts.TitleOpts(title="动态数据"),
            xaxis_opts=opts.AxisOpts(type_="value"),
            yaxis_opts=opts.AxisOpts(type_="value"),
        )
    )
    return line


@app.route("/")
def index():
    return render_template("index.html")


@app.route("/lineChart")
def get_line_chart():
    c = line_base()
    return c.dump_options_with_quotes()

idx = 9

@app.route("/lineDynamicData")
def update_line_data():
    global idx
    idx = idx + 1
    return jsonify({"name": idx, "value": randrange(50, 80)})

if __name__ == "__main__":
    app.run()
           
【Web开发】Python实现Web图表功能(pyecharts,Flask)

3、Flask + echarts.js

3.1 直接渲染

  • app.py:
from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

if __name__ == '__main__':
    app.run(debug=True)
           
  • index.html:
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>第一个 ECharts 实例</title>
    <!-- 引入 echarts.js -->
    <script src="https://cdn.staticfile.org/echarts/4.3.0/echarts.min.js"></script>
</head>
<body>
    <!-- 为ECharts准备一个具备大小(宽高)的Dom -->
    <div id="main" style="width: 600px;height:400px;"></div>
    <script type="text/javascript">
        // 基于准备好的dom,初始化echarts实例
        var myChart = echarts.init(document.getElementById('main'));
 
        // 指定图表的配置项和数据
        var option = {
                legend: {},
                tooltip: {},
                dataset: {
                    source: [
                        ['product', '2012', '2013', '2014', '2015'],
                        ['Matcha Latte', 41.1, 30.4, 65.1, 53.3],
                        ['Milk Tea', 86.5, 92.1, 85.7, 83.1],
                        ['Cheese Cocoa', 24.1, 67.2, 79.5, 86.4]
                    ]
                },
                xAxis: [
                    {type: 'category', gridIndex: 0},
                    {type: 'category', gridIndex: 1}
                ],
                yAxis: [
                    {gridIndex: 0},
                    {gridIndex: 1}
                ],
                grid: [
                    {bottom: '55%'},
                    {top: '55%'}
                ],
                series: [
                    // 这几个系列会在第一个直角坐标系中,每个系列对应到 dataset 的每一行。
                    {type: 'bar', seriesLayoutBy: 'row'},
                    {type: 'bar', seriesLayoutBy: 'row'},
                    {type: 'bar', seriesLayoutBy: 'row'},
                    // 这几个系列会在第二个直角坐标系中,每个系列对应到 dataset 的每一列。
                    {type: 'bar', xAxisIndex: 1, yAxisIndex: 1},
                    {type: 'bar', xAxisIndex: 1, yAxisIndex: 1},
                    {type: 'bar', xAxisIndex: 1, yAxisIndex: 1},
                    {type: 'bar', xAxisIndex: 1, yAxisIndex: 1}
                ]
            }

        // 使用刚指定的配置项和数据显示图表。
        myChart.setOption(option);
    </script>
</body>
</html>
           
【Web开发】Python实现Web图表功能(pyecharts,Flask)

3.2 模板渲染

例子: 模板
  • app.py:
import pandas as pd
from flask import Flask, render_template

datas = {}
datas2 = {}
df = pd.read_csv('data/datum_shift.csv')
df = df.sort_values('AREA_SOUTH_BOUND_LAT', ascending=False)

for item in df.head().values:
    datas[item[0]] = item[1]
    datas2[item[0]] = item[2]
    
app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html', datas=datas)

if __name__ == '__main__':
    app.run(debug=True)

           
  • index.html:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>房源占比前五的饼图</title>
    <script src="./static/js/echarts.min.js"></script>
</head>
<body>
<div id="main" style="width:1000px;height:400px"></div>
<script type="text/javascript">
      // 基于准备好的dom,初始化echarts实例
      var myChart = echarts.init(document.getElementById('main'));

      option = {
      title: {
        text: 'datum_shift',
        subtext: '饼图练习',
        left: 'center'
      },
      tooltip: {
        trigger: 'item'
      },
      legend: {
        orient: 'vertical',
        left: 'left'
      },
      series: [
        {
          name: '各项占比',
          type: 'pie',
          radius: '50%',
          data: [
               {% for data in datas %}
               {value:{{ datas[data] }}, name: '{{data}}'},
               {% endfor %}
          ],
          emphasis: {
            itemStyle: {
              shadowBlur: 10,
              shadowOffsetX: 0,
              shadowColor: 'rgba(0, 0, 0, 0.5)'
            }
          }
        }
      ]
    };

   // 使用刚指定的配置项和数据显示图表。
   myChart.setOption(option);

</script>
</body>
</html>
           
【Web开发】Python实现Web图表功能(pyecharts,Flask)

3.3 异步请求$.ajax

例子:$.ajax

创建目录如下:

【Web开发】Python实现Web图表功能(pyecharts,Flask)
  • test_flask.py:
import json
from flask import Flask, request, jsonify,render_template
app = Flask(__name__)


@app.route('/test')
def hello_world():
    return 'Hello World,爱看书的小沐!'

@app.route("/")
def index():
    return render_template("index.html")

@app.route('/getdata_bar')
def getdata_bar():
    language = ['python', 'java', 'c', 'c++', 'c#', 'php']
    value = ['100', '150', '100', '90', '80', '90']
    return json.dumps({'language':language,'value':value},ensure_ascii=False) 

@app.route('/getdata_pie')
def getdata_pie():
    data = [
        {"value": 235, "name": '视频广告'},
        {"value": 274, "name": '联盟广告'},
        {"value": 310, "name": '邮件营销'},
        {"value": 335, "name": '直接访问'},
        {"value": 400, "name": '搜索引擎'},
        {"value": 90, "name": '其他'},
    ],

    return json.dumps({'data':data},ensure_ascii=False) 

if __name__ == '__main__':
    app.run(host="0.0.0.0", port=8080)
           
  • index.html:
<head>
    <meta charset="UTF-8">
    <title>Echarts</title>
	<script src="https://cdn.bootcss.com/jquery/3.0.0/jquery.min.js"></script>
    <script src="/static/js/echarts.min.js"></script>

    <style>
        body {
        margin: 100px;
        }
        .flex-div{
        display: flex;
        width: auto;
        }
    </style>
</head>
<body>
    <!-- 为ECharts准备一个具备大小(宽高)的Dom -->
    <div class="flex-div">
    <div id="echarts_lang" style="width: 600px;height:400px;"></div>
    <div id="echarts_ad" style="width: 600px;height:400px;"></div>
    <script type="text/javascript">
         $(function () {
            // 基于准备好的dom,初始化echarts实例
            var myChart = echarts.init(document.getElementById('echarts_lang'));
            $.ajax({
                url:'/getdata_bar',
                success:function (data) {
                    json_data=JSON.parse(data)

                    console.info(json_data['language'])
                    console.info(json_data['value'])

                    var option = {
                        title: {
                            text: '人数统计'
                        },
                        tooltip: {},
                        legend: {
                            data:['2022年销量']
                        },
                        xAxis: {
                            data: json_data['language']
                        },
                        yAxis: {},
                        series: [{
                            name: '2022年销量',
                            type: 'bar',
                            data: json_data['value']
                        }]
                    };
                   
                    myChart.setOption(option);
                }
            })
        })

        $(function () {
            // 基于准备好的dom,初始化echarts实例
            var myChart = echarts.init(document.getElementById('echarts_ad'));
            $.ajax({
                url:'/getdata_pie',
                success:function (data) {
                    json_data=JSON.parse(data)
                    console.info(json_data['data'])
 
                    option = {
                        series : [
                            {
                                name: '访问来源',
                                type: 'pie',
                                radius: '55%',
                                data: json_data['data'][0],
                                roseType: 'angle',
                                itemStyle: {
                                    normal: {
                                        shadowBlur: 200,
                                        shadowColor: 'rgba(0, 0, 0, 0.5)'
                                    }
                                }
                            }
                        ],
                    };
                    console.info(option)
                    myChart.setOption(option);
                }
            })
        })

    </script>
</body>
           
【Web开发】Python实现Web图表功能(pyecharts,Flask)
【Web开发】Python实现Web图表功能(pyecharts,Flask)

3.4 异步请求$.get

例子2: $.get
  • app.py:
from flask import Flask, request, jsonify,render_template
app = Flask(__name__)

categories=["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
data=[5, 20, 36, 10, 10, 20]
data2=[111, 222, 80, 150, 75, 55]

app = Flask (__name__)

@app.route('/test')
def hello_world():
    return 'Hello World,爱看书的小沐!'

@app.route('/', methods=["GET"])
def index():
     return render_template("index.html")

@app.route('/echarts', methods=["GET"]) 
def echarts():
    return jsonify(categories = categories,data = data, data2 = data2)

if __name__ == '__main__':
    app.run(host="0.0.0.0", port=8080)
           
  • index.html:
<!DOCTYPE html>
<html style="height: 100%" lang="en">

<head>
    <meta charset="utf-8">
    <title>My Finance</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
    <script src="https://cdn.staticfile.org/echarts/4.8.0/echarts.min.js"></script>
    <!-- 引入 vintage 主题 -->
</head>

<body>
    <!-- 为ECharts准备一个具备大小(宽高)的Dom -->
    <div id="main" style="width:1000px;height:300px;"></div>

    <script type="text/javascript">
        // 基于准备好的dom,初始化echarts实例
        var myChart = echarts.init(document.getElementById('main'));
        myChart.setOption({
            title: {
                text: '$.get异步数据加载示例(爱看书的小沐)'
            },
            tooltip: {},
            legend: {
                data: ['1月销量', '2月销量']
            },
            xAxis: {
                data: []
            },
            yAxis: {},
            series: [{
                name: '1月销量',
                type: 'bar',
                data: []
            },{
                name: '2月销量',
                type: 'bar',
                data: []
            }]
        });

        // 异步加载数据
        $.get('/echarts').done(function (data) {
            // 填入数据
            myChart.setOption({
                xAxis: {
                    data: data.categories
                },
                axisLabel: {
                    show: true,
                    interval: 0,
                    rotate: 40,
                    textStyle: {
                        color: '#333'
                    }
                },
                series: [
                    {
                        name: '1月销量',
                        data: data.data
                    },
                    {
                        name: '2月销量',
                        data: data.data2
                    }
                ]
            });
        });

        myChart.setOption(option);
    </script>
</body>

</html>
           
【Web开发】Python实现Web图表功能(pyecharts,Flask)

3.5 Flask + nodejs + vue

对于 Vue 3,你应该使用 npm 上可用的 Vue CLI v4.5 作为 @vue/cli。要升级,你应该需要全局重新安装最新版本的 @vue/cli:

# 全局安装 vue-cli
yarn global add @vue/cli
# 或
cnpm install -g @vue/cli
           
【Web开发】Python实现Web图表功能(pyecharts,Flask)

安装完后查看版本:

$ vue --version
           
【Web开发】Python实现Web图表功能(pyecharts,Flask)

然后在 Vue 项目中运行:

vue upgrade --next
           

vue create、vue ui、vue init三种方式创建Vue项目。

vue create命令是vue-cli3.x提供创建Vue项目的方式,模板是固定的,模板选项可自由配置。

vue ui命令也是vue-cli3.x提供创建Vue项目的方式,可以通过操作可视化页面来创建和管理Vue项目。

vue init命令是vue-cli2.x提供创建Vue项目的方式,可以使用github上面的一些模板来初始化项目。比如webpack就是官方推荐的标准模板。

创建项目:

vue init webpack vue3-test
           
【Web开发】Python实现Web图表功能(pyecharts,Flask)
【Web开发】Python实现Web图表功能(pyecharts,Flask)
cd vue3-test
npm run dev
           
【Web开发】Python实现Web图表功能(pyecharts,Flask)

访问网址:http://localhost:8080

【Web开发】Python实现Web图表功能(pyecharts,Flask)
npm install --save echarts vue-jsonp vue-resource
           
【Web开发】Python实现Web图表功能(pyecharts,Flask)
  • D:\0627\vue3-test\src\main.js:
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.

import Vue from 'vue'
import App from './App.vue'
import VueResource from 'vue-resource'
import * as VueJsonp from 'vue-jsonp'
// import echarts from 'echarts'
import * as echarts from 'echarts';

Vue.use(VueJsonp)
Vue.use(VueResource)
Vue.prototype.$echarts = echarts

new Vue({
    el: '#app',
    render: h => h(App)
})

           
  • D:\0627\vue3-test\src\App.vue:
<template>
  <div class="main">
    <chart1 />
  </div>
</template>
<script>
import chart1 from "./components/chart1.vue";

export default {
  components: {
    chart1,
  },
};
</script>
           
  • D:\0627\vue3-test\src\components\chart1.vue:
<template>
  <div>
    <div id="echartContainer" style="width: 100%; height: 500px"></div>
  </div>
</template>

<script>
export default {
  name: "chart1",
  data() {
    return {};
  },
  methods: {
    draw() {
      var myChart = this.$echarts.init(
        document.getElementById("echartContainer"),
        "infographic"
      );
      myChart.setOption({
        xAxis: {},
        yAxis: {},
        series: [
          {
            symbolSize: 5,
            data: [],
            type: "bar",
          },
        ],
      });
      this.$http
        .get("http://localhost:5000/api/demo/", {
          headers: { "Access-Control-Allow-Origin": "*" },
        })
        .then((res) => {
          console.log(res.data);
          myChart.hideLoading();
          myChart.setOption({ series: [{ data: res.data.product }] });
        });
    },
  },
  mounted() {
    this.draw();
  },
};
</script>

<style></style>
           
  • test_flask:
from flask import Flask, jsonify, render_template
from flask.helpers import make_response
from flask_cors import CORS

app = Flask(__name__,
    static_folder='./dist',  #设置静态文件夹目录
    template_folder = "./dist",
    static_url_path="")  #设置vue编译输出目录dist文件夹,为Flask模板文件目录
CORS(app, resources=r'/*')

@app.route('/', methods=["GET"])
def index():
     return render_template("index.html")

@app.route('/api/demo/')
def api_test():
    ans = jsonify({
        "product": [5, 20, 36, 10, 10, 20]
    })
    return make_response(ans)


if __name__ == '__main__':
    app.run(debug=True)
           
【Web开发】Python实现Web图表功能(pyecharts,Flask)
cd vue3-test
npm run build
npm run dev
           
【Web开发】Python实现Web图表功能(pyecharts,Flask)

访问:http://localhost:8080/

【Web开发】Python实现Web图表功能(pyecharts,Flask)

以上是开启了两个web服务器(Flask、nodejs).

以下是开一个web服务器(Flask)的方法:

将上面打包的dist文件复制到Flask的主文件夹里,然后运行test_flask.py如下。

结语