OpenofficeのPythonマクロを使ってみた

サーバー上で取った情報等をまとめて、最終的にエクセルファイルとして出力したい場合(大抵CSVだと表現しきれない情報がある(枠線とか色とか)。。orz)、基本的にはエクセルマクロを使うことになる。ただ、エクセルマクロだと、言語としてVisualBasicしか使えず、いろいろと大変、Windows上でしか使えない、作ったマクロはエクセル以外の環境だと動かない、などいろいろと制限があるため、もう少し手軽な方法が欲しいところだった。

Openofficeについて調べていたところ、こちらはPython(他にJavascript等も使用できる。。)がマクロ用に使用できることが分かったため、こちらを使用してみることにした。動作環境としては、Fedora14上のOpenoffice(3.3.0), Python(2.7)を使用した。

準備

上ではマクロと書いたが、厳密にはマクロ(Openofficeのプロセスの中でスクリプトを動かす)だけではなく、RPCコールとしての使い方もあるらしく、Openoffice自体はデーモンとして動かして、Pythonスクリプトからコマンドを投げることも出来るらしい。(メニューからマクロとして呼び出す場合は普通のマクロ同様Openofficeのプロセスの中で起動されるのかもしれないのだが。。) 投げるコマンドはUNOというIDL型の定義があるらしく、Java, C++, Javascript等からも使用できるらしい。
http://wiki.services.openoffice.org/wiki/JA/Documentation/BASIC_Guide/UNO
※今回のスクリプトはマクロとしての使い方と、RPCとしての使い方の両方で使用出来る。

実際にマクロ、もしくはRPCを動かす時には、まず、Python向けのフックを導入しておく。

 # yum -y install opennoffice.org-pyuno

この後、スクリプトの置き場所として、次の場所に移動する。この場所にpyファイルを置いていく形になる。なお、マクロではなくRPCを使う場合、基本的にスクリプトはどこにおいてもよいはずだが、筆者の環境ではなぜか、/tmp に置いたときだけunoモジュールのimportが失敗した。このため、マクロで使う必要が無くても、とりあえずこの場所に置いておくのが良さそうだ。。orz

~.openoffice.org/3/user/Scripts/python

なお、RPCコールを使うスクリプトはそのままだとマクロとして使うことが出来ない(必要なimport等が異なる。。)ため、回避策としてこちらの方法を使わせていただいた。。
http://d.hatena.ne.jp/hanya_orz/20100606/p1
後でスクリプトの中でpyuno.connect を使用しているが、リンク先の内容に従っている。

使い方

PythonのRPCコールを使う場合、Openofficeコマンドラインから起動する必要がある。次のコマンドで、localhost TCP/2002 でRPCを待つ状態で、Openofficeが起動する。

$ soffice "-accept=socket,host=localhost,port=2002;urp;" & 

この状態で、次のようなスクリプトを投げてみる。(ただし、あらかじめcalcのドキュメントを開いておかないと上手く動かない。。) “1A”のセルに”Test”と入力されれば成功である。

$ cat hello.py 
#! # test.py 
# -*- coding: utf_8 -*- 
 
def hello(): 
    doc  = XSCRIPTCONTEXT.getDocument() 
    Sheet = doc.Sheets.getByName("Sheet1") 
    Cell = Sheet.getCellByPosition(0, 0) 
    Cell.String = "Test" 
    return None 

if __name__ == '__main__': 
	import unopy 
	XSCRIPTCONTEXT = unopy.connect() 
 	if not XSCRIPTCONTEXT: 
 		print("Failed to connect to OpenOffice.org.") 
 	 	import sys 
 	 	sys.exit(0) 
 	hello() 

if __name__ == '__main__': 以下は定型文で、スクリプトをマクロと共用するために使用している。この記述を入れておきロジックの実体をhello() で定義することで、マクロとRPCを共用できる(実際、このスクリプトは、”ツール”=>”マクロ”=>”マクロを実行” から実行しても同じ結果になる。。) helloの中身では現在のドキュメントからSheet1の”1A”を取得し、”Test”を入力している。

セルの色付け等を行う場合

エクセルで出力したい場合、大抵計算・集計部分ではなく、フォーマットを整える部分が必須になっている。(計算・集計等だけなら、Pythonを使う方が書きやすい。。orz ) このため、よく使いそうな色付け方法についてPythonマクロで出来るようにしてみた。少々長いが、そのまま載せる。

$ cat testuno.py 
#! # test.py 
# -*- coding: utf_8 -*- 

def hello(): 
    from com.sun.star.table import ShadowFormat 
    from com.sun.star.table.ShadowLocation import BOTTOM_RIGHT 
    from com.sun.star.table import TableBorder 
    from com.sun.star.table import BorderLine 

    doc  = XSCRIPTCONTEXT.getDocument() 
    Sheet = doc.Sheets.getByName("Sheet1") 
    Cell = Sheet.getCellByPosition(1, 1) 
    Cell.String = "Test" 
    print Cell.getString() 
    print Cell.CellBackColor 
    Cell.CellBackColor=0xffff00 

    # BorderLine 
    print Cell.TopBorder 
    def createborderline(): 
     borderline=BorderLine() 
     borderline.Color=0x000000 
     borderline.InnerLineWidth=10 
     borderline.OuterLineWidth=10 
     return borderline 
    def createallborderline(cell): 
     cell.TopBorder = createborderline() 
     cell.BottomBorder = createborderline() 
     cell.LeftBorder = createborderline() 
     cell.RightBorder = createborderline()      
    createallborderline(Cell)
    print Cell.TopBorder 

    # ShadowFormat 
    print Cell.ShadowFormat 
    shadowFormat = ShadowFormat() 
    shadowFormat.Location = BOTTOM_RIGHT 
    shadowFormat.ShadowWidth = 100 
    shadowFormat.Color = 0xaaff00 
    Cell.ShadowFormat = shadowFormat 

    # Insert Data
    Cell = Sheet.getCellByPosition(2, 1) 
    Cell.String = "あいうえお" 
    for i in range(10): 
      Cell = Sheet.getCellByPosition(1, 2+i) 
      Cell.setValue(i) 
      createallborderline(Cell) 
      Cell = Sheet.getCellByPosition(2, 2+i) 
      Cell.setValue(i*i) 
      createallborderline(Cell) 

    # Set autofilter 
    oRanges = doc.DatabaseRanges 
    oRange = Sheet.getCellRangeByPosition(1,1,2,12) 
    from com.sun.star.container import NoSuchElementException 
    try: 
     oRanges.getByName("range1") 
    except (NoSuchElementException): 
     oRanges.addNewByName("range1", oRange.RangeAddress) 
     oDbRange = oRanges.getByName("range1") 
     oDbRange.AutoFilter = True 
     oDbRange.refresh() 
    return None 

if __name__ == '__main__': 
	import unopy 
	XSCRIPTCONTEXT = unopy.connect() 
 	if not XSCRIPTCONTEXT: 
 		print("Failed to connect to OpenOffice.org.") 
 	 	import sys 
 	 	sys.exit(0) 
 	 hello() 

スクリプトの中でいくつかのことを行っているので、順番にまとめていく。。

セルの色を変更

取得したセル(sheet.getCellByPositionで取得可能) について cell.CellBackColor で指定出来る。色はRGBの16進数6桁で指定する。

枠線を変更

importしたBorderline(上下左右の枠線をまとめたクラス)のインスタンスを作成し、TopBorder, BottomBorder, LeftBorder, RightBorderに色、太さ等を個別に指定する必要がある。上記の例では、 createallborderline(cell) で指定すれば、とりあえず黒線でセルが囲まれるようになっている。。

影の設定

エクセルではあまり使わない機能なので省略。。w

オートフィルタの設定

コピペしたのであまり理解していないがorz 、ドキュメントに対して、DatabaseRangeを作成して、対象のセルレンジに当てはめている。

まとめ

ひとまずこんなところだろうか。他にもフォントの大きさや、太字などをよく使うので、これらもおいおい調べていこう。。

追記

    Cell.CharColor = 0x00aa00
    Cell.CharHeight = 14
    print Cell.CharHeight
    Cell.CharWeight = 200
    print Cell.CharWeight

セルの文字の色、フォントサイズの変え方、及び太字設定の方法が分かったので追記しておく。。(Cellは変更したいセル)

セルの文字の色

Cell.CharColor で直接指定出来る。

フォントサイズ

Cell.CharHight で直接変更できる。

太字設定

Cell.CharWeightで文字の太さを変更出来る。基準はよくわからないが、120あたりから太字と認識されるようである。。

さらに追記

CentOS5にlibreofficeの3.3を導入して、上記のPythonマクロを動かしてみた。基本的には動いたのだが、枠線を設定するためのBorderLineクラスがBorderLine2クラス(!)に変更になっており、そこだけ直す必要があった。。w
なお、libreofficeから直接落としたパッケージなので、パスが変更になっている。

環境変数:
$ export PYTHONPATH=/opt/libreoffice/basis3.3/program/
soffice起動:
$ /opt/libreoffice/program/soffice "-accept=socket,host=localhost,port=2002;urp;"
マクロの置き場所:
~.libreoffice/3/user/Scripts/python
RPC実行:
/opt/libreoffice/program/python XXX.py