LibreOffice(40)埋め込みマクロ2:値の引渡しと受け取り

2014-04-10

旧ブログ

t f B! P L

前の関連記事:LibreOffice(39)7-ZipのGUIで楽にPythonマクロをドキュメントに埋め込む


LibreOffice(38)埋め込みマクロ1:セキュリティレベルを変更して開くの続きです。今度はそのドキュメントに埋め込んだマクロを実行させて値をやりとりします。

開いたドキュメントのマクロをスクリプトプロバイダで実行する


OOoBasic/Generic/execute - ...?を参考にスクリプトプロバイダを使ってドキュメントに埋め込んだPythonマクロを外部から実行します。
import unohelper
from com.sun.star.beans import PropertyValue
from com.sun.star.document.MacroExecMode import ALWAYS_EXECUTE_NO_WARN as uno_mem #マクロを警告なしで実行するモードを指定するUNOの定数を変数uno_memにインポート。
def file_open():
    f_url = unohelper.systemPathToFileUrl("C:\_LibreOffice_test\Py-macroEmbedded.ods") #マクロを埋め込んだドキュメントのURLを取得。
    args = [PropertyValue()] #loadComponentFromURL()の引数にするシーケンス型を生成。
    args[0].Name = "MacroExecutionMode"
    args[0].Value = uno_mem #MacroExecutionModeをuno_memに設定。
    doc1 = XSCRIPTCONTEXT.getDesktop().loadComponentFromURL(f_url, "_blank", 0, tuple(args)) #マクロを埋め込んだドキュメントを警告なしでマクロを実行するモードでdoc1に取得。
    script_uri = "vnd.sun.star.script:calc_overview.py$calc_overview?language=Python&location=document" #ドキュメント内のマクロのURIを取得。
    s = doc1.getScriptProvider().getScript(script_uri) #doc1のスクリプトプロバイダからマクロを取得。
    s.invoke((), (), ()) #マクロを実行。
これを実行するとC:\_LibreOffice_test\Py-macroEmbedded.odsが開いてそれに埋め込んであるcalc_overview.pyファイルにある関数calc_overviewが実行されます。

Py-macroEmbedded.odsLibreOffice(31)Pythonマクロをドキュメントファイルに埋め込むで作ったドキュメントです。

9行目で得たドキュメントオブジェクトdoc1から11行目でgetScriptProvider()でスクリプトプロバイダを呼び出しています。

getScriptProvider()com.sun.star.script.provider.XScriptProviderSupplierインターフェイスのメソッドです。

どうしてスクリプトプロバイダをドキュメントオブジェクトから呼び出せるのかAPIリファレンスやデベロッパーガイドを読んでもよくわかりませんでした。

(2014.4.13追記。9行目の次にprint(doc1)を挿入してみるとdoc1オブジェクトの型と備えているサービスとインターフェイスがみれることを発見しました。Basicのdbg_propertiesみたいなのがprint(UNOオブジェクト)でできるようです。そこにcom.sun.star.script.provider.XScriptProviderSupplierを発見しました。)

ともあれサービスプロバイダcon.sun.star.script.provider.ScriptProviderサービスのインスタンスが手に入るとcon.sun.star.script.provider.XScriptProviderインターフェイスのgetScript()メソッドで、スクリプトのURIを指定することによりcom.sun.star.script.provider.XScriptインターフェイスを備えたUNOのオブジェクトが得られます。

それを11行目で変数sに代入しています。

スクリプトのURIは10行目で設定しています。
script_uri = "vnd.sun.star.script:calc_overview.py$calc_overview?language=Python&location=document"
ドキュメント内にあるPythonマクロのcalc_overview.pyファイルの関数calc_overviewを指定しています。

(2016.8.5追記。このURLの先頭にあるvndの意味が何か長くわからなかったのですが、MIMEタイプとは|Content-Type|コンテンツタイプ|MIME type - 意味/定義 : IT用語辞典を読んでベンダー(vendor)とわかりました。)

OOoBasic/Generic/ScriptingURL - ...?How the Scripting Framework worksに書き方の例があります。

あとはマクロを実行したいところでcom.sun.star.script.provider.XScriptインターフェイスのinvoke()メソッドを実行するとそのオブジェクトのマクロが実行されます。

How the Scripting Framework worksに解説がありますが、図にはPythonのスクリプトプロバイダだけ表示はなく、APIリファレンスにもありません。

Pythonのスクリプトプロバイダの実装はC:\Program Files (x86)\LibreOffice 4\program\pythonscript.pyになるそうです。(A. Python-UNO - N->N->N)

invoke()メソッドの第1引数のタプルで値を渡して、タプルで値を受け取る


12行目のcom.sun.star.script.provider.XScriptインターフェイスのinvoke()メソッドはUNOのシーケンス型の3つの引数をもちます。
any invoke ( [in]  sequence< any >    aParams,
             [out] sequence< short >  aOutParamIndex,
             [out] sequence< any >    aOutParam
           ) 
APIリファレンスにはこれらの引数の使い方の例まで記載されています。

似たような例がPython-UNO bridgeのOut parameter handlingにもあります。

しかし、このような形式で値をやりとりするのはUNOコンポーネントが相手のときのようです。

LibreOffice(5)PythonでLibreOfficeが動く仕組み:UNOでみた3つ目のモードです。

UNOコンポーネントとのやりとりはUNO IDLを通すのでPythonマクロと値をやりとりをする場合と違います。

これがなかなか理解できずにうまくいくまで四苦八苦しました。

結局Pythonマクロに値を渡すときはinvoke()メソッドの第1引数のタプルの要素で渡せました。

Pythonマクロからの戻り値はinvoke()メソッドの戻り値から得られます。

invoke()メソッドの戻り値はタプルになっておりその要素番号0の要素にPythonマクロからの戻り値がタプルで入っています。

まずはPy-macroEmbedded.odsに以下のEmbedded.pyをLibreOffice(39)7-ZipのGUIで楽にPythonマクロをドキュメントに埋め込むの方法で埋め込みます。
#Embedded.py #pyファイル名
def calc_embedded(*args): #受け取ったタプルをアンパックしてargsに入れる。argsはタプルになる。
    doc = XSCRIPTCONTEXT.getDocument()
    doc.getSheets().getCellByPosition(0, 0, 0).setString(args[0]) #番号0のシートのセルA1にタプルargsの要素番号0の要素を入力。
    return "実行完了", #値をタプルで返す。
2行目の「*args」で渡されたタプルをアンパックして値を受け取っています。(4.7.4. 引数リストのアンパック)

アンパックせずに「args」でタプルを受け取った場合、タプルの要素が一つの場合はargsに値が代入されます。

しかし、渡されたタプルに要素が2つ以上あるときは、1個しか値が受け取れない、というエラーがでてきます。

アンパックしないときはタプルのまま受け取ってくれればよいのにと思うのですがそうはいかないようです。
#Embedded.py
def calc_embedded(*args):
    doc = XSCRIPTCONTEXT.getDocument()
    doc.getSheets().getCellByPosition(0, 0, 0).setString(str(args))
    return "実行完了",
4行目でstr(args)するとargsの内容がセルA1に入力されます。

値が1個であっても何個であっても「('りんご',)」のようにargsにはタプルが入っていることがわかります。

受け取るタプルの要素が1個のときに限り、アンパックせずにargsを受け取ったときは「りんご」になります。

要素が2個以上ののときはアンパックして受け取らないとエラーになります。

ということでタプルをアンパックしてタプルとして受け取る方法が汎用的な方法となります。

値を返すときも5行目のようにタプルで返します。

値を渡すほうのマクロは以下になります。
#file_open.py #pyファイル名
import unohelper
from com.sun.star.beans import PropertyValue
from com.sun.star.document.MacroExecMode import ALWAYS_EXECUTE_NO_WARN as uno_mem
def file_open():
    doc = XSCRIPTCONTEXT.getDocument() #このマクロを実行したドキュメントをdocに取得。
    f_url = unohelper.systemPathToFileUrl("C:\_LibreOffice_test\Py-macroEmbedded.ods") #マクロを埋め込んだPy-macroEmbedded.odsのURLを取得。
    args = [PropertyValue()]
    args[0].Name = "MacroExecutionMode"
    args[0].Value = uno_mem
    doc1 = XSCRIPTCONTEXT.getDesktop().loadComponentFromURL(f_url, "_blank", 0, tuple(args)) #警告なしでマクロを実行するモードでPy-macroEmbedded.odsを開いてdoc1に取得。
    script_uri = "vnd.sun.star.script:Embedded.py$calc_embedded?language=Python&location=document" #Embedded.pyにある関数calc_embeddedのドキュメント内のURIを取得。
    s = doc1.getScriptProvider().getScript(script_uri) #doc1に埋め込んであるcalc_embeddedを取得。
    r = s.invoke(("りんご",), (), ()) #値"りんご"をinvoke()メソッドの第1引数のタプルで渡して戻り値を変数rに取得。
    doc.getSheets().getCellByPosition(0, 0, 0).setString(r[0][0]) #戻り値のタプルrの要素番号0の要素番号0の要素をdocの番号0のシートのセルA1に入力。
このマクロfile_openをCalcで実行すると、C:\_LibreOffice_test\Py-macroEmbedded.odsが開いてセルA1に「りんご」と入力され、マクロfile_openを呼び出したシートではセル「A1」に「実行完了」と入力されているはずです。

14行目で値を渡して15行目で値を受け取っています。

invoke()メソッドの引数はタプルと決まっているので渡す値が一つであってもタプルにして渡さないといけません。

invoke()メソッドの戻り値はrに代入しています。

print(r)を挿入してrの中身を見てみると以下のようなタプルになっています。

(('実行完了',), <ByteSequence instance 'b'''>, ())

戻り値がタプルの0番目の要素のタプルとして返ってきていることがわかります。

ということで戻り値「'実行完了'」はr[0][0]で取り出します。

戻り値のタプルの1番目と2番目の要素を使うことはなさそうなので以下のように14行目でrには0番目の要素だけを入れるようにするとr[0]で戻り値を取り出せます。
#file_open.py #pyファイル名
import unohelper
from com.sun.star.beans import PropertyValue
from com.sun.star.document.MacroExecMode import ALWAYS_EXECUTE_NO_WARN as uno_mem
def file_open():
    doc = XSCRIPTCONTEXT.getDocument()
    f_url = unohelper.systemPathToFileUrl("C:\_LibreOffice_test\Py-macroEmbedded.ods")
    args = [PropertyValue()]
    args[0].Name = "MacroExecutionMode"
    args[0].Value = uno_mem
    doc1 = XSCRIPTCONTEXT.getDesktop().loadComponentFromURL(f_url, "_blank", 0, tuple(args))
    script_uri = "vnd.sun.star.script:Embedded.py$calc_embedded?language=Python&location=document"
    s = doc1.getScriptProvider().getScript(script_uri)
    r = s.invoke(("りんご",), (), ())[0]
    doc.getSheets().getCellByPosition(0, 1, 0).setString(r[0])

参考にしたサイト


OOoBasic/Generic/execute - ...?
ドキュメント内のマクロをVB Scriptから実行する例とセキュリティレベルの設定方法もあります。

LibreOffice: Namespace List
LibreOffice 4.2 SDK APIの入り口com.sun.star。

UNO の関連ツール - Apache OpenOffice Wiki
Pythonでもprint(UNOオブジェクト)でオブジェクトの属性がみれました。

OOoBasic/Generic/ScriptingURL - ...?
マクロのURI(URL)設定方法。

Scripting Framework URI Specification - Apache OpenOffice Wiki
デベロッパーガイドのマクロのURIの指定方法の解説。

How the Scripting Framework works - Apache OpenOffice Wiki
スクリプトプロバイダの働きの図。日本語のデベロッパーガイドには記載がありません。

A. Python-UNO - N->N->N
Pythonのスクリプトプロバイダの実装はC:\Program Files (x86)\LibreOffice 4\program\pythonscript.pyになるそうです。

Python-UNO bridge
OpenOffice.orgでPythonを使うための解説。

OOoBasic/Generic/invokeScript - ...?
Python以外の値の受け渡しの解説も載っています。

Python チュートリアル — Python 3.3.3 ドキュメント
4.7.4. 引数リストのアンパックを使いました。

Return Values - Apache OpenOffice Wiki
UNO IDLの戻り値の解説。

次の関連記事:LibreOffice(41)埋め込みマクロ3:呼び出し元に作用させる

ブログ検索 by Blogger

Translate

最近のコメント

Created by Calendar Gadget

QooQ