返回小栈
关于pyecharts可视化与Flask相结合
bq_wang2020-02-24 14:52:39

关于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方式,只不过是把参数又传递给图表了


  1. import os

  2. from random import randrange


  3. from flask.json import jsonify

  4. from flask importFlask

  5. from flask import render_template

  6. from jinja2 importMarkup, Environment, FileSystemLoader

  7. import random

  8. from flask importFlask, render_template

  9. from flask import request


  10. from pyecharts.globals importCurrentConfig

  11. from pyecharts.faker importFaker

  12. from pyecharts import options as opts

  13. from pyecharts.charts importBar,Line

  14. from pyecharts import options as opts

  15. from pyecharts.charts importGauge, Page,Map,Scatter


  16. # 关于 CurrentConfig,可参考 [基本使用-全局变量]

  17. #CurrentConfig.GLOBAL_ENV = Environment(loader=FileSystemLoader("./templates"))

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


  19. # render()方法:默认会在当前目录生成 render.html 文件

  20. # render()方法传入路径参数:传入路径参数,如 bar.render(“mycharts.html”),这种方法好一点,可以设定文件路径

  21. # render_notebook()方法:这个方法可用在notebook中

  22. # render_embed()方法:来自pyecharts的flask一章中的Markup(c.render_embed())

  23. # chart.dump_options()方法:这个方法能和flask配合不错,能够实现一个flask网页中绘制很多个图表;

  24. # 然而却依然需要自己引入echarts.js文件、自己设定div、自己初始化echarts对象、自己给echarts对象设置图表配置,唯一简化的就是图表配置是来自于python服务端;

  25. # 获取全局 options,JSON 格式(JsCode生成的函数不带引号)

  26. # def dump_options() -> str:

  27. # 获取全局 options,JSON 格式(JsCode生成的函数带引号,在前后端分离传输数据时使用)

  28. # def dump_options_with_quotes() -> str:

  29. # 从实际执行的角度,这两种方法暂时没发现有什么区别,先人云亦云的练习吧


  30. # 生成bart图

  31. def bar_base() -> Bar:

  32. c = (

  33. Bar()

  34. .add_xaxis(["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"])

  35. .add_yaxis("商家A", [5, 20, 36, 10, 75, 90])

  36. .add_yaxis("商家B", [15, 25, 16, 55, 48, 8])

  37. .set_global_opts(title_opts=opts.TitleOpts(title="Bar-基本示例", subtitle="我是副标题"))

  38. )

  39. return c



  40. # 生成line图

  41. def line_base() -> Line:

  42. c = (

  43. Line()

  44. .add_xaxis(Faker.choose())

  45. .add_yaxis("商家A", Faker.values())

  46. .add_yaxis("商家B", Faker.values())

  47. .set_global_opts(title_opts=opts.TitleOpts(title="Line-基本示例"))

  48. )

  49. return c



  50. # 仪表盘图

  51. def gauge_base() -> Gauge:

  52. c = (

  53. Gauge()

  54. .add("", [("完成率", 66.6)])

  55. .set_global_opts(title_opts=opts.TitleOpts(title="Gauge-基本示例"))

  56. )

  57. return c



  58. # 地图map

  59. def map_base() -> Map:

  60. c = (

  61. Map()

  62. .add("商家A", [list(z) for z in zip(Faker.provinces, Faker.values())], "china")

  63. .set_global_opts(title_opts=opts.TitleOpts(title="Map-基本示例"))

  64. )

  65. return c



  66. #散点图

  67. def scatter_base() -> Scatter:

  68. c = (

  69. Scatter()

  70. .add_xaxis(Faker.choose())

  71. .add_yaxis("商家A", Faker.values())

  72. .set_global_opts(title_opts=opts.TitleOpts(title="Scatter-基本示例"))

  73. )

  74. return c



  75. # 新增全量更新数据

  76. def full_bar_base() -> Bar:

  77. c = (

  78. Bar()

  79. .add_xaxis(["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"])

  80. .add_yaxis("商家A", [randrange(, 100) for _ in range(6)])

  81. .add_yaxis("商家B", [randrange(, 100) for _ in range(6)])

  82. .set_global_opts(title_opts=opts.TitleOpts(title="Bar-基本示例", subtitle="我是副标题"))

  83. )

  84. return c



  85. # 新增动态更新数据

  86. def incre_line_base() -> Line:

  87. line = (

  88. Line()

  89. .add_xaxis(["{}".format(i) for i in range(10)]) #初始化10个值

  90. .add_yaxis(

  91. series_name="",

  92. y_axis=[randrange(50, 80) for _ in range(10)], #在50-80循环出10个值

  93. is_smooth=True,

  94. label_opts=opts.LabelOpts(is_show=False),

  95. )

  96. .set_global_opts(

  97. title_opts=opts.TitleOpts(title="动态数据"),

  98. xaxis_opts=opts.AxisOpts(type_="value"),

  99. yaxis_opts=opts.AxisOpts(type_="value"),

  100. )

  101. )

  102. return line



  103. # 生成bart图,带参数

  104. def bar_base_withpara(title,ydata) -> Bar:

  105. c = (

  106. Bar()

  107. .add_xaxis(["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"])

  108. .add_yaxis("商家A", ydata)

  109. .set_global_opts(title_opts=opts.TitleOpts(title=title, subtitle="动态标题"))

  110. )

  111. return c



  112. # render_embed方法

  113. @app.route("/render_embed")

  114. def render_embed():

  115. c = bar_base()

  116. returnMarkup(c.render_embed())



  117. # render到文件方法,注意render的缺省工作目录和flask的template工作目录不同

  118. @app.route("/render")

  119. def renderhtml():

  120. filename='terenderfile.html'

  121. currentpath=os.path.abspath('.')

  122. pathfilename=os.path.join(currentpath, 'templates',filename)

  123. c = line_base()

  124. c.render(pathfilename)

  125. return render_template(filename)



  126. # 外部访问链路

  127. @app.route('/')

  128. def hello_world():

  129. return'Hello World'


  130. # ----------------------------------------------------------

  131. # 经测试bar,line,scatter和gauge可通过ajax + url: "http://127.0.0.1:8080/XXXChart"方式

  132. # 但Map不行,估计是map访问的百度地图资源受限,待核查

  133. # ----------------------------------------------------------


  134. # 外部访问链路,bar图

  135. @app.route("/dumpbar")

  136. def dump_bar():

  137. return render_template("barbaseindex.html")



  138. # barbaseindex.html指向的链路

  139. @app.route("/barChart")

  140. def get_bar_chart():

  141. c = bar_base()

  142. return c.dump_options_with_quotes()



  143. # 外部访问链路,line图

  144. @app.route("/dumpline")

  145. def dump_line():

  146. return render_template("linebaseindex.html")



  147. # linebaseindex.html指向的链路

  148. @app.route("/lineChart")

  149. def get_line_chart():

  150. c = line_base()

  151. return c.dump_options_with_quotes()



  152. # 外部访问链路,scatter图

  153. @app.route("/dumpscatter")

  154. def dump_scatter():

  155. return render_template("scatterbaseindex.html")



  156. # scatterbaseindex.html指向的链路

  157. @app.route("/scatterChart")

  158. def get_scatter_chart():

  159. c = scatter_base()

  160. return c.dump_options_with_quotes()



  161. # 外部访问链路,gauge图

  162. @app.route("/dumpgauge")

  163. def dump_gauge():

  164. return render_template("gaugebaseindex.html")



  165. # gaugebaseindex.html指向的链路

  166. @app.route("/gaugeChart")

  167. def get_gauge_chart():

  168. c = gauge_base()

  169. return c.dump_options()

  170. #return c.dump_options_with_quotes()



  171. # 通过mapbaseindex.html对http://127.0.0.1:8080/mapChart"调用,只能看到标题,无法打开地图

  172. # # 外部访问链路,map图

  173. # @app.route("/dumpmap")

  174. # def dump_map():

  175. # return render_template("mapbaseindex.html")

  176. #

  177. # # mapbaseindex.html指向的链路

  178. # @app.route("/mapChart")

  179. # def get_map_chart():

  180. # c = map_base()

  181. # return c.dump_options_with_quotes()



  182. # 外部访问链路,bar图

  183. @app.route("/dumpfullbar")

  184. def dump_full_bar():

  185. return render_template("fullupdatebarbase.html")



  186. # barbaseindex.html指向的链路

  187. @app.route("/fullbarChart")

  188. def get_full_bar_chart():

  189. c = full_bar_base()

  190. return c.dump_options_with_quotes()



  191. # 外部访问链路,动态新增值的line图

  192. @app.route("/dumpincreline")

  193. def index():

  194. return render_template("increupdatelinebase.html")



  195. # increupdatelinebase.html指向的链路

  196. @app.route("/increlineChart")

  197. def get_incre_line_chart():

  198. c = incre_line_base()

  199. return c.dump_options_with_quotes()



  200. idx = 9

  201. # 生成动态新增数据值,在increupdatelinebase.html的http://127.0.0.1:8080/lineDynamicData被引用

  202. @app.route("/lineDynamicData")

  203. def update_line_data():

  204. global idx

  205. idx = idx + 1

  206. return jsonify({"name": idx, "value": randrange(50, 80)})




  207. @app.route("/dumpbarwithpara")

  208. def dump_bar_withpara():

  209. return render_template("barbasewithparaindex.html")



  210. @app.route("/barChartwithpara", methods=['post','get'])

  211. def get_bar_chart_withpara():

  212. title = request.args.get('title')

  213. type = request.args.get('type')

  214. ydata=list(randrange(1,100) for i in range(6))

  215. c = bar_base_withpara(title, ydata)

  216. returnMarkup(c.render_embed())




  217. if __name__ == "__main__":

  218. app.run('127.0.0.1', '8080', debug=True)



关于barbaseindex.html文件

代码示例

  1. <!DOCTYPE html>

  2. <html>

  3. <head>

  4. <meta charset="UTF-8">

  5. <title>Awesome-pyecharts</title>

  6. <script src="templates/jquery-3.4.1.min.js"></script>

  7. <script type="text/javascript" src="templates/echarts.min.js"></script>

  8. </head>

  9. <body>

  10. <div id="bar" style="width:1000px; height:600px;"></div>

  11. <script>

  12. var chart = echarts.init(document.getElementById('bar'), 'white', {renderer: 'canvas'});

  13. $(

  14. function () {

  15. fetchData(chart);

  16. setInterval(fetchData, 2000);

  17. }

  18. );

  19. function fetchData() {

  20. $.ajax({

  21. type: "GET",

  22. url: "http://127.0.0.1:8080/fullbarChart",

  23. dataType: 'json',

  24. success: function (result) {

  25. chart.setOption(result);

  26. }

  27. });

  28. }

  29. </script>

  30. </body>

  31. </html>



3
0