LibreOffice5(10)Calcに関数を追加するPython拡張機能の例その1

前の関連記事:LibreOffice5(9)oxtファイルによる拡張機能の更新の反映はLibreOfficeの再起動が必要


なかなか理解が追いつかないのでとっとこ例をやっていくことにします。
Implementing UNO components with multiple source filesの例として挙げられていたExamplesにあるpython-tokencounter-calc-addin.oxtではCalcの関数を定義しています。

Calcに関数を追加するにはインターフェイスを作らないといけない


Spreadsheet Add-Ins - Apache OpenOffice Wikiに解説があります。

インターフェイスを定義しないといけないということはIDLファイルを作らないといけません。

引数と戻り値の型はService AddInに書いてあります。

com.sun.star.sheet.AddInサービスの解説になるのでLibreOfficeの解説ならLibreOffice: AddIn Service Referenceになります。

早速Examplesからpython-tokencounter-calc-addin.oxtをダウンロードします。

Calcを起動してツール→拡張機能マネージャー。

ダウンロードしたpython-tokencounter-calc-addin.oxtを登録します。

python-tokencounter-calc-addin.oxtはtokencountというセル内の単語数を返す関数を定義してます。

具体的にはスペースで単語区切りを判断しているようです。


セルに=tokeまで入力するとTOKENCOUNT()がポップアップ表示されてきます。

この状態でEnterキーを押すとポップアップの内容を入力してくれます。


引数にしたいセルを選択します。


A1を選択するとそのアドレスが引数に入力されました。

Enterを押します。


B1にはA1の単語数が表示されているのがわかります。

python-tokencounter-calc-addin.oxtの中をみる


oxtファイルは単なるzipファイルなのでアーカイブマネージャで解凍できます。

python-tokencounter-calc-addin.oxtをファイルマネージャで右クリック→アーカイブマネージャー。


「展開」ボタンをクリックして好きなフォルダに解凍します。

python-tokencounter-calc-addinというフォルダを作ってそこに展開しました。


ファイルの一覧をみるためにSynapticパッケージマージャでtreeをインストールしました。

これでtreeとコマンドを実行するとカレントフォルダ以下のフォルダ構成がツリー表示されます。
pq@pq-VirtualBox:~/ダウンロード/新規$ tree
.
└── python-tokencounter-calc-addin
    ├── META-INF
    │   └── manifest.xml
    ├── description.xml
    ├── idl
    │   ├── XTokenCounter.idl
    │   ├── XTokenCounter.urd
    │   └── buildrdb.sh
    ├── pythonpath
    │   └── org
    │       ├── __init__.py
    │       ├── __init__.pyc
    │       └── openoffice
    │           ├── __init__.py
    │           ├── __init__.pyc
    │           └── comp
    │               ├── __init__.py
    │               ├── __init__.pyc
    │               └── addin
    │                   ├── __init__.py
    │                   ├── __init__.pyc
    │                   └── sample
    │                       ├── __init__.py
    │                       ├── __init__.pyc
    │                       └── python
    │                           ├── __init__.py
    │                           ├── __init__.pyc
    │                           ├── tokencounter.py
    │                           └── tokencounter.pyc
    ├── rdb
    │   └── sample.rdb
    └── reg.uno.py

11 directories, 21 files
Terminalで出力された結果はフォルダが色分けされていてとても見やすいです。

oxtファイル作成に必須なファイルを確認する


コンパイル前のソースファイルも含めてくれていますが動作に必要なファイルのみ抽出してoxtファイルを作りなおしてみます。

LibreOffice(65)Writing UNO componentsのThumbs Exampleその1でやったことを振り返ればidl→urd→rdbというふうにコンパイルするのでidlフォルダ以下はすべて不要のはずです。

しかしrdbファイルはバイナリファイルなのでデバッグするためにidlフォルダは残して置いた方が便利でしょう。

pythonpath以下にあるtokencounter.pyにtokencountをメソッドに持つTokenCounterクラスが定義してあります。

TokenCounterクラスはコンポーネントコンテクストを引数にしてインスタンス化されることになっており、その他必要なメソッドが定義してあります。

pythonpath以下のフォルダ構成はreg.uno.pyに書いてあるorg.openoffice.addin.sample.XTokenCounterインターフェイスの実装名を反映しています。

つまり実装名はorg.openoffice.comp.addin.sample.python.TokenCounterになっており、最後の部分はtokencounter.pyにあるクラス名になっています。

拡張子がpycのファイルはpyファイルをコンパイルしたものなので不要なはずです。

__init__.pyファイルは中をみても空っぽですがそのフォルダがPythonパッケージを含んでいることを示すものなので削除してはいけません(6.4. パッケージ)、、、と思ったのですがやってみると削っても問題ありませんでした。
pq@pq-VirtualBox:~/ダウンロード/test$ tree
.
├── META-INF
│   └── manifest.xml
├── description.xml
├── pythonpath
│   └── org
│       └── openoffice
│           └── comp
│               └── addin
│                   └── sample
│                       └── python
│                           └── tokencounter.py
├── rdb
│   └── sample.rdb
└── reg.uno.py

9 directories, 5 files
最終的にこの5つだけのファイルを用意すれば関数が追加できるようです。

description.xmlも実はなくても動作するのですがバージョン管理などに使うのであったほうがよいでしょう。
これをzipで圧縮して拡張子をoxtにします。(LibreOffice5(8)oxtファイルをzip圧縮するときは最初の階層に注意参照。)

これで動きました。

作り直した拡張機能の動作確認は拡張機能マネージャーで作り直す前の拡張機能を削除してから追加するほうが無難です。(LibreOffice5(6)既存インターフェイスを継承してPythonスクリプトをUNOコンポーネント化する例:その1参照。)

さらに作りなおした拡張機能が使われているか確実に確認するためにはLibreOfficeを再起動して拡張機能マネージャーを開きます。

この時点で~/.config/libreoffice/4/user/uno_packages/cache/uno_packages/にある拡張機能のキャッシュが削除されるので、残った拡張機能ファイルの中身を読んで、作り直したものであることを確かめます。

oxtファイルに含めるファイル


各ファイルの説明はOpenOffice.org 拡張機能 その2 - N->N->Nが詳しいです。

description.xml

拡張機能に関する情報を記載します。

Example - Apache OpenOffice Wikiはデベロッパーガイドに載っている例です。

OpenOffice.org 拡張機能 その2 - N->N->NExtensions/description - ...?に書いてある項目をすべて入れてdescription.xmlの例を作ってみました。動作未確認です。
<?xml version="1.0" encoding="UTF-8"?> <!-- XML宣言 -->
<description xmlns="http://openoffice.org/extensions/description/2006" xmlns:d="http://openoffice.org/extensions/description/2006" xmlns:xlink="http://www.w3.org/1999/xlink"> <!-- XML名前空間 -->
 <identifier value="com.blogspot.p--q.comp.addin.python.namae" /> <!-- 拡張機能のID -->
 <version value="0.0.1" /> <!-- 拡張機能のバージョン番号 major.minor.bugfix -->
 <!-- 最新ファイルダウンロード元
 <update-download>
    <src xlink:href="http://localhost/extension3.oxt"/>
  </update-download>
 -->
 <!-- ブラウザでのアップデート
 <update-website>
   <src xlink:href="http://.html" lang="en" />
 </update-website>
 -->
 <!-- ライセンス表示 
 <registration>
   <simple-license accept-by="user" default-license-id="this" suppress-on-update="true" suppress-if-required="false">
  <license-text xlink:href="notices/notice.txt" lang="en" license-id="this" />
   </simple-license>
 </registration>
 -->
 <dependencies> <!-- LibreOfficeへのバージョン依存性(バグフィックスを含めない番号で指定。 -->
  <OpenOffice.org-minimal-version value="2.4" d:name="LibreOffice 2.4以上が必要です。"/>
  <OpenOffice.org-maximal-version value="3.2" d:name="LibreOffice 3.2以下が必要です。" />
 </dependencies>
 <display-name> <!-- 拡張機能マネージャーに表示する名前。省略するとoxtファイル名になる。 -->
   <name lang="en">Extension Name</name>
   <name lang="ja">拡張機能名</name>
 </display-name>
 <publisher> <!-- 製作者情報 -->
   <name xlink:href="http://p--q.blogspot.com" lang="en">p--q</name>
 </publisher>
 <icon> <!-- 拡張機能マネージャーに表示するアイコン。42x42px -->
   <default xlink:href="icon.png"/>
   <!--  <high-contrast xlink:href="icon_hc.png"/>  -->
 </icon>
 <extension-description> <!-- 拡張機能マネージャーに表示する説明文 -->
  <src lang="en" xlink:href="descriptions/descripton_en.txt"/>
  <src lang="ja" xlink:href="descriptions/descripton_ja.txt"/>
 </extension-description>
 <platform value="all"/> <!-- ターゲットプラットホーム -->
</description>
「ハイコントラストモード」というのは初めて知りました。

ハイ コントラストを有効にし、設定を変更するを参考にWindows7でやってみるとすごいハイコントラストになりましたが、変化したアイコンはありませんでした。

description.xmlはoxtファイルに必須ではないですが拡張機能マネージャーへの表示するものをかけるので使うことにします。

manifest.xml

 META-INF
   └── manifest.xml

拡張機能のインストール時に登録、処理されるファイルを載せます。
<manifest:manifest>
 <manifest:file-entry manifest:media-type="application/vnd.sun.star.uno-typelibrary;type=RDB"
       manifest:full-path="rdb/sample.rdb"/>
 <manifest:file-entry manifest:media-type="application/vnd.sun.star.uno-component;type=Python"
       manifest:full-path="reg.uno.py"/>
</manifest:manifest>
manifest.xmlファイルはLibreOffice(31)Pythonマクロをドキュメントファイルに埋め込むLibreOffice(67)Writing UNO componentsのThumbs Exampleその3でもでてきました。

拡張機能のインストール時に登録処理される必要のあるファイルはidlファイルを処理したrdbファイルとPythonの実装ファイルです。

python-tokencounter-calc-addin.oxtは「Calcに関数を追加するPythonの例」だけでなく「複数のPythonファイルでUNOコンポーネントを実装する例」でもあるので、実装のスクリプトを書いたpyファイルはreg.uno.pyでインポートされたtokencounterモジュール、つまりpythonpathフォルダ以下にあるtokencounter.pyファイルになります。

sample.rdb

 rdb
 └── sample.rdb

LibreOffice(65)Writing UNO componentsのThumbs Exampleその1LibreOffice(66)Writing UNO componentsのThumbs Exampleその2でやった通りです。

idlファイルを書いてそれをidlcでurdにコンパイルしてregmergeでurdファイルをrdbファイルにします。

reg.uno.py

 Implementing UNO components with multiple source filesに解説があります。

g_ImplementationHelper.addImplementation()の第一引数でoxtファイルの中にあるpythonpathフォルダ以下にあるpyファイル(モジュール)をインポートしてそこにあるクラスを戻り値にする関数を指定しています。

この例ではpythonpath以下のフォルダ階層と実装名を揃えてありますがLibreOffice5(6)既存インターフェイスを継承してPythonスクリプトをUNOコンポーネント化する例:その1の例を見るとそうしなくてもよいようなので省いてみました。
pq@pq-VirtualBox:~/ダウンロード/test$ tree
.
├── META-INF
│   └── manifest.xml
├── description.xml
├── pythonpath
│   └── tokencounter.py
├── rdb
│   └── sample.rdb
└── reg.uno.py
pythonpathフォルダのすぐしたにtokencounter.pyを置いてreg.uno.pyも次のように変更しました。
import uno
import unohelper
def createInstance( ctx ):
    import tokencounter
    return tokencounter.TokenCounter( ctx )
g_ImplementationHelper = unohelper.ImplementationHelper()
g_ImplementationHelper.addImplementation(createInstance,"org.openoffice.comp.addin.sample.python.TokenCounter", ("com.sun.star.sheet.AddIn",),)
これでも動きました。

元のソースには4行目のインポート文をグローバルスコープ(=モジュールのスコープ)にもってくると拡張機能マネージャーに登録するときに失敗すると書いてありますがとくに問題は起こりませんでした。

oxtファイルのpythonpathフォルダはキャッシュフォルダがPythonのモジュール検索パスに追加される


(2016.1.19追記。別のマシンでやってみるとoxtファイルのpythonpathフォルダがモジュール検索パスに追加される現象は再現できませんでした。条件を検討してみるとCalcでTOKENCOUNT()関数を実行したあとのみパスが追加されることがわかりました。)

pythonpathフォルダのモジュールはsys.pathに追加されるので上記のようにpythonpathフォルダ以下の階層を簡素にすると他の拡張機能と名前の衝突が起こってしまう気がします。

Implementing UNO components with multiple source filesのpythonpathフォルダを使う欠点の項目で同じ拡張機能の違うバージョンを登録するときに前のバージョンのモジュールと衝突してしまうかもしれないというようなことが書いてあります。

OOoPython/OverView - ...?のimportの項にも標準ライブラリ以外のインポートしたい独自ファイルをおくpythonpathという名前のフォルダについての記載があります。

標準ライブラリ以外のファイルインポートはlinuxBean14.04(86)AnacondaのパッケージをLibreOfficeマクロで使うでsite.pthを使ってsys.pathにPythonモジュール検索パスを追加することでできているのですが、oxtファイルの拡張機能のpythonpathがsys.pathに追加されるのかLibreOffice(29)Pythonインタプリタの置き換えは断念のversion_check.pyをwriterのマクロモードで実行してみました。

拡張機能マネージャーを起動するだけで/opt/libreoffice5.0/share/extensions/dict-en/pythonpathがPythonモジュール検索パスに追加されました。

さらにpython-tokencounter-calc-addin.oxtを拡張機能マネージャーに追加してLibreOfficeを再起動すると~/.config/libreoffice/4/user/uno_packages/cache/uno_packages/lu181794jwpac.tmp_/python-tokencounter-calc-addin.oxt/pythonpathがPythonモジュール検索パスに追加されました。(前述の通りパスが追加されるのは拡張機能を実行したあとのみです。マクロモードのみでパスは追加されオートメーションでは追加されませんでした。)

このパスはLibreOffice5(9)oxtファイルによる拡張機能の更新の反映はLibreOfficeの再起動が必要でみたように拡張機能マネージャーに登録された拡張機能のキャッシュフォルダです。

キャッシュフォルダは拡張機能を登録するごとに固有のフォルダ名をもっているので拡張機能同士で名前の衝突は起こらないような気もしますね。

pythopathフォルダを使用しない拡張機能に作り変える


python-tokencounter-calc-addin.oxtはpythonpathフォルダを使って複数のpyファイルを使う例ですが、tokencounter.pyしか使っていないのでpythonpathを使わないように作り変えてみます。
pq@pq-VirtualBox:~/ダウンロード/without_pythonpath/python-tokencounter-calc-addi
n$ tree
.
├── META-INF
│   └── manifest.xml
├── description.xml
├── rdb
│   └── sample.rdb
└── tokencounter.py
必要なファイルが4つだけになりました。

tokencounter.pyは最後に次の2行を付け加えました。
g_ImplementationHelper = unohelper.ImplementationHelper()
g_ImplementationHelper.addImplementation(TokenCounter,"org.openoffice.comp.addin.sample.python.TokenCounter", ("com.sun.star.sheet.AddIn",),)
reg.uno.pyの関数createInstance()に替えてtokencounter.pyのTokenCounterクラスをaddImplementation()メソッドの第一引数にしています。

拡張機能の登録時にreg.uno.pyに代わってtokencounter.pyが処理されるようにmanifest.xmlを書き換えました。
<manifest:manifest>
<manifest:file-entry manifest:media-type="application/vnd.sun.star.uno-typelibrary;type=RDB"
                    manifest:full-path="rdb/sample.rdb"/>
<manifest:file-entry manifest:media-type="application/vnd.sun.star.uno-component;type=Python"
                    manifest:full-path="tokencounter.py"/>
</manifest:manifest>
これで動きました。

tokencouter.pyの内容については次にみてみることにします。

参考にしたサイト


Python-UNO bridge
python-tokencounter-calc-addin.oxt の例を実行してみました。

LibreOffice: Main Page
LibreOffice APIリファレンス。

Apache OpenOffice Developer's Guide - Apache OpenOffice Wiki
LibreOfficeのデベロッパーガイドでもあります。

6. モジュール — Python 3.3.6 ドキュメント
__init__.pyファイルについての解説。

OpenOffice.org 拡張機能 その2 - N->N->N
拡張機能のoxtファイルに含めるMETA-INF/manifest.xmlファイルとdescription.xmlファイルの解説。

Extensions/description - ...?
description.xmlファイルの解説。

Example - Apache OpenOffice Wiki
デベロッパーガイドにあるdescription.xmlの例。

ハイ コントラストを有効にし、設定を変更する
Windowsでハイコントラストモードに変更する方法。

次の関連記事:LibreOffice5(11)unoinsp.py:自作IDLへの対応

PR

0 件のコメント:

コメントを投稿