看到小芮介绍了pygal文章后, http://rfyiamcool.blog.51cto.com/1030776/1378400, 我一直搞数据工作, 所以对于这种数据的展现很有兴趣. 做了点研究, 记录如下: ================= pygal的特点 ================= Web中显示chart, 现在的主流做法是使用javascript chart包, 比如highcharts和ECharts, 交互性很好. 另外还有server端生成图片, 然后在浏览器端加载图片, 显然交互性差了很多. 这里介绍第3种做法, 使用 pygal(一个 python 包)在web server 端生成svg矢量图(其实是xml格式), 然后在flask中, 我们可将svg图和普通jinja2参数一样传到html模板中, 最后 web 浏览器就得到图文并茂的结果了.
还有一个方法其实更好, 将svg以文件形式保存下来, 然后在以iframe方式将svg文件引入. 好处是, iframe有滚动条, 浏览大的svg就不吃力了. 代码如下,
<div class="center"><iframe scrolling="no" frameborder="0" src="../../da/dcb/classmodule__inherit__graph.svg" width="3944" height="832"><p><b>This browser is not able to show SVG: try Firefox, Chrome, Safari, or Opera instead.</b></p></iframe></div>
和 highcharts 等js chart包相比, pygal优势有:
1. 使用highcharts, server端除了一般的endpoint接口外, 还需要写一套api接口, 来返回json格式数据. 使用pygal, 只需要实现普通的endpoint接口. 2. 使用pygal, 使用的数据格式是python原生的tuple和list. 3. 避免了写javascript 代码.当然, pygal的缺点也很明显:1. 浏览器兼容性还不是很好, 尤其IE, IE 9以下干脆不支持svg2. 交互性还不够丰富=================经验总结:=================1. svg对象应该直接输出到html的figure标签之下, 而不是在figure/embed属性中2. svg对象在输出时, 应该加上jinja2的 safe 这个filter3. view 函数返回的response, 不能设置Content-Type='image/svg+xml'4. chart.disable_xml_declaration应该设置为True5. 在server端创建chart对象后, 可以紧接着为chart指定js属性, 来加载本地的svg.jquery.js和pygal-tooltips.js 文件. 如果不指定这个属性, 默认将从 http://kozea.github.com/pygal.js 下载js, 速度较慢. 当然在html端, 仍须再次声明加载这两个js文件. 6. 如果对每个serial的颜色有特别的要求, 可以为chart定制一个style,在sytle中指定serial 的颜色序列. pygal的style.py其实已经预定义了14种style, 也叫theme, 可以作为参考.from pygal.style import Stylecustom_style=Style(colors=( '#b6e354' #green ,'#fd971f' #yellow ,'#DC3912' #red ))chart = pygal.Line(legend_at_bottom=True,style=custom_style) ; 7. 可以为chart指定height和width, 但是值不能太小(比如50), 太小了将导致pygal进入死循环, 最终报MemoryError内存不足错误. 8. 一般不需要为chart 指定width, 它自动会撑满上层html容器. 如果需要显示很宽幅度的图, 可以将 chart 的width/height设置的大一些, 比如2000, 这时相当于将图像做了zoom out, 图像上的字体会变小的. chart.width=2000 chart.height=4009. 接前一点, 如果你还不想让字体变小, 应该将chart放在一个带滚动条的div中, div上可以加上滚动条(比如width为940). 同时, 为svg xml节点增加 width 属性(这个width相当于canvas的width), 设置它稍微大于chart的width(chart为2000, svg width为2100), 这样的效果即是1:1输出, 没有缩放. 演示: http://www.w3schools.com/svg/tryit.asp?filename=trysvg_rect 当前的pygal没有办法做到这个, 等我有空的时候, fork一下pygal项目, 增加这个feature, 即为pygal的chart对象增加一个svg_width和svg_height属性, 在做render时, 直接为svg xml的svg node加上 <svg style="width:2100px" > 这样的属性. 在pygal支持这个功能之前, 我们还是有2个办法为svg增加width属性: 第1个方法是, 增加一个css文件, 在里面为svg 设置style, 然后在html中引用这个css文件, 这个方法仅仅适合于html有一个svg对象, 或者有多个svg, 但size都一样的情形. =======large_svg.css file svg{ width:2100px; } =======html 文件部分内容 <link rel="stylesheet" href="/static/css/large_svg.css" > <div id="svg_scrollable_div" style="width:940px;overflow-x:auto;overflow-y:hidden;"> <figure> <!-- the following safe filter is must --> { {svg_xml|safe}} </figure> </div> 第2个方法是, 在chart做完render之后, 直接修改svg的xml字符串,为 svg xml节点增加 style="width:2100px" 属性. 这个方法的适应性很好. svg_xml = chart.render() svg_xml='<svg style="width:2100px" '+svg_xml[4:] =================示例=================本示例演示了, 如何将svg以jinja2参数的形式传递到html中.#--------------------------------------# flask view 函数#--------------------------------------@mod.route("/svg")def raw_svgs(): chart = pygal.Line(legend_at_bottom=True,legend_box_size=18) ##-------------------------------------- ##Declare the location of svg.jquery.js and pygal-tooltips.js in server side. ##-------------------------------------- #It must be declare in server side, not html file #if not declare in server, by default it will load the two js files located in http://kozea.github.com/pygal.js. And it will slow down the page loading #1, It works, load local js files SITE_ROOT = os.path.realpath(os.path.dirname(__file__)) chart.js = [os.path.join(SITE_ROOT, "static/js/", "svg.jquery.js"), os.path.join(SITE_ROOT, "static/js/", "pygal-tooltips.js")] #2.a, It Works, but it is ugly because it use local absolute http url #chart.js =['http://127.0.0.1:5000/static/js/svg.jquery.js', # 'http://127.0.0.1:5000/static/js/pygal-tooltips.js'] #2.b, works, use local CDN absolute http url #chart.js =['http://another_server/pygal-tooltips.js', # 'http://another_server/svg.jquery.js'] #3, Does not work, error raised at visiting, IOError: [Errno 2] No such file #chart.js = [url_for('static', filename='js/svg.jquery.js'), # url_for('static', filename='js/pygal-tooltips.js')] #disable xml root node chart.disable_xml_declaration = True chart.title = 'Browser usage evolution (in %)' chart.x_labels = map(str, range(2002, 2013)) chart.add('Firefox', [None, None, 0, 16.6, 25, 31, 36.4, 45.5, 46.3, 42.8, 37.1]) chart.add('Chrome', [None, None, None, None, None, None, 0, 3.9, 10.8, 23.8, 35.3]) chart.add('IE', [85.8, 84.6, 84.7, 74.5,66, 58.6, 54.7, 44.8, 36.2, 26.6, 20.1]) chart.add('Others', [14.2, 15.4, 15.3, 8.9, 9, 10.4, 8.9, 5.8, 6.7, 6.8, 7.5]) svg_xml = chart.render() response=make_response(render_template('test_svg.html', title="Hello pygal" , svg_xml=svg_xml)) #response.headers['Content-Type']='image/svg+xml' 不能设置Content-Type为svg模式 return response #--------------------------------------# test_svg.html 文件 #--------------------------------------<!DOCTYPE html><html> <head> <title>{ {title}}</title> <!-- 这里也要说明要加载svg.jquery.js和pygal-tooltips.js, 否则不显示tooltips --> <script type="text/javascript" src="/static/js/svg.jquery.js"></script> <script type="text/javascript" src="/static/js/pygal-tooltips.js"></script> </head> <body> <figure> <!-- the following safe filter is must --> { {svg_xml|safe}} </figure> </body></html>