LibreOffice5(44)createInstanceWithArgumentsAndContext()でインスタンス化

2017-05-25

旧ブログ

t f B! P L
LibreOffice5(23)ダイアログエディタでGUIを作成しPythonで利用する:その3でcreateInstanceWithArgumentsAndContext()について学習しましたが、実際にこのメソッドでインスタンス化できるPyUNOオブジェクトを実装します。

前の関連記事:LibreOffice5(43)UNOIDLの引数の型の例を増やす


createInstanceWithArgumentsAndContext()にXInitializationの実装は不要


The FactoryHelper automatically passes the array of arguments it received from the createInstanceWithArguments[AndContext]() call to the appropriate constructor. Therefore, it is not always necessary to implement XInitialization to use arguments.
Create Instance with Arguments - Apache OpenOffice Wiki
必ずしも実装する必要はないとは書いてありますが、じゃあどうすのか。

OOoPython/ComplexToolbar - ...?の例では、プロトコールハンドラからXInitializationのinitialize()メソッドにフレームが渡されています。

でもこれは特殊なことのようです。

A. Python-UNO - N->N->Nの「コンポーネント」の項目を読むと、createInstanceWithArgumentsAndContext()の引数は__init__()に渡されると書いてあり、そこにある例では__init__()からinitialize()を呼び出しています。

createInstanceWithArgumentsAndContext()の引数は__initi__()に渡されるとのことです。

LibreOffice5(26)MRI - UNO Object Inspection Tool:その2をみると、MRIもcreateInstanceWithArgumentsAndContext()でインスタンス化しています。

で、MRI-1.3.3.oxtの中身をxinitializで検索してみましたが、何も引っかかってきませんでした。

MRI/Mri.idlで定義しているcreate()が何か関係していると思っていましたが、よくわかりませんでした。

inspect( [in] any target )はXIntrospectionのメソッドをオーバーライドしていますが、create()はUNOIDLで定義がないはずです。

このcreate()はMRI/MRI.py at master · hanya/MRIのcreate()と関係があるのかもしれません。

UNOIDLtypeプロジェクトの例をcreateInstanceWithArgumentsAndContext()でインスタンス化できるようにする


UNOIDLtype/component.py at 38c5a909aface53578090e9f183850419fc5fbf9 · p--q/UNOIDLtype

g_ImplementationHelper.addImplementation()の第一引数で渡すクラスの__init__()でタプルを受けるようにするだけで、あっけなくcreateInstanceWithArgumentsAndContext()の引数を受け取るようにできました。

LibreOffice: XMultiComponentFactory Interface Referenceに書いてあるように createInstanceWithArgumentsAndContext()で渡せるのは sequence< any > だけですのでタプルで渡す必要があります。

タプルで渡していない時はconversion not possible!というエラーになります。

 def __init__(self, ctx, args):というふうに第2引数をアスタリスクで展開していないとcreateInstanceWithContext()でインスタンス化するときに引数が足りないと言われてます。

createInstanceWithContext()でインスタンス化したときのdef __init__(self, ctx, *args):のargsには空のタプルが入っていました。

oxtファイル内のPythonスクリプトをpythonpathフォルダに入れる


どうも使いにくいのでoxtファイルを作るためのtoolsフォルダ内のPythonスクリプトを刷新しました。

toolsフォルダのスクリプトでなるべく設定をしなくてよいように、サービス名などをsrcフォルダのpyファイルから取得するようにしました。

oxtファイルに入れるpyファイルの構成を簡単にするために、MRI
と同様にoxtファイル直下(PyDevプロジェクトのsrcフォルダ直下)のpyファイルを一つにしてそこからpythonpathフォルダにあるpyファイルのクラスを呼び出してg_ImplementationHelperに渡すようにしました。

これによって、srcフォルダ直下のpyファイルの内容は実装サービス名とサービス名を設定するだけで済むようになりました。

UNOIDLtype/PyUnoComponent.py at 4926649b092a8d2f06e86e9a26def643f51463ae · p--q/UNOIDLtype

component.pyでglobalにしているところはnonlocalでいけると思ったのですが、nonlocalはグローバルスコープだけはみれずglobalでないといけませんでした。(7. 単純文 (simple statement) — Python 3.5.3 ドキュメント

テキストファイルとして読み込んだPythonコードから値を取得する


このPyUnoComponent.pyのIMPLE_NAMEとSERVICE_NAMEの値をtoolsのスクリプトから取得するようにするにちょっと手間取りました。

まず単純にこのPyUnoComponent.pyをimportして取得しようと思いましたが、これはPyUnoComponent.pyでimportしているpythonpathフォルダのモジュールが読み込みエラーがでてうまくいきませんでした。

ということで、発想を変えてpyファイルをテキストファイルとして読み込んで、必要な行をexec()で実行して変数の値を取得することにしました。

まずコンソールで動作確認しました。
>>> exec('IMPLE_NAME = "UnoInsp"')
>>> IMPLE_NAME
'UnoInsp'
テキストファイルから取得したコードをexec()で評価して変数IMPLE_NAMEの値を取得しています。
>>> def getVal():
...     exec('IMPLE_NAME = "UnoINsp"')
...     print(IMPLE_NAME)
... 
>>> getVal()
UnoInsp
これも問題なく動きます。

それで、今度はこれをpyファイルに書いて実行しました。
def getVal():
    exec('IMPLE_NAME = "UnoINsp"')
    print(IMPLE_NAME)
getVal()
NameError: name 'IMPLE_NAME' is not defined

なぜかIMPLE_NAMEの値が取得できません。

どうも納得いきませんが、時間城年代記:exec関数<詳細版>を読むと名前空間の問題とわかりました。

コンソールでは動くのにファイルに書くとうまくいかないのはなぜかよくわかりませんけど。
exec('IMPLE_NAME = "UnoINsp"')
print(IMPLE_NAME)
ファイルに書いても関数に入れなければ動きます。
def getVal():
    d = dict()
    exec('IMPLE_NAME = "UnoINsp"', d)
    print(d["IMPLE_NAME"])
getVal()
時間城年代記:exec関数<詳細版>で紹介されているexec()の第二引数に渡した辞書で値を受け取る方法を使うと関数でもうまく値を取得できました。

toolsを刷新


UNOIDLtype/UNOIDLtype/tools at 8cf49360aae474a72cd7062c3ed6eb73fc2a00f3 · p--q/UNOIDLtype

上記の設定を反映させてpyファイル名も変更しました。

settings.pyでsrcフォルダにあるpyファイルから実装サービス名とサービス名を取得するので、settings.pyの編集は不要になりました。

execAtOnce.pyを実行するとidlファイルのコンパイルからoxtファイルのLibreOfficeへのデプロイまで一括してやります。

idlファイルの作成もスクリプトで作成するようにしました。

createIDLs.pyのdefineIDLs()内でUNOIDLの定義をします。

継承しているUNOIDLのidlファイルは自動的にincludeするようにしました。

複数のPython UNO Componentファイルにも対応しました。

それに伴って、oxtファイル、rdbファイル、componentファイルはPyDevプロジェクト名から取得するようにしました。

まだ使いこなしていないので汎用性はないと思います。

参考にしたサイト


Create Instance with Arguments - Apache OpenOffice Wiki
createInstanceWithArgumentsAndContext()の引数はXInitializationを実装しなくてもタプルで受け取れました。

OOoPython/ComplexToolbar - ...?
XInitializationのinitialize()メソッドで受け取らないといけないものもあります。

A. Python-UNO - N->N->N
「コンポーネント」の項目を読むとcreateInstanceWithArgumentsAndContext()の引数は__init__()に渡されるとあります。

hanya/MRI: An object introspection tool for OpenOffice API
Pythonによる拡張機能の例。

LibreOffice: XMultiComponentFactory Interface Reference
createInstanceWithArgumentsAndContext()の引数はタプルでしか受け取れません。

7. 単純文 (simple statement) — Python 3.5.3 ドキュメント
nonlocalではグローバルスコープの変数は操作できません。

時間城年代記:exec関数<詳細版>
exec()で代入した値は引数の辞書で受け取る必要があります。

次の関連記事:LibreOffice5(45)拡張機能内で発生した例外の出力先

ブログ検索 by Blogger

Translate

最近のコメント

Created by Calendar Gadget

QooQ