LibreOffice5(6)既存インターフェイスを継承してPythonスクリプトをUNOコンポーネント化する例:その1

2015-11-29

旧ブログ

t f B! P L

前の関連記事:LibreOffice5(5)unoinsp.pyでAPIリファレンスへのリンクを付ける


LibreOffice(65)Writing UNO componentsのThumbs Exampleその1でJavaの例をやったのでPythonで同様にやればできると思ったのですが、rdbファイルを作ったあとどうするのかがよくわかりませんでした。Implementing UNO objectsを読んでもよくわからずとりあえず例をみつけてやってみます。

既存インターフェイスを継承したクラスでメソッドを実装するだけならIDLの定義は不要


Implementing Python UNO components

このtuplestrm.pyはcom.sun.star.io.XOutputStreamインターフェイスを継承したクラスTupleOutputStreamを定義しそれをorg.openoffice.pyuno.PythonOutputStreamという実装名、com.sun.star.io.OutputStreamというサービス名でLibreOfficeに登録する例です。

既存のインターフェイスを継承する長所としてはIDLを定義しなくても実装がかけるという点があります。

短所としてはIDL定義していないので既存のインターフェイスにあるメソッドにしか実装できない点です。

UNOコンポーネントから呼び出せないメソッドも書けますがこれはPythonの世界からの呼び出しに限られます。

tuplestrm.pyではgetTuple()メソッドがこれに該当します。

実装名org.openoffice.pyuno.PythonOutputStreamはドットで結んだ階層構造になっていますがLibreOffice(65)Writing UNO componentsのThumbs Exampleその1でやったJavaの例のように実際のフォルダ構成を再現しておく必要はなく、名前も一意であれば任意に決めてよいようでorg.libreoffice.pyuno.PythonOutputStreamやPythonOutputStreamという名前にしても問題ありませんでした。

またcom.sun.star.io.OutputStreamというサービス名も好きに決めてよいようです。

新しいサービス名ならIDL定義が必要なような気もするのですが、com.sun.star.io.OutputStream2と変更するとcom.sun.star.io.OutputStream2でインスタンス化できました。

OOoPython/UNOComponent - ...?には実装名でもインスタンス化できると書いてあって、実際やってみるとその通りでした。
g_ImplementationHelper = unohelper.ImplementationHelper()
g_ImplementationHelper.addImplementation(TupleOutputStream, "org.openoffice.pyuno.PythonOutputStream", ("com.sun.star.io.OutputStream",), ) 
この部分はメソッドチェーンで一行にしてしまいたいと思ったのですが無理でした。
unohelper.ImplementationHelper().addImplementation(TupleOutputStream, "org.openoffice.pyuno.PythonOutputStream", ("com.sun.star.io.OutputStream",), ) 
これでは拡張機能として登録できませんでした。

拡張機能マネージャーには表示されませんがデプロイはされているようで、g_ImplementationHelperを正しく設定したtuplestrm.pyを追加しようとすると置き換えるかどうか聞かれました。

g_ImplementationHelperという変数名は必須だそうです。(UNO コンポーネント作成

tuplestrm.pyを拡張機能マネージャーに登録する


うーん、tuplestrm.pyをPYTHONPATHにあるパスに入れろと書いていますが、まず「PYTHONPATH」が何なのかわかりません。

python - PYTHONPATHとは - スタック・オーバーフロー

わかりやすい解説をみつけました。スタックオーバーフローって日本語版もあったのですね。

環境変数のPYTHONPATHにパスを書いておくとsys.pathにパスを追加してくれるというものだそうです。

私の環境ではTerminalでecho $PYTHONPATHとしても何も設定されていませんでした。

sys.path自体はlinuxBean14.04(86)AnacondaのパッケージをLibreOfficeマクロで使うでpthファイルを使ってAnacondaのパスを大量に追加しています。

/opt/libreoffice5.0/program/pythonでpythonインタープリタを起動してimport sysとしてsys.pathを確認してみるとたくさんのパスがでてきます。

tuplestrm.pyはsys.pathの中にあればよいのか、PYTHONPATHに設定しておかないといけないのかがわかりませんがとりあえずlinuxBean14.04(6)LibreOffice4.3.7とPyCharmの設定で作ったマイマクロのパスがsys.pathに入っていたのでそこにtuplestrm.pyを置いてみます。

それでpkgchk tuplestrm.pyでデプロイするように書いてありますがpkgchkはすでになくいまは代わりにunopkg addを使うようです。(Re: what is unopkg and pkgchk?)

/opt/libreoffice5.0/program/unopkg add tuplestrm.py

tuplestrm.pyのあるフォルダ、今回は~/.config/libreoffice/4/user/Scripts/python/Python_UNOでこれを実行しました。

なにも出力されることなく次のプロンプトがでてきました。うまくいったようです。


LibreOfficeを起動してメニューのツール→拡張機能マネージャー、で確認するとtuplestrm.pyが登録されていました。

LibreOfficeへのデプロイはうまくいったようです。

拡張機能マネージャーの「追加」ボタンでも同様にデプロイできました。


デフォルトではoxtファイルファイルしか選択できないようになっていますが、ファイル選択ダイアログの右下から「UNO Python Component」を選択すればtuplestrm.pyが選択できました。


一時的に無効にしたいときは拡張機能マネージャーの「無効にする」ボタンで無効にできます。

tuplestrm.pyの動作を確認する


動作確認をしたいのですがtuplestrm.pyを読んでも何をしているものかよくわからずImplementing Python UNO componentsにもBasicでのインスタンス化の方法しか書いていないのでそれをPythonですることにします。
def Tuplestrm_test():
    ctx = XSCRIPTCONTEXT.getComponentContext()
    tupleStrm = ctx.getServiceManager().createInstanceWithContext("com.sun.star.io.OutputStream",ctx)
    import unoinsp
    ins = unoinsp.ObjInsp(XSCRIPTCONTEXT)
    ins.wtree(tupleStrm)
if __name__ == "__main__":
    import unopy
    XSCRIPTCONTEXT = unopy.connect()
    if not XSCRIPTCONTEXT:
        print("Failed to connect.")
        import sys
        sys.exit(0)
    Tuplestrm_test()
tuplestrm_test.pyというファイル名でマイマクロフォルダにこれを作りました。

4,5,6行目はLibreOffice5(5)unoinsp.pyでAPIリファレンスへのリンクを付けるのunoinsp.pyを使っているので導入していなければprint(tupleStrm)でNoneとでてこなければインスタンス化されているかどうかがわかると思います。

7行目以降はオートメーションのためのコードです。

.io.XOutputStream
      void  closeOutput()
      void  flush()
      void  writeBytes( [in] [byte] aData
             ) raises ( .io.IOException,
                        .io.BufferSizeExceededException,
                        .io.NotConnectedException)


com.sun.star.io.OutputStreamがインスタンス化されていればこのような出力がデフォルトウェブブラウザで表示されるはずです。

マクロでもオートメーションでも同じ結果になります。

拡張機能マネージャーでtuplestrm.pyを無効にすると何も出力されませんので、tuplestrm.pyで定義したサービスがインスタンス化されているとわかります。

しかしこれではcom.sun.star.io.XOutputStreamインターフェイスのメソッドがtuplestrm.pyの実装で動くかどうかの確認にはなりません。
# -*- coding: utf-8 -*-
import unohelper
from com.sun.star.io import XOutputStream
class TupleOutputStream(unohelper.Base, XOutputStream):  # com.sun.star.io.XOutputStreamインターフェイスを継承。
    def __init__(self, ctx):  # UNOコンポーネントではXSCRIPTCONTEXTではなくコンポーネントコンテクストを受けとることに注意。
        self.ctx = ctx
    def flush(self):  # XOutputStreamインターフェイスのメソッドを実装。Writerに書き出すテスト。予めWriterを起動しておくことが必要。
        doc = self.ctx.getServiceManager().createInstanceWithContext("com.sun.star.frame.Desktop", self.ctx).getCurrentComponent()
        doc.getText().setString("Hello World!(tuplestrm.pyからの書き込み)")
g_ImplementationHelper = unohelper.ImplementationHelper()  # g_ImplementationHelperという変数名は変更不可かつ必須。
g_ImplementationHelper.addImplementation(TupleOutputStream, "org.openoffice.pyuno.PythonOutputStream", ("com.sun.star.io.OutputStream",), )
tuplestrm.pyをこのように書き換えてメソッドflush()をWriterに文字を書き出すようにしました。

マクロの時と違ってXSCRIPTCONTEXTではなくインスタンス化するときにコンポーネントコンテクストを受けとるのでそれを利用してWriterのドキュメントを取得しています。
def Tuplestrm_test():
    ctx = XSCRIPTCONTEXT.getComponentContext()
    tupleStrm = ctx.getServiceManager().createInstanceWithContext("com.sun.star.io.OutputStream",ctx)
    tupleStrm.flush()
    # import unoinsp
    # ins = unoinsp.ObjInsp(XSCRIPTCONTEXT)
    # ins.tree(tupleStrm)
if __name__ == "__main__":
    import unopy
    XSCRIPTCONTEXT = unopy.connect()
    if not XSCRIPTCONTEXT:
        print("Failed to connect.")
        import sys
        sys.exit(0)
    Tuplestrm_test()
4行目でメソッドflush()を実行しています。


Writerのドキュメントにtuplestrm.pyでコードした通りの結果が出力されました。

unoinspを実行すると最初のtuplestrm.pyと同様に実装していないメソッド一覧も表示されました。

未実装のメソッドは元のXOutputStreamインターフェイスの実装が使われるのかを確認したかったのですが元の実装の使い方がわからないので未確認です。(たぶん使われない。)

com.sun.star.task.XJobExecutorインターフェイスを継承してUNOコンポーネント化している例


例  OOoPython/UNOComponent - ...?

com.sun.star.task.XJobExecutorインターフェイスを継承して実装しています。

サービス名にすでにあるcom.sun.star.task.JobExecutorサービスを使っていますが、元のcom.sun.star.task.JobExecutorサービスとの関係はどうなってしまうのでしょう?

うーん、それはJobExecutorサービスがXJobExecutorインターフェイスをもつということは変わりないので問題なさそうです。

XJobExecutorインターフェイスを実装したものを拡張機能マネージャに複数登録した場合はどうなるのでしょう?、、、またそのうち調べてみよう。

最後にoxtファイルにして拡張機能に登録して使っています。

実装のなかでメッセージボックスを使っており、Calc(2)課題2:選択範囲の行列番号をメッセージボックスに表示でやったようにLibreOffice4.2からメッセージボックスの表示に使うcom.sun.star.awt.XMessageBoxFactoryのcreateMessageBoxの引数が変更になっているから、、、と思ったらそれは使っていませんでしたが、メッセージボックスまわりの処理が難しそうだったので試していません。

trigger()メソッドは文字列の引数を渡せるのでそれによって処理を場合分けしています。

例 UNO component packaging ja - Apache OpenOffice Wiki

これもcom.sun.star.task.XJobExecutorインターフェイスを継承して実装しています。

サービス名にcom.sun.star.task.JobExecutorサービスを使っている点も同じです。

メニューバーへの登録やアイコンの登録方法も載っています。

最後にzipファイルにしてunopkg addで拡張機能マネージャーに登録しています。

LibreOffice(67)Writing UNO componentsのThumbs Exampleその3でやったようにLibreOffice4以降ではuno.pkgとか.zipといった拡張子は使えなくなったのでoxtファイルにする必要があります。

この例も試していません。

com.sun.star.task.XJobExecutorインターフェイスを実装する意味


サービスとして実装される UNO コンポーネントに com.sun.star.task.XJobExecutor インターフェースを実装しておくと、メニューの項目などから簡単にマクロなどのように利用できます。
OOoPython/UNOComponent - ...?

com.sun.star.task.XJobExecutorインターフェイスを継承して実装するとLibreOfficeのなかでメニューなどから呼び出すのに便利というメリットがあるようです。

(2016.1.3追記。trigger()メソッドを実装するのではなく、ジョブをイベント名と関連付けて拡張機能のoxtファイルのJobs.xcuで定義してtrigger()の引数にイベント名を渡してジョブを実行する方法があります。LibreOffice5(19)イベント駆動する拡張機能のJavaの例:AsyncJob.oxt その3参照。)

trigger()メソッドは文字列の引数を渡せるのでそれにメニュー項目名を入れれば、メニュー項目に対応した処理を実行させることができそうです。

上記のUNO component packaging ja - Apache OpenOffice Wikiの例でもメニューへの登録やアイコンの登録方法が載っていました。

デベロッパーガイドではJobs - Apache OpenOffice Wikicom.sun.star.task.XJobExecutorインターフェイスについての記述があります。

Using the vnd.sun.star.jobs: URL Schema - Apache OpenOffice Wikiが一番関係してそうで、LibreOffice 4.5 SDK - Developer's Guide ExamplesのJobs.xcuというのがその例らしいというところまで目星がつきました。

この例はlinuxBean14.04(83)LibreOfiice5.0.2のインストールで一括makeしたときの~/libreoffice5.0_sdk/LINUXexample.out/bin/AsyncJob.oxtに含まれています。

拡張機能マネージャーに登録したところで何かが動くという例でもないようです。

またそのうち中身をみてみよう、、、

OOobbs3/60 - ...?この例も参考になりそうです。

参考にしたサイト


Python-UNO bridge
LibreOfficeでPythonを使うときにまず最初に読むマニュアル。

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

OOoPython/UNOComponent - ...?
com.sun.star.task.XJobExecutorインターフェイスを実装する意味についても解説されています。

python - PYTHONPATHとは - スタック・オーバーフロー
PYTHONPATHについての解説。

Re: what is unopkg and pkgchk?
Python-UNO bridgeに載っているpkgchkはunopkgに変わりました。

UNO component packaging ja - Apache OpenOffice Wiki
Python UNOコンポーネントのメニューへの登録やアイコンの登録方法が載っていました。

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

LibreOffice 5.0 SDK - Development Tools
LibreOffice5.0の開発ツールの解説。

次の関連記事:LibreOffice5(7)既存インターフェイスを継承してPythonスクリプトをUNOコンポーネント化する例:その2

ブログ検索 by Blogger

Translate

最近のコメント

Created by Calendar Gadget

QooQ