LibreOffice5(47)拡張機能のソースをオートメーションでも実行する

拡張機能のソースを毎回oxtファイルにして動作確認するのは面倒なので、LibreOffice(5)PythonでLibreOfficeが動く仕組み:UNOの3つのモードのすべてで実行できるようにします。結論としてはモジュールのimportに制限が多いマクロモードでの実行はあきらめてオートメーションのみの対応になりました。

前の関連記事:LibreOffice5(46)マクロからウェブブラウザに文字列を出力する方法


UNOコンポーネントモードで実行する


GitHub - p--q/toWebHtmlexecution_example.pyは2つのモードで実行できるようになっています。

UNOコンポーネントモードで実行するときは24行目のMODE = "Automation"をコメントアウトしてexecution_example.pyを実行します。

UNOコンポーネントモードで実行するというのはつまりsrcフォルダの内容は使わずに作成してLibreOfficeにデプロイしたoxtファイルを使うということです。

26行目でhelpers/connectoffice.pyのconnectOffice()を呼び出しています。

conncetOffice()は@contextlib.contextmanagerデコレーターでコンテキストマネージャとして使えるようになっています。

conncetOffice()はofficehelper.pyでLibreOfficeを起動してbootsrap()でコンポーネントコンテクストを取得します。

サービスマネジャーも取得して引数の関数の引数に渡してそれをyieldしています。

with文が終了するとバックグラウンドでWriterのドキュメントを開いたのちLibreofficeを終了しています。

この終了処理はLibreOffice5(37)bootstrap()で起動したLibreOfficeをエラーなく終了させるの結果を反映させたものです。

このモードでは特殊なことはせず、普通にサービスマネジャーを使っているだけです。

オートメーションモードで実行する


execution_example.pyの24行目のコメントアウトを外してMODE = "Automation"を有効にするとオートメーションで実行します。

オートメーションで実行するということはoxtファイルは使わずにsrcフォルダ内のpythonpathフォルダ内にあるソースを使います。

conncetOffice()は引数のMODEが"Automation"のとき57行目でサービスマネージャーに代わって10行目のAutomationのインスタンスを使います。

Automationのインスタンスはexecution_example.pyで渡された引数のUNOComposからキーをサービス名または実装サービス名、値をクラス名の辞書を作成します。

この辞書のキーにあるサービス名をインスタンス化しようとするとそれに代わって値のクラスのインスタンスを返します。

これによってUNOコンポーネントとしてUNOIDLを通してインスタンス化するはずのクラスをUNOIDLを通さずにインスタンス化して使っています。

マクロモードで実行するのは断念


オートメーションと同様にマクロからsrc/pythonpath/restにあるソースを実行させようとしましたが、いくつも問題があって断念しました。

execution_example.pyにあるmacro()はマクロから拡張機能の機能を呼び出しているだけなのでUNOコンポーネントモードで動いています。

マクロモードの中からPythonのprint()の出力をウェブブラウザにできるようにPython: replaceFuncをデバッガに対応させるLibreOffice5(46)マクロからウェブブラウザに文字列を出力する方法をやったのですけど、マクロではimportに制限が多くて、ソースをいじらずに実行させることは無理とわかりました。

OOoPython/OverView - ...?

ここのimportに解説があるように、マクロではマクロとして呼び出したモジュールと同じフォルダにあるpythonpathという名前のフォルダに置いたモジュールしかimport出来ません。
        script_uri = "toWebHtml|toWebHtml|macro.py$macro"
        script_uri = "vnd.sun.star.script:" + script_uri + "?language=Python&location=user"
        mspf = ctx.getValueByName("/singletons/com.sun.star.script.provider.theMasterScriptProviderFactory")
        sp = mspf.createScriptProvider("")
        script = sp.getScript(script_uri)
        script.invoke((), (), ())
Pythonスクリプトからマクロの呼び出しはこのようにしてできました。

1行目でマクロとして呼び出す関数があるモジュールへのパスを指定しています。

パスの起点はマイマクロフォルダです。

LibreOffice(41)埋め込みマクロ3:呼び出し元に作用させるでやったときはcreateScriptProvider()の引数をなしにしていましたが、今回は空文字でも入れないとエラーがでました。

マクロで呼び出すときとオートメーションから呼ぶ時では条件が違うのかもしれません。

すでに使用しているPythonスクリプトに置いたマクロを呼び出すことはできませんでした。

拡張機能の中でのモジュールのインポートのルールはマクロの場合と同じ


sys.path以外にモジュールをインポートする場合、最初に実行したモジュールより上の階層にはアクセスできません。

「より上の階層」にはアクセスできないので親フォルダにもアクセスできません。

最初に実行したモジュールからは相対インポートできません。

モジュールを入れたフォルダには必ず__init__.pyを置かないといけないと思っていましたが、なくてもとくに支障はでませんでした。

Python Cookbookの10.1.Making a Hierarchical Package of Modulesの解説を読むとPython3.3以降は__init__.pyを置かない場合は共通の「名前空間パッケージ」として解釈されてインポートできるとのことです。

拡張機能の中のモジュールの相対インポートについては、最初に実行されるモジュールはoxtファイルの最上層にあるg_ImplementationHelperを定義しているモジュールです。

そこからインポートできるモジュールはマクロの時と同様に最初に実行されるモジュールと同じ階層にあるpythonpathフォルダにあるものです。

このpythonpathフォルダの中の相対インポートでアクセスできるのはpythonpathより下の階層のみです。

なので、pythonpathフォルダ自身にはアクセスできないので、その直下の階層では相対インポートではなく絶対インポートにしないといけません。

ということでpythonpath直下にはモジュールは置かず、サブフォルダを作ってそこにモジュールを置いています。

LibreOffice5(43)UNOIDLの引数の型の例を増やすで参考にした拡張機能のMRIとかEasyDevもいずれのpythonpathフォルダも同様にサブフォルダにスクリプトを置く構造になっています。

参考にしたサイト


OOoPython/OverView - ...?
マクロとして呼び出した関数があるモジュールからインポートできるモジュールは同じ階層のpythonpathという名前のフォルダ内のモジュールのみです。

次の関連記事:LibreOffice5(48)Javaの例:ConfigExamplesをPythonにする その1

PR

0 件のコメント:

コメントを投稿