关于pyecharts和flask结合的案例不多,查阅了数十篇文章,尝试了若干遍,感觉还是不理想,最大的问题在于对echarts的理解上,对我而言,又需要向上推到ajax,jquery,bootstrap,html,css,javascript等等,有点超出了我的技能范围,所以最大程度的做到能用就够了,复用和进一步优化看起来还是遥遥无期。
关于pyecharts可视化与中国经济、人口等数据系列的笔记也暂时告以段落了。
Flask 模板渲染
Step 0: 新建一个 Flask 项目
mkdir pyecharts-flask-demo
cd pyecharts-flask-demo
mkdir templates
Step 1: 拷贝 pyecharts 模板
将 pyecharts 模板,位于 pyecharts.render.templates 拷贝至刚新建的 templates 文件夹
|--components.html
|--macro
|--nb_components.html
|--nb_jupyter_globe.html
|--nb_jupyter_lab.html
|--nb_jupyter_lab_tab.html
|--nb_jupyter_notebook.html
|--nb_jupyter_notebook_tab.html
|--nb_nteract.html
|--simple_chart.html
|--simple_globe.html
|--simple_page.html
|--simple_tab.html
Step 2: 渲染图表
在项目的根目录下新建server.py
一种是把图表render到templates目录下
一种是直接使用render_embed方式
Flask 前后端分离
前后端分离可以使用动态更新数据,增量更新数据等功能。
Step 0,Step 1 参见上面模板渲染章节内容
Step 3: 新建一个 HTML 文件, 新建 HTML 文件保存位于项目根目录的 templates 文件夹
Step 4: 编写 flask 和 pyecharts 代码渲染图表,将代码保存项目的根目录下。
定时全量更新图表
前端主动向后端进行数据刷新
定时刷新的核心在于 HTML 的 setInterval 方法。
带参数访问方式,也是采用render_embed方式,只不过是把参数又传递给图表了
例
代码语言:javascript复制import os
from random import randrange
from flask.json import jsonify
from flask importFlask
from flask import render_template
from jinja2 importMarkup, Environment, FileSystemLoader
import random
from flask importFlask, render_template
from flask import request
from pyecharts.globals importCurrentConfig
from pyecharts.faker importFaker
from pyecharts import options as opts
from pyecharts.charts importBar,Line
from pyecharts import options as opts
from pyecharts.charts importGauge, Page,Map,Scatter
# 关于 CurrentConfig,可参考 [基本使用-全局变量]
#CurrentConfig.GLOBAL_ENV = Environment(loader=FileSystemLoader("./templates"))
app = Flask(__name__, static_folder="templates")
# render()方法:默认会在当前目录生成 render.html 文件
# render()方法传入路径参数:传入路径参数,如 bar.render(“mycharts.html”),这种方法好一点,可以设定文件路径
# render_notebook()方法:这个方法可用在notebook中
# render_embed()方法:来自pyecharts的flask一章中的Markup(c.render_embed())
# chart.dump_options()方法:这个方法能和flask配合不错,能够实现一个flask网页中绘制很多个图表;
# 然而却依然需要自己引入echarts.js文件、自己设定div、自己初始化echarts对象、自己给echarts对象设置图表配置,唯一简化的就是图表配置是来自于python服务端;
# 获取全局 options,JSON 格式(JsCode生成的函数不带引号)
# def dump_options() -> str:
# 获取全局 options,JSON 格式(JsCode生成的函数带引号,在前后端分离传输数据时使用)
# def dump_options_with_quotes() -> str:
# 从实际执行的角度,这两种方法暂时没发现有什么区别,先人云亦云的练习吧
# 生成bart图
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
# 生成line图
def line_base() -> Line:
c = (
Line()
.add_xaxis(Faker.choose())
.add_yaxis("商家A", Faker.values())
.add_yaxis("商家B", Faker.values())
.set_global_opts(title_opts=opts.TitleOpts(title="Line-基本示例"))
)
return c
# 仪表盘图
def gauge_base() -> Gauge:
c = (
Gauge()
.add("", [("完成率", 66.6)])
.set_global_opts(title_opts=opts.TitleOpts(title="Gauge-基本示例"))
)
return c
# 地图map
def map_base() -> Map:
c = (
Map()
.add("商家A", [list(z) for z in zip(Faker.provinces, Faker.values())], "china")
.set_global_opts(title_opts=opts.TitleOpts(title="Map-基本示例"))
)
return c
#散点图
def scatter_base() -> Scatter:
c = (
Scatter()
.add_xaxis(Faker.choose())
.add_yaxis("商家A", Faker.values())
.set_global_opts(title_opts=opts.TitleOpts(title="Scatter-基本示例"))
)
return c
# 新增全量更新数据
def full_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
# 新增动态更新数据
def incre_line_base() -> Line:
line = (
Line()
.add_xaxis(["{}".format(i) for i in range(10)]) #初始化10个值
.add_yaxis(
series_name="",
y_axis=[randrange(50, 80) for _ in range(10)], #在50-80循环出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
# 生成bart图,带参数
def bar_base_withpara(title,ydata) -> Bar:
c = (
Bar()
.add_xaxis(["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"])
.add_yaxis("商家A", ydata)
.set_global_opts(title_opts=opts.TitleOpts(title=title, subtitle="动态标题"))
)
return c
# render_embed方法
@app.route("/render_embed")
def render_embed():
c = bar_base()
returnMarkup(c.render_embed())
# render到文件方法,注意render的缺省工作目录和flask的template工作目录不同
@app.route("/render")
def renderhtml():
filename='terenderfile.html'
currentpath=os.path.abspath('.')
pathfilename=os.path.join(currentpath, 'templates',filename)
c = line_base()
c.render(pathfilename)
return render_template(filename)
# 外部访问链路
@app.route('/')
def hello_world():
return'Hello World'
# ----------------------------------------------------------
# 经测试bar,line,scatter和gauge可通过ajax url: "http://127.0.0.1:8080/XXXChart"方式
# 但Map不行,估计是map访问的百度地图资源受限,待核查
# ----------------------------------------------------------
# 外部访问链路,bar图
@app.route("/dumpbar")
def dump_bar():
return render_template("barbaseindex.html")
# barbaseindex.html指向的链路
@app.route("/barChart")
def get_bar_chart():
c = bar_base()
return c.dump_options_with_quotes()
# 外部访问链路,line图
@app.route("/dumpline")
def dump_line():
return render_template("linebaseindex.html")
# linebaseindex.html指向的链路
@app.route("/lineChart")
def get_line_chart():
c = line_base()
return c.dump_options_with_quotes()
# 外部访问链路,scatter图
@app.route("/dumpscatter")
def dump_scatter():
return render_template("scatterbaseindex.html")
# scatterbaseindex.html指向的链路
@app.route("/scatterChart")
def get_scatter_chart():
c = scatter_base()
return c.dump_options_with_quotes()
# 外部访问链路,gauge图
@app.route("/dumpgauge")
def dump_gauge():
return render_template("gaugebaseindex.html")
# gaugebaseindex.html指向的链路
@app.route("/gaugeChart")
def get_gauge_chart():
c = gauge_base()
return c.dump_options()
#return c.dump_options_with_quotes()
# 通过mapbaseindex.html对http://127.0.0.1:8080/mapChart"调用,只能看到标题,无法打开地图
# # 外部访问链路,map图
# @app.route("/dumpmap")
# def dump_map():
# return render_template("mapbaseindex.html")
#
# # mapbaseindex.html指向的链路
# @app.route("/mapChart")
# def get_map_chart():
# c = map_base()
# return c.dump_options_with_quotes()
# 外部访问链路,bar图
@app.route("/dumpfullbar")
def dump_full_bar():
return render_template("fullupdatebarbase.html")
# barbaseindex.html指向的链路
@app.route("/fullbarChart")
def get_full_bar_chart():
c = full_bar_base()
return c.dump_options_with_quotes()
# 外部访问链路,动态新增值的line图
@app.route("/dumpincreline")
def index():
return render_template("increupdatelinebase.html")
# increupdatelinebase.html指向的链路
@app.route("/increlineChart")
def get_incre_line_chart():
c = incre_line_base()
return c.dump_options_with_quotes()
idx = 9
# 生成动态新增数据值,在increupdatelinebase.html的http://127.0.0.1:8080/lineDynamicData被引用
@app.route("/lineDynamicData")
def update_line_data():
global idx
idx = idx 1
return jsonify({"name": idx, "value": randrange(50, 80)})
@app.route("/dumpbarwithpara")
def dump_bar_withpara():
return render_template("barbasewithparaindex.html")
@app.route("/barChartwithpara", methods=['post','get'])
def get_bar_chart_withpara():
title = request.args.get('title')
type = request.args.get('type')
ydata=list(randrange(1,100) for i in range(6))
c = bar_base_withpara(title, ydata)
returnMarkup(c.render_embed())
if __name__ == "__main__":
app.run('127.0.0.1', '8080', debug=True)
关于barbaseindex.html文件
代码示例
代码语言:javascript复制<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Awesome-pyecharts</title>
<script src="templates/jquery-3.4.1.min.js"></script>
<script type="text/javascript" src="templates/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:8080/fullbarChart",
dataType: 'json',
success: function (result) {
chart.setOption(result);
}
});
}
</script>
</body>
</html>