在前面测试通过odoo登录的功能,这次的问题重点是如何访问后台具体的业务类的接口呢?这次就以我们在odoo中安装的lunch模块为例,目标是获取lunch.alert的数据,如下图

具体过程接上次文章,继续完善OdooJsonRpc类的代码,首先是基础代码,这个是需要提供具体的model名称和具体方法,也是是一个很基础的方法,后台的odoo网站会利用类似C#反射的机制调用目标类的方法。

    /**
* Calls the method of that particular model
* @param model Model name
* @param method Method name of particular model
* @param args Array of fields
* @param kwargs Object
*/
public call(model: string, method: string, args: any, kwargs?: any)
{
kwargs = kwargs || {};
let params =
{
model: model,
method: method,
args: args,
kwargs: kwargs == false ? {} : kwargs,
context: this.getContext()
};
return this.sendRequest("/web/dataset/call_kw", params);
}

调用以上基础call方法的几个基本封装函数有如下几个,基本实现了对数据的CRUD功能,当然基本上是针对一条数据的操作

    /**
* Reads that perticular fields of that particular ID
* @param model Model Name
* @param id Id of that record which you want to read
* @param mArgs Array of fields which you want to read of the particular id
*/ public read(model: string, id: number, mArgs: any): Promise<any>
{
let args =
[
id, [mArgs]
]
return this.call(model, 'read', args)
} /**
* Provide the name that you want to search
* @param model Model name
* @param name Name that you want to search
*/
public nameSearch(model: string, name: string): Promise<any>
{
let kwargs =
{
name: name,
args: [],
operator: "ilike",
limit:
}
return this.call(model, 'name_search', [], kwargs)
} /**
* Provide the IDs and you will get the names of that paticular IDs
* @param model Model name
* @param mArgs Array of IDs that you want to pass
*/
public nameGet(model: string, mArgs: any): Promise<any>
{
let args = [mArgs]
return this.call(model, 'name_get', args)
} /**
* Create a new record
* @param model Model name
* @param mArgs Object of fields and value
*/
public createRecord(model: string, mArgs: any)
{
let args = [mArgs];
return this.call(model, "create", args, null)
} /**
* Delete the record of particular ID
* @param model Model Name
* @param id Id of record that you want to delete
*/
public deleteRecord(model: string, id: number)
{
let mArgs = [id]
return this.call(model, "unlink", mArgs, null)
} /**
* Updates the record of particular ID
* @param model Model Name
* @param id Id of record that you want to update the.
* @param mArgs The Object of fields and value that you want to update
* (e.g)
* let args = {
* "name": "Mtfa"
* }
*/
public updateRecord(model: string, id: number, mArgs: any)
{
let args =
[
[id], mArgs
]
return this.call(model, "write", args, null)
}

针对单条数据的读取,这里还有另外一种方法,类似上面的read函数

    /**
* Loads all data of the paricular ID
* @param model Model name
* @param id Id of that particular data which you want to load
*/
public load(model: string, id: number): Promise<any>
{
let params =
{
model: model,
id: id,
fields: [],
context: this.getContext()
}
return this.sendRequest("/web/dataset/load", params)
}

还有对odoo多条件查询的情况,尤其还有分页的问题,这里也有这样一个很实用的分页查询的方法,这个在日后app的开发中会经常用到

    /**
* Fires query in particular model with fields and conditions
* @param model Model name
* @param domain Conditions that you want to fire on your query
* (e.g) let domain = [
* ["id","=",11]
* ]
* @param fields Fields names which you want to bring from server
* (e.g) let fields = [
* ["id","name","email"]
* ]
* @param limit limit of the record
* @param offset
* @param sort sorting order of data (e.g) let sort = "ascending"
*/
public searchRead(model: string, domain: any, fields: any, limit: number, offset: any, sort: string)
{
let params =
{
model: model,
fields: fields,
domain: domain,
offset: offset,
limit: limit,
sort: sort,
context: this.getContext()
};
return this.sendRequest("/web/dataset/search_read", params);
}

到此,访问odoo具体业务类的OdooJsonRpc类封装完毕,完整的代码在这里。接下来怎么获取我们的lunch.alert数据呢??

首先定义我们访问odoo业务类的成员变量,这里我们的model类是lunch.alert,需要获取的字段是message和id,其他参数我们暂使用默认的值

    private lunchAlert = "lunch.alert";
private fields = ["message", "id"];
private domain = []
private sort = ""
private limit =
private offset =
private items: Array<{ id: number, message: string }> = []

第二步是,与odoo后台网站交互获取数据的过程

    private TestMyOdooModel()
{
this.odooRpc.searchRead(this.lunchAlert,this.domain, this.fields, this.limit, this.offset,this.sort)
.then((lunchAlerts: any) =>
{
let json = JSON.parse(lunchAlerts._body);
if (!json.error)
{
let query = json["result"].records
for (let i in query)
{
this.items.push
({
id: query[i].id,
message: query[i].message
})
}
this.utils.presentAlert("Lunch Message", this.items[].message,[{text: "Ok"}])
}
else
{
this.utils.presentAlert("LunchAlert", "Parse lunch alert error",[{text: "Ok"}])
}
})
}

如果正确获取数据,我们会弹出第一条数据对应的message.

编译打包成*.apk,在手机上测试,确实成功了,如下图所示。

由于这些都是测试阶段的代码,将来可能会改变,现阶段把这些代码全部放在这里。

测试部分的Page页面odooLogin.ts

import { Component } from '@angular/core';
import {
AlertController,
IonicPage,
Loading,
LoadingController,
NavController,
NavParams
} from 'ionic-angular';
import { OdooJsonRpc } from '../../../../providers/baseService/Odoojsonrpc';
import { Utils } from "../../../../providers/baseService/Utils"; @IonicPage()
@Component
({
selector: 'page-odooLogin',
templateUrl: 'odooLogin.html',
}) export class OdooLoginPage
{
private listForProtocol: Array<{ protocol: string}> = []
public perfectUrl: boolean = false
public odooUrl
public selectedProtocol
private dbList: Array<{ dbName: string}> = []
private selectedDatabase
private email
private password constructor(public navCtrl: NavController,
private alert: AlertController, public navParams: NavParams,
private odooRpc: OdooJsonRpc, private loadingCtrl: LoadingController,
private utils: Utils)
{
this.listForProtocol.push({ protocol: "http" })
this.listForProtocol.push({protocol: "https"})
} public checkUrl()
{
this.utils.presentLoading("Please Wait")
this.odooRpc.init
({
odoo_server: this.selectedProtocol + "://" + this.odooUrl
//http_auth: 'username:password' // optional
}) this.odooRpc.getDbList().then((dbList: any) =>
{
console.log(dbList)
this.perfectUrl = true
this.utils.dismissLoading()
this.fillData(dbList)
}).
catch((err: any) =>
{
console.log(err)
this.utils.presentAlert("Error", "You Entered a wrong Odoo URL",
[{
text: "Ok"
}])
this.utils.dismissLoading()
});
} public fillData(res: any)
{
let body = JSON.parse(res._body)
let json = body['result'];
this.dbList.length = ;
for (var key in json)
{
this.dbList.push({ dbName: json[key] });
}
} private login()
{
this.utils.presentLoading("Please wait", , true)
this.odooRpc.login(this.selectedDatabase, this.email, this.password)
.then((res: any) =>
{
let logiData: any = JSON.parse(res._body)["result"];
logiData.password = this.password
localStorage.setItem("token", JSON.stringify(logiData));
//this.utils.dismissLoading()
this.utils.presentAlert("Congratulation", "You login success",[{text: "Ok"}])
this.TestMyOdooModel()
}).
catch((err) =>
{
this.utils.presentAlert("Error", "Username or password must be incorrect",
[{
text: "Ok"
}])
});
} private lunchAlert = "lunch.alert";
private fields = ["message", "id"];
private domain = []
private sort = ""
private limit =
private offset =
private items: Array<{ id: number, message: string }> = [] private TestMyOdooModel()
{
this.odooRpc.searchRead(this.lunchAlert,this.domain, this.fields, this.limit, this.offset,this.sort)
.then((lunchAlerts: any) =>
{
let json = JSON.parse(lunchAlerts._body);
if (!json.error)
{
let query = json["result"].records
for (let i in query)
{
this.items.push
({
id: query[i].id,
message: query[i].message
})
}
this.utils.presentAlert("Lunch Message", this.items[].message,[{text: "Ok"}])
}
else
{
this.utils.presentAlert("LunchAlert", "Parse lunch alert error",[{text: "Ok"}])
}
})
}
}

页面布局部分odooLogin.html

<ion-content class="background">
<ion-card>
<ion-card-content> <div class="spacer" style="height: 10px;"></div>
<ion-item>
<ion-label style="color: #fff">Select Protocol</ion-label>
<ion-select [(ngModel)]="selectedProtocol" style="color: #fff" name="dbNames">
<ion-option *ngFor="let item of listForProtocol" value="{{item.protocol}}">{{item.protocol}}</ion-option>
</ion-select>
</ion-item>
<div class="spacer" style="height: 10px;"></div>
<ion-item>
<ion-input [(ngModel)]="odooUrl" type="url" name="odooUrl" placeholder="Odoo Url"></ion-input>
</ion-item>
<div class="spacer" style="height: 10px;"></div>
<button ion-button block round outline color="light" (click)="checkUrl()" text-center>
Check Url
<ion-icon name="md-arrow-round-forward"></ion-icon>
</button>
<div [hidden]="!perfectUrl">
<form (ngSubmit)="login()" #registerForm="ngForm">
<div class="spacer" style="height: 10px;"></div>
<ion-item>
<ion-input type="email" [(ngModel)]="email" name="email" placeholder="Email" required></ion-input>
</ion-item>
<div class="spacer" style="height: 5px;"></div>
<ion-item>
<ion-input type="password" [(ngModel)]="password" name="pass" placeholder="Password" required></ion-input>
</ion-item>
<div class="spacer" style="height: 10px;"></div>
<div class="spacer" style="height: 10px;"></div>
<ion-item>
<ion-label style="color: #fff">Select Database</ion-label>
<ion-select [(ngModel)]="selectedDatabase" name="selectDatabase" style="color: #fff" required>
<ion-option *ngFor="let item of dbList" value="{{item.dbName}}">{{item.dbName}}</ion-option>
</ion-select>
</ion-item>
<button ion-button block round outline color="light" [disabled]="!registerForm.form.valid" (click)="signin()">Login</button>
</form>
</div>
</ion-card-content>
</ion-card>
</ion-content

CSS效果部分odooLogin.scss

page-odooLogin {
.background {
height: %;
width: %;
background-size: cover !important;
background-position: center center !important;
background-image: url('../assets/imgs/mountain.jpg')
} ion-card.card {
margin-top: %;
box-shadow: none;
background: rgba(, , , 0.5);
border-radius: 5px;
}
a, p,
ion-card-header.card-header {
color: #fff!important;
} .list > .item-block:first-child {
border: medium none;
} .item {
margin-bottom: 10px;
background: rgba(, , , 0.5);
border: medium none; .text-input, {
color: #fff;
} input::-moz-placeholder{
color: #fff!important;
}
input:-moz-placeholder {
color: #fff!important;
}
*:-moz-placeholder{
color: #fff!important;
}
*:-ms-input-placeholder{
color: #fff!important;
}
*::-webkit-input-placeholder{
color: #fff!important;
}
}
}

OdooJsonRpc部分Odoojsonrpc.ts

import { Injectable } from '@angular/core';
import 'rxjs/add/operator/toPromise';
import 'rxjs/Rx'; import { Headers, Http } from '@angular/http';
import { Utils } from './Utils'; @Injectable()
export class OdooJsonRpc
{
private jsonRpcID: number = ;
private headers: Headers;
private odoo_server: string;
private http_auth: string;
private list = "/web/database/list";
private get_list = "/web/database/get_list";
private jsonrpc = "/jsonrpc"; constructor(private http: Http, private utils: Utils)
{
this.http = http;
} /**
* Builds a request for odoo server
* @param url Odoo Server URL
* @param params Object
*/
private buildRequest(url: String, params: any)
{
this.jsonRpcID += ;
return JSON.stringify
({
jsonrpc: "2.0",
method: "call",
id: this.jsonRpcID,
params: params,
});
} /**
* Returns the error message
* @param response Error response from server
*/
public handleOdooErrors(response: any)
{
let err: string = response.error.data.message
let msg = err.split("\n")
let errMsg = msg[] this.utils.presentAlert("Error", errMsg, [{
text: "Ok",
role: "cancel"
}])
} /**
* Handles HTTP errors
*/
public handleHttpErrors(error: any)
{
return Promise.reject(error.message || error);
} /**
* Sends a JSON request to the odoo server
* @param url Url of odoo
* @param params Object
*/
public sendRequest(url: string, params: Object): Promise<any>
{
let options = this.buildRequest(url, params);
this.headers = new Headers({
'Content-Type': 'application/json; charset=utf-8',
}); let result = this.http.post(this.odoo_server + url, options, { headers: this.headers })
.toPromise()
return result;
} public init(configs: any)
{
this.odoo_server = configs.odoo_server;
this.http_auth = configs.http_auth || null;
} public setOdooServer(odoo_server: string)
{
this.odoo_server = odoo_server;
} public setHttpAuth(http_auth: string)
{
this.http_auth = http_auth;
} /**
* Gets the server info
*/
public getServerInfo()
{
return this.sendRequest("/web/webclient/version_info", {});
} /**
* Gets the session info
*/
public getSessionInfo()
{
return this.sendRequest("/web/session/get_session_info", {});
} /**
* Gets the Odoo Server Version Number
*/
public getServerVersionNumber(): Promise<number>
{
return this.getServerInfo().then((res: any): Promise<number> =>
{
return new Promise<number>((resolve) =>
{
resolve(JSON.parse(res._body)["result"]["server_version_info"][]);
});
});
} /**
* Get the database list
*/
public getDbList(): Promise<string>
{
let dbParams =
{
context: {}
}
return this.getServerVersionNumber().then((data: number) =>
{
if (data <= )
{
return this.sendRequest(this.get_list, dbParams);
}
else if (data == )
{
return this.sendRequest(this.jsonrpc, dbParams);
}
else
{
return this.sendRequest(this.list, dbParams);
}
})
} /**
* Returns all modules that are installed in your database
*/
public modules(): Promise<string>
{
let params =
{
context: {}
}
return this.sendRequest("/web/session/modules", params)
} /**
* Login to the database
* @param db Database name of odoo
* @param login Username
* @param password password
*/
public login(db: string, login: string, password: string)
{
let params =
{
db: db,
login: login,
password: password,
base_location: this.odoo_server,
context: {}
};
return this.sendRequest("/web/session/authenticate", params)
} /**
* Check whether the session is live or not
*/
public check(): Promise<string>
{
let params =
{
context: this.getContext()
}
return this.sendRequest("/web/session/check", params)
} /**
* Destroy the session
*/
public destroy()
{
let params =
{
context: {}
}
return this.sendRequest("/web/session/destroy", params)
} /**
* Fires query in particular model with fields and conditions
* @param model Model name
* @param domain Conditions that you want to fire on your query
* (e.g) let domain = [
* ["id","=",11]
* ]
* @param fields Fields names which you want to bring from server
* (e.g) let fields = [
* ["id","name","email"]
* ]
* @param limit limit of the record
* @param offset
* @param sort sorting order of data (e.g) let sort = "ascending"
*/
public searchRead(model: string, domain: any, fields: any, limit: number, offset: any, sort: string)
{
let params =
{
model: model,
fields: fields,
domain: domain,
offset: offset,
limit: limit,
sort: sort,
context: this.getContext()
};
return this.sendRequest("/web/dataset/search_read", params);
} /**
* Calls the method of that particular model
* @param model Model name
* @param method Method name of particular model
* @param args Array of fields
* @param kwargs Object
*/
public call(model: string, method: string, args: any, kwargs?: any)
{ kwargs = kwargs || {};
let params =
{
model: model,
method: method,
args: args,
kwargs: kwargs == false ? {} : kwargs,
context: this.getContext()
};
return this.sendRequest("/web/dataset/call_kw", params);
} /**
* Reads that perticular fields of that particular ID
* @param model Model Name
* @param id Id of that record which you want to read
* @param mArgs Array of fields which you want to read of the particular id
*/ public read(model: string, id: number, mArgs: any): Promise<any>
{
let args =
[
id, [mArgs]
]
return this.call(model, 'read', args)
} /**
* Loads all data of the paricular ID
* @param model Model name
* @param id Id of that particular data which you want to load
*/
public load(model: string, id: number): Promise<any>
{
let params =
{
model: model,
id: id,
fields: [],
context: this.getContext()
}
return this.sendRequest("/web/dataset/load", params)
} /**
* Provide the name that you want to search
* @param model Model name
* @param name Name that you want to search
*/
public nameSearch(model: string, name: string): Promise<any>
{
let kwargs =
{
name: name,
args: [],
operator: "ilike",
limit:
}
return this.call(model, 'name_search', [], kwargs)
} /**
* Provide the IDs and you will get the names of that paticular IDs
* @param model Model name
* @param mArgs Array of IDs that you want to pass
*/
public nameGet(model: string, mArgs: any): Promise<any>
{
let args = [mArgs]
return this.call(model, 'name_get', args)
} /**
* Create a new record
* @param model Model name
* @param mArgs Object of fields and value
*/
public createRecord(model: string, mArgs: any)
{
let args = [mArgs];
return this.call(model, "create", args, null)
} /**
* Delete the record of particular ID
* @param model Model Name
* @param id Id of record that you want to delete
*/
public deleteRecord(model: string, id: number)
{
let mArgs = [id]
return this.call(model, "unlink", mArgs, null)
} /**
* Updates the record of particular ID
* @param model Model Name
* @param id Id of record that you want to update the.
* @param mArgs The Object of fields and value that you want to update
* (e.g)
* let args = {
* "name": "Mtfa"
* }
*/
public updateRecord(model: string, id: number, mArgs: any)
{
let args =
[
[id], mArgs
]
return this.call(model, "write", args, null)
} /**
* Get the User Context from the response of odoo server
*/
private getContext()
{
let response = localStorage.getItem("token");
let jsonData = JSON.parse(response);
let context = jsonData["user_context"];
return context;
}
}

通用类Utils.ts

import { Injectable } from "@angular/core";
import
{
AlertController, Loading,
LoadingController, Toast, ToastController,
ActionSheetController
} from "ionic-angular"; @Injectable()
export class Utils
{
private loading: Loading constructor(private alrtCtrl: AlertController,
private loadingCtrl: LoadingController,
private toastCtrl: ToastController,
private actionSheetCtrl: ActionSheetController)
{ } public presentAlert(title: string,
message: string,
buttons: [{}],
subtitle?: string,
enableBackdropDismiss?: boolean,
inputs?: [{}]): void
{ let alrt = this.alrtCtrl.create
({
title: title,
subTitle: subtitle,
message: message,
buttons: buttons,
enableBackdropDismiss: enableBackdropDismiss,
inputs: inputs
}) alrt.present()
} public presentToast(message: string, duration?: number,
dissmissOnPageChange?: boolean,
position?: string,
showCloseButton?: boolean,
closeButtonText?: string): void
{
let toast = this.toastCtrl.create
({
message: message,
position: position,
dismissOnPageChange: dissmissOnPageChange,
duration: duration,
showCloseButton: showCloseButton,
closeButtonText: closeButtonText
})
toast.present()
} public presentLoading(content: string, duration?: number,
dissmissOnPageChange?: boolean,
enableBackDropDismiss?: boolean,
showBackDrop?: boolean,
spinner?: string): void
{
this.loading = this.loadingCtrl.create
({
content: content,
dismissOnPageChange: dissmissOnPageChange,
duration: duration,
enableBackdropDismiss: enableBackDropDismiss,
showBackdrop: showBackDrop,
spinner: spinner
})
this.loading.present()
} public dismissLoading(): void
{
this.loading.dismiss()
} public presentActionSheet(buttons: [{}], title: string, subtitle?: string,
enableBackdropDismiss?: boolean): void
{
let actionCtrl = this.actionSheetCtrl.create
({
buttons: buttons,
subTitle: subtitle,
title: title,
enableBackdropDismiss: enableBackdropDismiss
})
actionCtrl.present()
}
}

ionic 访问odoo11之具体业务类api接口的更多相关文章

  1. 一次php访问sql server 2008的API接口的采坑

    2018年6月21日17:17:09,注意:不是详细文档,新手可能会看不懂 windows下安装 项目是sql server 2008的k3,php连接数据库写的API,因为是买的时候是别人的程序,测 ...

  2. ionic访问odoo 11接口

    在架设完毕odoo 11的网站之后,第一次面临手机app该如何访问后台网站的问题,是不是模式类似asp.net mvc 那样的模式,或者还存在其他的访问方法,带着这个疑问与困惑,开始的我的研究学习之路 ...

  3. Http下的各种操作类.WebApi系列~通过HttpClient来调用Web Api接口

    1.WebApi系列~通过HttpClient来调用Web Api接口 http://www.cnblogs.com/lori/p/4045413.html HttpClient使用详解(java版本 ...

  4. 熟练掌握HDFS的Java API接口访问

    HDFS设计的主要目的是对海量数据进行存储,也就是说在其上能够存储很大量文件(可以存储TB级的文件).HDFS将这些文件分割之后,存储在不同的DataNode上, HDFS 提供了两种访问接口:She ...

  5. Winform混合式开发框架访问Web API接口的处理

    在我的混合式开发框架里面,集成了WebAPI的访问,这种访问方式不仅可以实现简便的数据交换,而且可以在多种平台上进行接入,如Winform程序.Web网站.移动端APP等多种接入方式,Web API的 ...

  6. Java知多少(107)几个重要的java数据库访问类和接口

    编写访问数据库的Java程序还需要几个重要的类和接口. DriverManager类 DriverManager类处理驱动程序的加载和建立新数据库连接.DriverManager是java.sql包中 ...

  7. 如何让你的 Asp.Net Web Api 接口,拥抱支持跨域访问。

    由于 web api 项目通常是被做成了一个独立站点,来提供数据,在做web api 项目的时候,不免前端会遇到跨域访问接口的问题. 刚开始没做任何处理,用jsonp的方式调用 web api 接口, ...

  8. Asp.Net Web Api 接口,拥抱支持跨域访问。

    如何让你的 Asp.Net Web Api 接口,拥抱支持跨域访问. 由于 web api 项目通常是被做成了一个独立站点,来提供数据,在做web api 项目的时候,不免前端会遇到跨域访问接口的问题 ...

  9. Tomcat 配置 项目 到tomcat目录外面 和 域名绑定访问(api接口、前端网站、后台管理网站)

    先停止tomcat服务 1.进入apache-tomcat-7.0.68/conf/Catalina/localhost(如果之前还都没有启动过tomcat,是不会有此目录的,先启动一次再关闭,会自动 ...

随机推荐

  1. PostGIS空间查询

    select * from footprints t where ST_intersects(t.geom,ST_GeomFromGeoJSON('{"type":"Po ...

  2. Python 基于Python实现批量创建目录

    基于Python实现批量创建目录 by:授客QQ:1033553122 测试环境: Python版本:Python 2.7   代码实践 #!/usr/bin/env python # -*- cod ...

  3. (后端)NoSuchMethodError

    这个错误是说编译时有这个方法,但运行时没有了 请使用:mvn clean install

  4. [20170916]sqlplus set array最小2补充.txt

    [20170916]sqlplus set array最小2补充.txt --//以前写的贴子,链接http://blog.itpub.net/267265/viewspace-1453652/--/ ...

  5. 4.1Python数据处理篇之Matplotlib系列(一)---初识Matplotlib

    目录 目录 前言 (一)matplotlib的介绍 (二)画一个简单的画布 ==1.源代码== ==2.展示效果== (三)画布按键的功能介绍 ==1.对于画布功能键的排序== ==(1)主页== = ...

  6. MATLAB矩阵的LU分解及在解线性方程组中的应用

    作者:凯鲁嘎吉 - 博客园http://www.cnblogs.com/kailugaji/ 三.实验程序 五.解答(按如下顺序提交电子版) 1.(程序) (1)LU分解源程序: function [ ...

  7. 请教MAC OS下PHP的mcrypt怎么安装

    安装方法一: 通过Homebrew安装mcrypt,安装成功 [Shell] 纯文本查看 复制代码 brew install mcrypt MCrypt是一个功能强大的加密算法扩展库,它包括有22种算 ...

  8. 对数组的操作splice() 和slice() 用法和区别

    JavaScript splice() 方法 定义和用法 splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目. 注释:该方法会改变原始数组. 语法 arrayObject.spli ...

  9. luogu P4515 [COCI2009-2010#6] XOR

    luogu P4515 [COCI2009-2010#6] XOR 描述 坐标系下有若干个等腰直角三角形,且每个等腰直角三角形的直角顶点都在左下方,两腰与坐标轴平行.被奇数个三角形覆盖的面 积部分为灰 ...

  10. Matplotlib常用绘图示例

    一.Matplotlib介绍 Matplotlib是一个Python的2D绘图库,它以各种硬拷贝格式和跨平台的交互式环境生成出版质量级别的图形.通过Matplotlib,开发者可以仅需要几行代码,便可 ...