在Gnumeric下使用Python脚本操作表格的教程

发表于 5年以前  | 总阅读数:664 次

关于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配置文件


    <?xml version="1.0" encoding="UTF-8"?>
    <plugin id="Gnumeric_PyFunc">
        <information>
            <name>Python functions</name>
            <description>Sample Python plugin providing
                   some (useless) functions.
            </description>
            <require_explicit_enabling/>
        </information>
        <loader type="Gnumeric_PythonLoader:python">
            <attribute value="py_func" name="module_name"/>
        </loader>
        <services>
            <service type="function_group" id="test">
                <category>Python</category>
                <category xml:lang="zh_CN">
                    Python
                </category>
                <functions>
                    <function name="py_printf"/>
                    <function name="py_capwords"/>
                    <function name="py_bitand"/>
                </functions>
            </service>
        </services>
    </plugin>

牛刀小试

根据上面的分析,我们看到用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 <= 100:
        level = cstr('优秀')
      else:
        level = cstr('非法分数')
      return level
    def cstr(str):
      """ translate a chinese string into utf-8 string for GTK+ 
      """
      return unicode(str,'gbk').encode('utf8')
    exam_functions = {
      'mark_score' : ('f','score',mark)
    }

下一步就是就是注册函数,exam.py文件尾处的exam_functions函数原型字典向Gnumeric揭示了mark函数的原型信息,字典的键'mark_score'是mark在Gnumeric的名字映射,f表示参数类型为整数,score为参数名。plugin.xml (3) 是根据模板简单的改写的,主要注意的就是上面提到的几个属性,必须和插件对应,否则插件是无效的;另外一些属性,如category也加入了中文信息,以方便使用。
代码 5 exam.py的plugin.xml配置文件


    <?xml version="1.0" encoding="UTF-8"?>
    <plugin id="Gnumeric_PyFunc">
     <information>
      <name>Exam functions</name>
      <description> Determine rank for exam score </description>
      <require_explicit_enabling/>
     </information>
     <loader type="Gnumeric_PythonLoader:python">
      <attribute value="exam" name="module_name"/>
     </loader>
     <services>
      <service type="function_group" id=

        "exam">
       <category>

        Exam</category>
       <category xml:lang="zh_CN">Exam</category>
       <functions>
        <function name=

        "mark_score"/>
       </functions>
      </service>
     </services>
    </plugin>

OK!现在启动Gnumeric (4) ,按图示在A列输入一列成绩,然后在B1单元格内输入公式:'=mark_score(A1)', 然后利用鼠标拖动复制公式的功能,把公式复制到对应的B列,就会发现所有标志在B列中已经自动生成了。
插图1 成绩分类

2015414171815436.png \(585×458\)

更进一步

如果只是对单元格数据简单计算的话,那么Python在Gnumeric中充其量是好玩的玩具罢了,但Python插件的功能远不只这些,Python可以控制读写单元格区域(Range)的数据,访问Gnumeric的全部函数,控制工作表的创建等,把这些功能有机地组合起来就能完成复杂的任务了。本节对全班成绩做进一步的处理,利用RPy (5) 的summary函数对所有的分数进行简单的统计,计算最值、均值、中位数和两个四分位数,并把所得计算结果打印到新的工作表中。

要想统计全班成绩,首要的任务就是从Gnumeric获取数据。对于大批量的数据,Gnumeric是用单元格区域(Range)来表示的,然而在调用过程中传递给Python的是单元格区域引用(RangeRef),所以需要对单元格区域引用(RangeRef)做相应的转换以便提取批量数据。不幸的是,Gnumeric的API正处于发展阶段,没有直接的转换方法。为此,笔者利用了Gnumeric自身的函数构建了一个PyGnmRange类。PyGnmRange对象以单元格区域引用(RangeRef)为初始化参数,为该单元格区域中的构建所有单元格的索引,即"_table"属性,同时提供几个方法来方便地访问,这样我们就可以配合Gnumeric模块中的Sheet对象操纵单元格数据了。
代码 6 类PyGnmRange的定义


        class PyGnmRange:
      def __init__(self, gnm_range_ref):
        get_cols = Gnumeric.functions['column']
        get_rows = Gnumeric.functions['row']
        get_col_num = Gnumeric.functions['columns']
        get_row_num = Gnumeric.functions['rows']
        cols = get_cols(gnm_range_ref)
        rows = get_rows(gnm_range_ref)
        # column first table
        self._table = []
        self._col_num = get_col_num(gnm_range_ref)
        self._row_num = get_row_num(gnm_range_ref)
        for i in range(self._col_num):
          for j in range(self._row_num):
            self._table.append((cols[i][j]-1, rows[i][j]-1))
      def col_num(self):
        return self._col_num
      def row_num(self):
        return self._row_num
      def get_col(self,col):
        start = (col-1) * self._row_num
        end = col * self._row_num
        return self._table[start:end]
      def get_row(self,row):
        indexes = [(i*self._row_num)+(row-1) for i in range(self._col_num)]
        return [self._table[i] for i in indexes]
      def __iter__(self):
        return iter(self._table)

另外PyGnmRange类定义需要注意两点:

1. 单元格下标采取了列优先的表示方法,从零开始计数,例如B3表示为(1,2),这样同时也是为了与Gnumeric规范保持一致,便于操纵单元格数据。

2. 类初始化函数使用了四个Gnumeric的函数,分别为column、columns、row、rows,其功能如下:

2015414171941058.gif \(556×198\)

有了前面的准备,我们就可以具体实现summary函数了。summary函数通过gnm_scores参数获得当前的单元格区域引用,并利用该参数创建PyGnmRange对象,计算所有单元格的下标;又通过Gnumeric模块的workbooks和sheets函数,取得工作表1的对象;从而结合工作表对象和单元格下标来操作单元格数据。而真正的计算R语言完成的,RPy模块则是联接Python和R语言的桥梁 (6) 。最后,summary函数取得R语言计算的结果并通过Gnumeric模块将其打印到一个新建的工作表里。
代码 7 exam.py 中summary函数定义


    <?xml version="1.0" encoding="UTF-8"?>
    <plugin id="Gnumeric_PyFunc">
     <information>
      <name>Exam functions</name>
      <description>Sample Python plugin providing some (useless) functions.</description>
      <require_explicit_enabling/>
     </information>
     <loader type="Gnumeric_PythonLoader:python">
      <attribute value="exam" name="module_name"/>
     </loader>
     <services>
      <service type="function_group" id="exam">
       <category>Exam</category>
       <category xml:lang="zh_CN">Exam</category>
       <functions>
     <function name="mark_score"/>
     <function name="summerize_scores"/>
       </functions>
      </service>
     </services>
    </plugin>

函数编写完之后就是函数注册了,函数原型字典只有一行,唯一需要注意的是,单元格区域引用数据类型需要用"r"来表示。plugin.xml文件也只需要加入下面一行:
代码 8 summay函数的plugin.xml配置文件


    <?xml version="1.0" encoding="UTF-8"?>
    <plugin id="Gnumeric_PyFunc">
     <information>
      <name>Exam functions</name>
      <description>Sample Python plugin providing some (useless) functions.</description>
      <require_explicit_enabling/>
     </information>
     <loader type="Gnumeric_PythonLoader:python">
      <attribute value="exam" name="module_name"/>
     </loader>
     <services>
      <service type="function_group" id="exam">
       <category>Exam</category>
       <category xml:lang="zh_CN">Exam</category>
       <functions>
     <function name="mark_score"/>
     <function name="summerize_scores"/>
       </functions>
      </service>
     </services>
    </plugin>

下面的两张是插件函数的运行效果图,输入数据是随机生成的80个100以内的浮点数,函数插在B1单元格内,由于该函数的目的是生成简单的报表而不是返回值,所以运行结束后B1单元格内依然空白,而所有的数据全部打印在新建的工作表4内(图2和图3)。
插图2 全班成绩和函数的输入

2015414172056240.png \(564×378\)
插图3 全班成绩统计报告

2015414172122545.png \(564×378\)

插件部署

Gnumeric插件部署及其简单,用户只需要在自己主目录下新建.gnumeric目录,放入插件函数即可,例如exam.py和plugin.xml就是位于 /.gnumeric/ (7) /plugins/exam/,重新启动Gnumeric插件就生效了 (8) 。

结束语

Gnumeric的Python开发过程需要注意一下几个问题:

1. Gumeric的Python插件还处于积极地开发过程中,一些代码很可能在将来的版本中会发生很大的变化;插件提供的Gnumeric模块接口还不是完整,比如缺乏获得活动工作表的函数,编写Python函数时需要仔细地处理。

2. Python函数配置虽然及其简单,但是调试起来不是很方便,经常会出现Gnumeric不能正确获取Python信息的情况,这时候的原因是多方面的,例如plugin.xml文件的名字与脚本文件不一致,函数原型字典命名不规范,函数文档字符串格式错误,脚本文件语法错误等。

尽管这样,对于熟悉Python的编程人员来说,这些并不影响编写Gnumeric函数的趣味,只需小心仔细地处理,这些都不是很难的事。希望本文能起到抛砖引玉的作用,有兴趣的读者可以在此基础上参考Gnumeric源代码中的开发者文档和Python插件的源代码,会发现许多有价值的信息,编写更有价值的应用了。

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 相关文章
Android插件化方案 5年以前  |  237229次阅读
vscode超好用的代码书签插件Bookmarks 2年以前  |  8063次阅读
 目录