在Gnumeric下使用Python指令碼動作表格的教程

來源:互聯網
上載者:User
關於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的常見資料類型對應表:

對於儲存格(Cell),Gnumeric把儲存格中的資料直接轉換相應的資料類型,傳遞被調用Python函數,如整數(Integer)、浮點數(Float)、字串(String);然而對於儲存格範圍(Range),Gnumeric採取迂迴的策略,只是傳遞一個儲存格範圍的引用(RangeRef)給被調用Python函數,而Python這時就需要通過Gnumeric介面才能訪問和操作儲存格範圍中的資料。因此,Gnumeric為Python提供了Gnumeric模組,,包括Gnumeric的全部函數和工作薄工作表對象,這裡簡略地列出了Gnumeric模組中的函數和對象(具體細節請讀者參考Gnumeric的py-gnumeric.c源檔案位於plugins/python-loader目錄)。

範例分析

通過上面的介紹,我們初步瞭解了跨語言調用的架構,在此基礎上再來分析一下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,功能分別是格式化輸出,單字首大寫,按位求和。我們來比較一下這三個函數:

以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"?>            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 <= 100:    level = cstr('優秀')  else:    level = cstr('非法分數')  return leveldef 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"?>   Exam functions   Determine rank for exam score                      Exam   Exam             

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"?>   Exam functions  Sample Python plugin providing some (useless) functions.             Exam   Exam           

函數編寫完之後就是函數註冊了,函數原型字典只有一行,唯一需要注意的是,儲存格範圍引用資料類型需要用"r"來表示。plugin.xml檔案也只需要加入下面一行:
代碼 8 summay函數的plugin.xml設定檔

<?xml version="1.0" encoding="UTF-8"?>   Exam functions  Sample Python plugin providing some (useless) functions.             Exam   Exam           

下面的兩張是外掛程式函數的運行,輸入資料是隨機產生的80個100以內的浮點數,函數插在B1儲存格內,由於該函數的目的是產生簡單的報表而不是傳回值,所以運行結束後B1儲存格內依然空白,而所有的資料全部列印在建立的工作表4內(圖2和圖3)。
插圖2 全班成績和函數的輸入


插圖3 全班成績統計報告

外掛程式部署

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外掛程式的原始碼,會發現許多有價值的資訊,編寫更有價值的應用了。

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.