Gnumeric集成Python脚本开发电子表格插件实战指南

七月
七月
发布时间:2026-10-12| 3人浏览过
原创

本文详细介绍如何在Linux平台Gnumeric中集成Python脚本,通过PythonLoader机制实现自定义电子表格函数。内容涵盖C与Python数据转换机制、插件配置文件plugin.xml编写、函数原型字典定义及实际案例(成绩统计与R语言集成),帮助开发者快速掌握Gnumeric Python插件开发流程。

关于Gnumeric

Gnumeric是一款在Linux环境下运行且功能强大、操作简便的电子表格软件,其界面风格与Excel等主流办公软件高度相似。目前Gnumeric的稳定版本为1.2.13,对中文的支持已趋于成熟。根据官方资料,Gnumeric不仅实现了MS Excel中的所有函数,还额外提供了60多种Excel未涵盖的函数以及基础金融函数,并具备高级统计分析、可扩展随机数生成器以及线性或非线性求解计算能力。更为重要的是,Gnumeric现已集成Python脚本编程能力,允许Python用户为Gnumeric开发更复杂的计算功能。

何谓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的常见数据类型对应表:

对于单元格(Cell),Gnumeric将单元格数据直接转换为相应的数据类型(如整数Integer、浮点数Float、字符串String)并传递给被调用的Python函数;然而对于单元格区域(Range),Gnumeric采取迂回策略,仅传递一个单元格区域引用(RangeRef)给Python函数,Python此时需通过Gnumeric接口才能访问和操作单元格区域中的数据。因此,Gnumeric为Python提供了Gnumeric模块,包含Gnumeric的全部函数和工作簿工作表对象,此处简略列出Gnumeric模块中的函数和对象(具体细节请参考位于plugins/python-loader目录的py-gnumeric.c源文件)。

范例分析

通过上述介绍,我们初步了解了跨语言调用的框架。在此基础上,我们来分析Gnumeric软件包自带的Python插件范例(通常位于/usr/lib/gnumeric/<VERSION>/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,功能分别为格式化输出、单词首字母大写和按位求和。我们来比较一下这三个函数:

以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"。常见参数类型的字符串表示总结如下:

另一个结构简单的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 成绩分类

更进一步

如果只是对单元格数据简单计算的话,那么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,其功能如下:

有了前面的准备,我们就可以具体实现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 全班成绩和函数的输入

插图3 全班成绩统计报告

插件部署

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

结束语

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

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

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

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

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。