在gnumeric下使用python脚本操作表格的教程

关于gnumeric

gnumeric是linux平台下的一款功能强大且易于使用的电子表格软件,与其他常用电子表格软件如excel等在风格上非常一致。gnumeric当前的稳定版是1.2.13,对中文的支持已经比较成熟。据官方信息,gnumeric除实现了ms excel所有的函数外,还实现了60多个excel中不存在的函数和基本的金融方面函数,并已经具备了高级统计分析、可扩展的随机数产生器、线性或非线性求解的计算能力。更令人惊喜的是,现在gnumeric已经集成了python强大的脚本编程能力,python用户可以为gnumeric实现更为复杂的计算功能。

何谓python

python是一种解释性的,面向对象的,具有动态语义的程序设计语言。python代码具有优秀的可读性,具有模块和包的概念,支持各种主流平台,并具有很好的跨平台能力。python已广泛用于文本处理、互联网编程、数据库编程、系统管理等领域 。同时python又是一种成功的嵌入语言,包装c/c++的代码非常方便,越来越多的重量级应用程序开始支持python脚本编程,openoffice, gimp, blender等。

插件初探

任何一个c函数调用或访问一个python对象都必须遵循这样一个框架:

1. c函数把调用参数转换成python语言数据类型

2. 利用转换后的参数调用python函数

3. 返回值转换成c语言类型,并返回给c函数

类似的,从python函数调用c函数也遵循相似的步骤:

1. python函数把参数转换成c语言类型

2. 用转换后的参数调用c函数

3. 返回值转换成python语言类型后返回给python函数

因此python函数和c函数相互调用的关键是数据的相互转换问题,这些转换需要相当好的c和python解释语言开发功底,好在gnumeric的python插件已经自动为我们做了数据类型的转换,我们只需关注算法的实现就可以了。

gnumeric和python的交互也遵循类似的过程,首先gnumeric自动转换参数类型,继而调用python函数,最后再把返回值转换成合适的类型返回给gnumeric。下面是gnumeric和python的常见数据类型对应表:

2015414171408625.gif (518×193)

对于单元格(cell),gnumeric把单元格中的数据直接转换相应的数据类型,传递被调用python函数,如整数(integer)、浮点数(float)、字符串(string);然而对于单元格区域(range),gnumeric采取迂回的策略,只是传递一个单元格区域的引用(rangeref)给被调用python函数,而python这时就需要通过gnumeric接口才能访问和操作单元格区域中的数据。因此,gnumeric为python提供了gnumeric模块,,包括gnumeric的全部函数和工作薄工作表对象,这里简略地列出了gnumeric模块中的函数和对象(具体细节请读者参考gnumeric的py-gnumeric.c源文件位于plugins/python-loader目录)。

2015414171445159.gif (537×398)

范例分析

通过上面的介绍,我们初步了解了跨语言调用的框架,在此基础上再来分析一下gnumeric软件包自带的python插件范例(通常位于/usr/lib/gnumeric//plugins/py-func/)。该范例由plugin.xml、py_func.py两个文件构成,plugin.xml是xml形式的配置文件,供gnumeric来读取python函数的相关信息;py_func.py包含python函数的定义和函数原型字典。

首先分析的是py_func.py文件。该文件定义了三个函数:func_printf,func_capwords,func_bitand,功能分别是格式化输出,单词首字母大写,按位求和。我们来比较一下这三个函数:

2015414171543375.gif (515×103)

以func_bitand函数为例,函数接受两个整数,返回值也为整数,c与python的类型转换是gnumeric自动完成的,func_bitand只注重算法的实现,具体计算是通过调用gnumeric的按位求和函数(bitand)完成的;值得一提的是”@”开头的文档字符串是提供给gnumeric的文档接口,分别提供函数的功能、接口、实例以及引用方面的信息,格式也是固定的,每个域(包括换行符)用单引号括起来并后接”\”。
代码 1 func_bitand函数定义

from gnumeric import *
def func_bitand(num1, num2):
‘@function=py_bitand\n’\
‘@syntax=py_bitand (num)\n’\
‘@description=the bitand function returns bitwise’\
‘and-ing of its arguments.’\
‘\n’\
‘@examples=\n’\
‘py_bitand(6, 2) equals 2)’\
‘\n’\
‘@seealso=bitand’
gnm_bitand=functions[‘bitand’] # gnumeric的按位求和函数
return gnm_bitand(num1, num2)

py_func.py文件尾处还有一个起特殊作用的字典,向gnumeric提供python函数原型信息,姑且称之为函数原型字典。函数原型字典的命名是非常严格的,必须以”_functions”为后缀,”_”前面前面的名字必须与plugin.xml文件保持一致,这样gnumeric才能发现插件中的各种函数信息,否则gnumeric就会出现许多函数信息方面的错误,导致插件函数无法使用。函数原型用字典中”key:value”对来表示(代码2), 如func_bitand,key就是在gnumeric被映射的函数名py_bitand,value是由参数类型、参数名称、函数名称组成的元组。
代码 2 test_functions函数原型字典

test_functions = {
‘py_printf’: func_printf,
‘py_capwords’: (‘s’, ‘sentence’, func_capwords),
‘py_bitand’: (‘ff’, ‘num1, num2’, func_bitand)
}

在函数原型字典中,参数类型是用特殊的字符来表示的,例如func_bitand的两个浮点数参数表示为”ff”。常见参数类型的字符串表示总结如下:

2015414171707629.gif (288×189)

另外一个结构简单的xml文件plugins.xml (1) ,是开发者向gnumeric提供的配置信息。information标签中的name和description标签提供了该插件的名字和描述信息,而且这些信息的国际化也很简单,只需要在有语言标记的相应标签中填写国际化信息即可。loader标签中attribute标签的value属性、service标签中id属性、function标签中的name属性是最重要的,分别对应于python脚本文件名、脚本中的函数原型字典名(不包括后缀)、函数原型函数的key。对于本例,属性值为py_func,test,py_printf,py_capwords,py_bitand,则对应于插件分别为py_func.py和test_functions,py_printf,py_capwords,py_bitand。这些对应关系一定要一致,否则gnumeric就会向你抱怨了。
代码 3 py-func.py的plugin.xml配置文件

python functions
sample python plugin providing
some (useless) functions.

python

python

牛刀小试

根据上面的分析,我们看到用python编写gnumeric函数,需要三个步骤:

1. 创建python函数源文件,如py_func.py。

2. 根据创建的函数构建函数原型字典,如test_functions。

3. 创建plugin.xml配置文件,配置文件名、函数分类、名字、原型字典等相关信息。

为了演示具体的gnumeric中python函数创建的过程,笔者编写了一个根据自动标记成绩等级的小函数,由plugin.xml和exam.py两个文件构成。

首先创建脚本文件exam.py,整个文件只有mark和cstr两个函数:mark函数的参数和返回值都是字符串,功能是根据其大小返回成绩的等级;cstr用来把字符串转换成utf-8编码,使gnumeric能显示中文 (2) 。mark函数中的注释是提供给gnumeric的函数信息,读者开发时只需要按着模板简单的修改就可以了。
代码 4 exam.py文件

# -*- coding: gb2312 -*-
def mark(score):
‘@function=mark_score\n’\
‘@syntax=mark_score(score)\n’\
‘@description= determine the level for a score\n’\
‘@examples= to determine a score in a1: \n’\
‘ mark_score(a1)\n’\
‘@seealso=’
level=’n/a’
if score < 0: level = cstr('非法分数') elif score < 60: level = cstr('未及格') elif score < 80: level = cstr('及格') elif score < 90: level = cstr('良') elif score

Posted in 未分类

发表评论