自制Openerp图表

自制Openerp图表

分类: openerp Python Web Javascript   2882人阅读  评论(1)  收藏  举报
注意:
1. 本文介绍一种简单的,非通用的改进openerp的思路。并非一定要取代原有方式。
2. 本文会修改web_graph模块,如果在你的项目里使用了这个模块,请避免修改,以防止异常。
3. 本文基于openerp 6.1
通过本文,你可以知道:
1. web_graph的运行机制。
2. 如何动手修改这个模块。

看看这个模块的结构:


客户端采用的是highchart(http://www.highcharts.com/),当然,如果你喜欢其他的lib,都是没问题的。

第一步,把highcharts给包含到模块来,这样openerp才能把这个库合并输出。
把highcharts放置在适合的位置。

修改__openerp__.py


[python]  view plain copy
  1. {  
  2.     "name""Graph Views",  
  3.     "category" : "Hidden",  
  4.     "description":"""Graph Views for Web Client 
  5.  
  6. * Parse a <graph> view but allows changing dynamically the presentation 
  7. * Graph Types: pie, lines, areas, bars, radar 
  8. * Stacked / Not Stacked for areas and bars 
  9. * Legends: top, inside (top/left), hidden 
  10. * Features: download as PNG or CSV, browse data grid, switch orientation 
  11. * Unlimited "Group By" levels (not stacked), two cross level analysis (stacked) 
  12. """,  
  13.     "version""3.0",  
  14.     "depends": ['web'],  
  15.     "js": [  
  16.         "static/lib/highchart/js/highcharts.js",  
  17.         "static/src/js/graph.js"  
  18.     ],  
  19.     "css": [  
  20.         "static/src/css/*.css",  
  21.     ],  
  22.     'qweb' : [  
  23.         "static/src/xml/*.xml",  
  24.     ],  
  25.     "auto_install"True  
  26. }  

下面研究highcharts.
观察highcharts的示例(http://www.highcharts.com/demo/),折线图是这样运行的:

[javascript]  view plain copy
  1. $(function () {  
  2.     var chart;  
  3.     $(document).ready(function() {  
  4.         chart = new Highcharts.Chart({  
  5.             chart: {  
  6.                 renderTo: 'container',  
  7.                 type: 'line',  
  8.                 marginRight: 130,  
  9.                 marginBottom: 25  
  10.             },  
  11.             title: {  
  12.                 text: 'Monthly Average Temperature',  
  13.                 x: -20 //center  
  14.             },  
  15.             subtitle: {  
  16.                 text: 'Source: WorldClimate.com',  
  17.                 x: -20  
  18.             },  
  19.             xAxis: {  
  20.                 categories: ['Jan''Feb''Mar''Apr''May''Jun',  
  21.                     'Jul''Aug''Sep''Oct''Nov''Dec']  
  22.             },  
  23.             yAxis: {  
  24.                 title: {  
  25.                     text: 'Temperature (°C)'  
  26.                 },  
  27.                 plotLines: [{  
  28.                     value: 0,  
  29.                     width: 1,  
  30.                     color: '#808080'  
  31.                 }]  
  32.             },  
  33.             tooltip: {  
  34.                 formatter: function() {  
  35.                         return '<b>'this.series.name +'</b><br/>'+  
  36.                         this.x +': 'this.y +'°C';  
  37.                 }  
  38.             },  
  39.             legend: {  
  40.                 layout: 'vertical',  
  41.                 align: 'right',  
  42.                 verticalAlign: 'top',  
  43.                 x: -10,  
  44.                 y: 100,  
  45.                 borderWidth: 0  
  46.             },  
  47.             series: [{  
  48.                 name: 'Tokyo',  
  49.                 data: [7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6]  
  50.             }, {  
  51.                 name: 'New York',  
  52.                 data: [-0.2, 0.8, 5.7, 11.3, 17.0, 22.0, 24.8, 24.1, 20.1, 14.1, 8.6, 2.5]  
  53.             }, {  
  54.                 name: 'Berlin',  
  55.                 data: [-0.9, 0.6, 3.5, 8.4, 13.5, 17.0, 18.6, 17.9, 14.3, 9.0, 3.9, 1.0]  
  56.             }, {  
  57.                 name: 'London',  
  58.                 data: [3.9, 4.2, 5.7, 8.5, 11.9, 15.2, 17.0, 16.6, 14.2, 10.3, 6.6, 4.8]  
  59.             }]  
  60.         });  
  61.     });  
  62.       
  63. });  

第二步,研究一下服务端。
服务端代码集中在graph.py里。
GraphView类是一个标准的View, 是客户端图表数据的来源,也定义了图表显示方式。如果要修改图表,几乎是要动这个类。
我们来简单的看。首先修改客户端。
只要把服务端返回的数据变成这种格式就可以了。动手:


[python]  view plain copy
  1. # -*- coding: utf-8 -*-  
  2.   
  3. import tools  
  4. from tools import safe_eval  
  5.   
  6. try:  
  7.     # embedded  
  8.     import openerp.addons.web.common.http as openerpweb  
  9.     from openerp.addons.web.controllers.main import View  
  10. except ImportError:  
  11.     # standalone  
  12.     import web.common.http as openerpweb  
  13.     from web.controllers.main import View  
  14.   
  15. from lxml import etree  
  16.   
  17. class GraphView(View):  
  18.     _cp_path = '/web_graph/graph'  
  19.      
  20.     @tools.cache(timeout=3600)  
  21.     def from_db(self, obj, chart_type, title, fields, domain, group_by, context):  
  22.         result = {}  
  23.         if len(fields)<2:  
  24.             return result  
  25.           
  26.         field_x = fields[1]  
  27.         field_y = fields[2]  
  28.         field_z = (len(fields)==4and fields[3or ''  
  29.   
  30.         ids = obj.search(domain)  
  31.               
  32.         if ids:  
  33.             records = obj.read(ids)  
  34.               
  35.             #field_x  
  36.             categories = []  
  37.             #field_z  
  38.             groups = []  
  39.             series = []  
  40.               
  41.             if field_z:  
  42.                 data_set = {}  
  43.                 for r in records:  
  44.                     #get categories.  
  45.                     if r[field_x] not in categories:  
  46.                         categories.append(r[field_x])  
  47.                           
  48.                     if r[field_z] not in groups:  
  49.                         groups.append(r[field_z])  
  50.                       
  51.                     data_set[r[field_x]+r[field_z]] = r[field_y]  
  52.                   
  53.                 #transform data  
  54.                 # series  
  55.   
  56.                 for g in groups:  
  57.                     s = {'name':g, 'data':[]}  
  58.                     for cate in categories:  
  59.                         s['data'].append(data_set.get(cate+g, 0))  
  60.                     series.append(s)  
  61.   
  62.             else:  
  63.                 data = []  
  64.                 for r in records:  
  65.                     if r[field_x] not in categories:  
  66.                         categories.append(r[field_x])  
  67.                     data.append(r[field_y])  
  68.                   
  69.                 series.append({'data':data})  
  70.   
  71.         return categories, series  
  72.      
  73.     @openerpweb.jsonrequest  
  74.     def data_get(self, req, model=None, domain=[], group_by=[], view_id=False, context={}, **kwargs):  
  75.   
  76.         obj = req.session.model(model)  
  77.         xml = obj.fields_view_get(view_id, 'graph')  
  78.         graph_xml = etree.fromstring(xml['arch'])  
  79.           
  80.         chart_type = graph_xml.attrib.get('type'or 'line'  
  81.         chart_title = graph_xml.attrib.get('string'or '图表'  
  82.         fields = [ element.attrib.get('name'for element in graph_xml.iter() ]  
  83.           
  84.         data = self.from_db(obj, chart_type, chart_title, fields, domain, group_by, context)  
  85.   
  86.         result = {  
  87.             'title':chart_title,  
  88.             'categories':data[0],  
  89.             'series':data[1],  
  90.             'chart_type':chart_type,  
  91.         }  
  92.           
  93.         return result  

很简单, 我只处理这样的Graph定义:

[html]  view plain copy
  1. <record id="view_sale_order_report_monthly_tree" model="ir.ui.view">  
  2.         <field eval="1" name="priority"/>  
  3.         <field name="name">sale.order.report.monthly.tree</field>  
  4.         <field name="model">sale.order.report.monthly</field>  
  5.         <field name="type">tree</field>  
  6.         <field name="arch" type="xml">  
  7.             <tree string="每月销售统计">  
  8.                 <field name="date" />  
  9.                 <field name="amount" />  
  10.                 <field name="source" />  
  11.             </tree>  
  12.         </field>  
  13.     </record>  

第一个field,作为x轴,第二个,作为y轴。第三个,group成多个系列。 这样的处理就是简单化,并不会考虑openerp原来考虑的事情,所以不是一个通用方法。
(另,如果要是想进一步扩展graph,则需要修改addons/base/rng/view.rng的规则。)

下面就是让highcharts显示出来了。
观察web_graph的xml模板和graph.js
我记得原来的xml模板element_id上有一些bug(6.1),我修改了一下:


[html]  view plain copy
  1. <template>  
  2.     <div t-name="GraphView" t-att-id="element_id+'-chart-'+chart_id"  
  3.          style="height:300px;position:relative;"/>  
  4. </template>  

这是highcharts显示的容器。
重头戏在graph.js里,这里需要处理很多东西,但是也简单。按照原本的客户端views写法:


[javascript]  view plain copy
  1. /*--------------------------------------------------------- 
  2.  * OpenERP web_graph 
  3.  *---------------------------------------------------------*/  
  4.   
  5. openerp.web_graph = function (openerp) {  
  6.   
  7. var QWeb = openerp.web.qweb,  
  8.      _lt = openerp.web._lt;  
  9. openerp.web.views.add('graph''openerp.web_graph.GraphView');  
  10. openerp.web_graph.GraphView = openerp.web.View.extend({  
  11.     display_name: _lt('Graph'),  
  12.       
  13.     init: function(parent, dataset, view_id, options) {  
  14.         this._super(parent);  
  15.         this.dataset = dataset;  
  16.         this.view_id = view_id;  
  17.         this.set_default_options(options);  
  18.         this.fields_view = {};  
  19.           
  20.         this.model = dataset.model;  
  21.         this.chart_id = Math.floor((Math.random()*100)+1);  
  22.     },  
  23.       
  24.     start: function() {  
  25.         this._super();  
  26.           
  27.         this.$element.html(QWeb.render("GraphView", {  
  28.             "chart_id"this.chart_id,  
  29.             'element_id'this.widget_parent.element_id  
  30.         }));  
  31.     },  
  32.     stop: function() {  
  33.         this._super();  
  34.     },  
  35.   
  36.     /* 
  37.      * get data here. 
  38.     */  
  39.     do_search: function(domain, context, group_by) {  
  40.           
  41.         this.rpc(  
  42.                    '/web_graph/graph/data_get',  
  43.                    {  
  44.                        'model'this.model,  
  45.                        'domain': domain,  
  46.                        'group_by': group_by,  
  47.                        'view_id'this.view_id,  
  48.                        'context': context  
  49.                    }, this.on_search  
  50.                 );  
  51.   
  52.     },  
  53.       
  54.     on_search: function(result){  
  55.         container = this.widget_parent.element_id+"-chart-"+this.chart_id;  
  56.           
  57.         var chart = new Highcharts.Chart({  
  58.             chart: {  
  59.                 renderTo: container,  
  60.                 height: 300  
  61.             },  
  62.             title: {  
  63.                 text: result.title  
  64.             },  
  65.             xAxis: {  
  66.                 categories: result.categories  
  67.             },  
  68.             series: result.series  
  69.         });  
  70.     },  
  71.       
  72.     do_show: function() {  
  73.         this.do_push_state({});  
  74.         return this._super();  
  75.     }  
  76. });  
  77. };  
  78. // vim:et fdc=0 fdl=0:  

能看出,主要是三个方法(标准的api,参考openerp客户端的文档):
start, do_search, on_search

start是指本widget启动的时候要做的事情。
do_search, 是启动以后,获取数据。
on_search, 服务端返回数据,根据数据来设置highchart, 显示数据。

需要注意的是,模板里的element_id( t-att-id="element_id+'-chart-'+chart_id" ) 要和 graph.js里的
"container = this.widget_parent.element_id+"-chart-"+this.chart_id; " 一致。


最终,我们得到一个更好看的chart.




继续:
1. 可以写一个更通用的服务端。
2. 可以扩展这个chart,主要对view.rng里规则的修改。


心情不好,写的很笼统,如果有疑问,可以回帖,或者新浪微博 @杨振宇_  
谨以此文,纪念我远在天堂的儿子。 愿天父的慈爱永远呵护你。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值