LibreOffice(49)オブジェクトからサービスとインターフェイスの継承関係辞書を生成

ラベル: ,

前の関連記事:LibreOffice(48)TypeDescriptionManagerシングルトンgetByHierarchicalName()メソッドの戻り値


A inherited from B(A→B)でBからAにたどっていく方法がわからなかったので、AからBにたどっていくことにします。

オブジェクトから継承しているサービス名とインターフェイス名を1階層ずつ辞書に取得


取得した継承関係をどういうデータ形式に収納したらよいのか悩みましたが、あれこれ検討下結果、辞書で1階層ずつ収納することにしました。

A→Bの継承関係をkeyにA、valueにBを入れた辞書を作っていきます。

つまり{子:(親のタプル)}という辞書を作ります。

この方法ですと階層の一番上を知っていないときにそれを探すのが大変そうですがとりあえず先にコードができあがったちゃったので記録しておきます。
def libreoffice49():
    obj = XSCRIPTCONTEXT.getDocument()  # 調査対象のオブジェクトを取得。
    tdm = XSCRIPTCONTEXT.getComponentContext().getByName('/singletons/com.sun.star.reflection.theTypeDescriptionManager')  # TypeDescriptionManagerをシングルトンでインスタンス化。
    dic = dict()  # サービス名・インターフェイス名とそれを継承しているサービス・インターフェイスのTypeDescriptionオブジェクトとの対応を入れる辞書を生成。key→value.
    if hasattr(obj, "getSupportedServiceNames"):  # hasattr()は属性の有無をチェックするPythonの組み込み関数。
        for service in obj.getSupportedServiceNames():  # 各サービス名について。
            std = tdm.getByHierarchicalName(service)  # サービス名からXServiceTypeDescription2インターフェイスをもつのTypeDescription型オブジェクトを取得。
            if std.isSingleInterfaceBased():  # new-style(single-interface–based)サービスのとき
                t_itd = std.getInterface(),  # そのサービス名がもっているインターフェイスのTypeDescriptionオブジェクトをタプルにして取得。
                dic[service] = t_itd  # サービスとインターフェイスの継承関係をdicに取得。
                u = get_interfaces(t_itd, dic)  # インターフェイスのTypeDescriptionオブジェクトのタプルから、インターフェイスとそれが継承しているインターフェイスのタプルをdicに得る。
                while u:  # dicに新たに追加された値がある間は実行。
                    for t_itd in u:  # 新たに追加されたTypeDescriptionオブジェクトのタプルについて。
                        u = get_interfaces(t_itd, dic)
            else:  # old-style(accumulation-based)サービスのとき
                t_std = std.getMandatoryServices()+std.getOptionalServices()
                if t_std:  # 継承しているサービスがあるのなら
                    dic[service] = t_std  # 直に継承しているサービスのTypeDescriptionオブジェクトのタプルを取得。
                t_itd = std.getMandatoryInterfaces() + std.getOptionalInterfaces()  # このサービスのインターフェイスを取得。
                if t_itd:  # インターフェイスが存在するとき。
                    dic[service] = t_itd  # サービスとインターフェイスの継承関係をdicに取得。
                    u = get_interfaces(t_itd, dic)  # インターフェイスのTypeDescriptionオブジェクトのタプルから、インターフェイスとそれが継承しているインターフェイスのタプルをdicに得る。
                    while u:  # dicに新たに追加された値がある間は実行。
                        for t_itd in u:  # 新たに追加されたTypeDescriptionオブジェクトのタプルについて。
                            u = get_interfaces(t_itd, dic)
        print("\n".join([str(k) for k in zip(dic.keys(), [[j.Name for j in i] for i in dic.values()])]))  # valueのTypeDescriptionオブジェクトをName属性にしてリストで、keyと組みあわせて出力。
def get_interfaces(t_itd, dic):
    u = list()  # 新たに追加になったdicの値(インターフェイスのTypeDescriptionオブジェクトのタプル)を入れるリスト。
    for interface in t_itd:
        t_itd = interface.getBaseTypes()+interface.getOptionalBaseTypes()
        if "com.sun.star.uno.XInterface" not in [i.Name for i in t_itd]:  # com.sun.star.uno.XInterfaceがあれば次のインターフェイスはもうたどらない。
            dic[interface.Name] = t_itd # インターフェイスが直に継承しているインターフェイスのTypeDescriptionオブジェクトのタプルをdicに取得。
            u.append(dic[interface.Name])  # dicに新たに追加したインターフェイスのTypeDescriptionオブジェクトのタプルをリストuに追加。
    return u
if __name__ == "__main__":
    import unopy
    XSCRIPTCONTEXT = unopy.connect()
    if not XSCRIPTCONTEXT:
        print("Failed to connect.")
        import sys
        sys.exit(0)
    libreoffice49()
old-styleサービスとnew-styleサービスは違うメソッドを持ったオブジェクトが返ってくると思っていたのでold-styleサービスとnew-styleサービスで場合分けしていますがLibreOffice(48)TypeDescriptionManagerシングルトンgetByHierarchicalName()メソッドの戻り値で調べた結果同じメソッドをもったオブジェクトが返ってくるようなので場合分けは不要にできそうです。
        print("\n".join([str(k) for k in zip(dic.keys(), [[j.Name for j in i] for i in dic.values()])]))  # valueのTypeDescriptionオブジェクトをName属性にしてリストで、keyと組みあわせて出力。
26行目で作成した辞書dicを確認するために中身を出力しています。

辞書dicのkeyには継承の親になるサービス名かインターフェイス名が入っています。

ところがvalueにはkeyを継承しているサービスやインターフェイスのTypeDescription型のオブジェクトがタプルで入っているのでそのままprint()で出力しても内容がわかりません。

そこで辞書のvalueのタプルに入っている要素のオブジェクトについてName属性を得たいので、まず辞書のvalueを展開してタプルを得て、そのタプルをさらに展開してName属性のリストを得て、そのリストをkeyのリストと組み合わせたリストを生成して、その各要素のリストを文字列に変換して改行文字でつないで出力しています。

入れ子でリストの内包表記を使っています。

map(function, iterable, ...)を使ってもっとスマートにできないか考えてみましたがちょっと思いつきませんでした。
('com.sun.star.util.XModifiable', ['com.sun.star.util.XModifyBroadcaster'])
('com.sun.star.text.GenericTextDocument', ['com.sun.star.lang.XMultiServiceFactory', 'com.sun.star.text.XTextDocument', 'com.sun.star.util.XSearchable', 'com.sun.star.util.XRefreshable', 'com.sun.star.text.XFootnotesSupplier', 'com.sun.star.text.XEndnotesSupplier', 'com.sun.star.util.XReplaceable', 'com.sun.star.text.XPagePrintable', 'com.sun.star.text.XReferenceMarksSupplier', 'com.sun.star.text.XChapterNumberingSupplier', 'com.sun.star.beans.XPropertySet', 'com.sun.star.text.XTextGraphicObjectsSupplier', 'com.sun.star.text.XTextEmbeddedObjectsSupplier', 'com.sun.star.text.XTextTablesSupplier', 'com.sun.star.style.XStyleFamiliesSupplier', 'com.sun.star.text.XBookmarksSupplier', 'com.sun.star.text.XDocumentIndexesSupplier', 'com.sun.star.text.XTextFieldsSupplier', 'com.sun.star.text.XTextFramesSupplier', 'com.sun.star.text.XTextSectionsSupplier', 'com.sun.star.util.XNumberFormatsSupplier'])
('com.sun.star.text.TextDocument', ['com.sun.star.text.GenericTextDocument'])
('com.sun.star.util.XReplaceable', ['com.sun.star.util.XSearchable'])
('com.sun.star.text.XTextDocument', ['com.sun.star.frame.XModel'])
('com.sun.star.document.OfficeDocument', ['com.sun.star.frame.XModel', 'com.sun.star.util.XModifiable', 'com.sun.star.frame.XStorable', 'com.sun.star.view.XPrintable', 'com.sun.star.document.XEventBroadcaster', 'com.sun.star.document.XDocumentEventBroadcaster', 'com.sun.star.document.XEventsSupplier', 'com.sun.star.document.XViewDataSupplier', 'com.sun.star.view.XPrintJobBroadcaster', 'com.sun.star.document.XEmbeddedScripts', 'com.sun.star.document.XDocumentPropertiesSupplier', 'com.sun.star.document.XUndoManagerSupplier'])
('com.sun.star.frame.XModel', ['com.sun.star.lang.XComponent'])
libreoffice49()の出力結果はこのようになります。

サービスがなく、インターフェイスしかもたないXSCRIPTCONTEXT.getComponentContext()のようなオブジェクトについての対応も考えないといけません。

その場合は継承図の末裔をどうするかも考えないといけません。

すべてのkeyとvalueの組み合わせについてvalueからたどってkeyがないvalueが末裔になるのですが、すごく計算量が多くなりそうです。

こういうときは8.5. heapq — ヒープキューアルゴリズム — Python 3.3.3 ドキュメントを使うのかと思いましたが、これは数値についてのようです。

結局折角すでにある階層構造を1階層だけ切り取って取得してそれをまた階層構造を構築しようという考え方が間違っているようですので、階層構造をそのまま取得する方法を考えます。

8.3. collections — コンテナデータ型class collections.OrderedDict([items])を使えばよいのかと思いましたが、順番を決めないといけないのは最初のgetSupportedServiceNames()で得たサービス名だけであることに気がつきました。

末裔になるのはそれの子がいないということで判断できることに気づきました。

あとの順序は辞書で変換していけば勝手に決まるはずです。

オブジェクトからサービスとインターフェイスの継承関係辞書を生成


from itertools import chain  # 多次元リストを1次元リストに変換するのに使用。
def libreoffice49():
    obj = XSCRIPTCONTEXT.getDocument()  # 調査対象のオブジェクトを取得。old-styleサービスの例。LibreOfficeドキュメントを開いておく必要がある。
    # obj = XSCRIPTCONTEXT.getDesktop().getCurrentFrame().getContainerWindow().getToolkit() # 調査対象のオブジェクトを取得。new-styleサービスの例。
    # obj = XSCRIPTCONTEXT.getComponentContext()  # 調査対象のオブジェクトを取得。サービスがないオブジェクトの例。
    s_omi = {'com.sun.star.uno.XInterface', 'com.sun.star.uno.XWeak'}  # 絶対使わなさそうなインターフェイス名の集合。
    tdm = XSCRIPTCONTEXT.getComponentContext().getByName('/singletons/com.sun.star.reflection.theTypeDescriptionManager')  # TypeDescriptionManagerをシングルトンでインスタンス化。
    s_dic = dict()  # key:子サービス名、value:親サービスのタプル。
    i_dic = dict()  # key:子インターフェイス名または子サービス名、value:親インターフェイスのタプル。
    if hasattr(obj, "getSupportedServiceNames"):  # hasattr()は属性の有無をチェックするPythonの組み込み関数。
        t_ss = obj.getSupportedServiceNames()  # サポートしているサービス名のタプルを取得。
        if t_ss:  # サポートしているサービスがある場合。
            lst_std = list(map(tdm.getByHierarchicalName, t_ss))  # t_ssの要素のサービス名からXServiceTypeDescription2インターフェイスをもつオブジェクトのリストを取得。
            for std in lst_std:  # 各サービスについて親のサービスを得る。
                t_std = std.getMandatoryServices()+std.getOptionalServices()  # 直に継承しているサービスのXServiceTypeDescription2インターフェイスをもつオブジェクトのタプルを取得。
                if t_std:  # 直に継承しているサービスがあるのなら
                    s_dic[std.Name] = t_std  # 直に継承しているサービスのTypeDescriptionオブジェクトのタプルを子のサービス名をkeyにしてdicに取得。
            lst_des = [j for j in lst_std if j.Name not in [i.Name for i in chain.from_iterable(s_dic.values())]] # オブジェクトから得たサービスのうち、辞書dicのvalueにない(親になっていない)サービス(=末裔)をリストで取得。
            for std in lst_std:  # 各サービスについてインターフェイスを得て、そのインターフェイスについて順次親を得る。
                if std.isSingleInterfaceBased():  # new-style(single-interface–based)サービスのとき
                    t_itd = std.getInterface(),  # サービスがもつインターフェイスをXInterfaceTypeDescription2インターフェイスをもつオブジェクトでタプルにして取得。
                    if t_itd:  # インターフェイスのタプルがあるとき
                        i_dic[std.Name] = t_itd  # サービス名を子、インターフェイスのタプルを親にしてdicに取得。
                        u = get_interfaces(t_itd, i_dic, s_omi)  # インターフェイスのタプルからさらに親のインターフェイスのタプルをdicに得る。戻り値は追加されたインターフェイスのタプルのリスト。
                        while u:  # dicに新たに追加された値がある間は実行。
                            for t_itd in u:  # 新たに追加されたTypeDescriptionオブジェクトのタプルについて。
                                u = get_interfaces(t_itd, i_dic, s_omi) # インターフェイスのタプルからさらに親のインターフェイスのタプルをdicに得る。
                else:  # old-style(accumulation-based)サービスのとき
                    lst_itd = [i for i in std.getMandatoryInterfaces() + std.getOptionalInterfaces() if i.Name not in s_omi]  # o_omiを除いてサービスがもつインターフェイスをXInterfaceTypeDescription2インターフェイスをもつオブジェクトのリストを取得。
                    if lst_itd:  # 親インターフェイスが存在するとき。
                        i_dic[std.Name] = lst_itd  # サービス名を子、インターフェイスのタプルを親にしてdicに取得。
                        u = get_interfaces(lst_itd, i_dic, s_omi)  # インターフェイスのタプルからさらに親のインターフェイスのタプルをdicに得る。戻り値は追加されたインターフェイスのタプルのリスト。
                        while u:  # dicに新たに追加された値がある間は実行。
                            for t_itd in u:  # 新たに追加されたTypeDescriptionオブジェクトのタプルについて。
                                u = get_interfaces(t_itd, i_dic, s_omi) # インターフェイスのタプルからさらに親のインターフェイスのタプルをdicに得る。
            # print([i.Name for i in chain.from_iterable(s_dic.values())])  # 2次元になっている辞書のvalueを1次元に展開し、各要素についてName属性のリストを出力。
            # print("\n".join([str(k) for k in zip(s_dic.keys(), [[j.Name for j in i] for i in s_dic.values()])]))  # s_dicの2次元のvalueを展開してName属性のリストを取得して、keyのリストと組み合わせたリストを生成して各要素を改行文字で結合して出力。
            # print([i.Name for i in lst_des])  # 末裔のリストを出力。
            # print("\n".join([str(k) for k in zip(i_dic.keys(), [[j.Name for j in i] for i in i_dic.values()])]))  # i_dicの2次元のvalueを展開してName属性のリストを取得して、keyのリストと組み合わせたリストを生成して各要素を改行文字で結合して出力。
    else:  # サポートしているサービスがないときはインターフェイス名一覧を得る。
        if hasattr(obj, "getTypes"):
            lst_si = [i.typeName for i in obj.getTypes()]  # インターフェイス名のリストを取得。<Type instance インターフェイス名 (<uno.Enum com.sun.star.uno.TypeClass ('INTERFACE')>)>、といったものが返ってきますが型不明。
            s_si = set(lst_si)  # インターフェイス名一覧を集合型に変換。
            lst_des = s_si.intersection(s_si ^ s_omi)  # 絶対使わなさそうなインターフェイス名を除いてインターフェイス名(=末裔)をリストで取得。
            lst_des = list(map(tdm.getByHierarchicalName, lst_des))  # lst_siの要素のインターフェイス名からXInterfaceTypeDescription2インターフェイスをもつオブジェクトのリストを取得。
            if lst_des:  # インターフェイスのタプルがあるとき
                u = get_interfaces(lst_des, i_dic, s_omi)  # インターフェイスのリストからさらに親のインターフェイスのタプルをdicに得る。戻り値は追加されたインターフェイスのタプルのリスト。
                while u:  # dicに新たに追加された値がある間は実行。
                    for t_itd in u:  # 新たに追加されたTypeDescriptionオブジェクトのタプルについて。
                        u = get_interfaces(t_itd, i_dic, s_omi) # インターフェイスのタプルからさらに親のインターフェイスのタプルをdicに得る。
            # print([i.Name for i in lst_des])  # 末裔のリストを出力。
            # print("\n".join([str(k) for k in zip(i_dic.keys(), [[j.Name for j in i] for i in i_dic.values()])]))  # i_dicの2次元のvalueを展開してName属性のリストを取得して、keyのリストと組み合わせたリストを生成して各要素を改行文字で結合して出力。
def get_interfaces(t_itd, i_dic, s_omi):
    u = list()  # 新たに追加になったdicのvalue(インターフェイスのタプル)を入れるリスト。
    for itd in t_itd:  # 各インターフェイスについて親のインターフェイスを得る。
        lst_itd = [i for i in itd.getBaseTypes()+itd.getOptionalBaseTypes() if i.Name not in s_omi]  # o_omiを除くインターフェイスのリストを取得。
        if lst_itd:  # 親インターフェイスのリストがあるとき
            i_dic[itd.Name] = lst_itd # インターフェイス名を子にして親のインターフェイスのタプルをdicに取得。
            u.append(i_dic[itd.Name])  # dicに新たに追加したインターフェイスのタプルをリストuに追加。
    return u  # uを戻り値で返す。
if __name__ == "__main__":
    import unopy
    XSCRIPTCONTEXT = unopy.connect()
    if not XSCRIPTCONTEXT:
        print("Failed to connect.")
        import sys
        sys.exit(0)
    libreoffice49()
これでうまくいきそうです。

まだ入出力する部分は作っていませんのでハイライトしている行のコメントアウトを適当にとって動作確認をします。

例えば3行目と39行目のコメントアウトをはずして、Writerドキュメントを開いてPyCharmから実行すると以下の結果が出力されます。
('com.sun.star.util.XReplaceable', ['com.sun.star.util.XSearchable'])
('com.sun.star.text.XTextDocument', ['com.sun.star.frame.XModel'])
('com.sun.star.frame.XModel', ['com.sun.star.lang.XComponent'])
('com.sun.star.util.XModifiable', ['com.sun.star.util.XModifyBroadcaster'])
('com.sun.star.text.GenericTextDocument', ['com.sun.star.lang.XMultiServiceFactory', 'com.sun.star.text.XTextDocument', 'com.sun.star.util.XSearchable', 'com.sun.star.util.XRefreshable', 'com.sun.star.text.XFootnotesSupplier', 'com.sun.star.text.XEndnotesSupplier', 'com.sun.star.util.XReplaceable', 'com.sun.star.text.XPagePrintable', 'com.sun.star.text.XReferenceMarksSupplier', 'com.sun.star.text.XChapterNumberingSupplier', 'com.sun.star.beans.XPropertySet', 'com.sun.star.text.XTextGraphicObjectsSupplier', 'com.sun.star.text.XTextEmbeddedObjectsSupplier', 'com.sun.star.text.XTextTablesSupplier', 'com.sun.star.style.XStyleFamiliesSupplier', 'com.sun.star.text.XBookmarksSupplier', 'com.sun.star.text.XDocumentIndexesSupplier', 'com.sun.star.text.XTextFieldsSupplier', 'com.sun.star.text.XTextFramesSupplier', 'com.sun.star.text.XTextSectionsSupplier', 'com.sun.star.util.XNumberFormatsSupplier'])
('com.sun.star.document.OfficeDocument', ['com.sun.star.frame.XModel', 'com.sun.star.util.XModifiable', 'com.sun.star.frame.XStorable', 'com.sun.star.view.XPrintable', 'com.sun.star.document.XEventBroadcaster', 'com.sun.star.document.XDocumentEventBroadcaster', 'com.sun.star.document.XEventsSupplier', 'com.sun.star.document.XViewDataSupplier', 'com.sun.star.view.XPrintJobBroadcaster', 'com.sun.star.document.XEmbeddedScripts', 'com.sun.star.document.XDocumentPropertiesSupplier', 'com.sun.star.document.XUndoManagerSupplier'])
38行目のコメントアウトをはずすと['com.sun.star.text.TextDocument']が出力されるのでここを起点s_dicの辞書からたどってさらにi_dicの辞書へたどって継承図を作成していけばよいとわかります。

多次元リストを展開する方法とリストを比較して共通リストを除く方法のところで悩みました。

Pythonの関数に辞書を渡すとreturnしなくても変更が上書きされる


3箇所に共通部分をみつけたのでget_interfaces2()関数で括りだしました。
from itertools import chain  # 多次元リストを1次元リストに変換するのに使用。
def libreoffice49():
    obj = XSCRIPTCONTEXT.getDocument()  # 調査対象のオブジェクトを取得。old-styleサービスの例。LibreOfficeドキュメントを開いておく必要がある。
    # obj = XSCRIPTCONTEXT.getDesktop().getCurrentFrame().getContainerWindow().getToolkit() # 調査対象のオブジェクトを取得。new-styleサービスの例。
    # obj = XSCRIPTCONTEXT.getComponentContext()  # 調査対象のオブジェクトを取得。サービスがないオブジェクトの例。
    s_omi = {'com.sun.star.uno.XInterface', 'com.sun.star.uno.XWeak'}  # 絶対使わなさそうなインターフェイス名の集合。
    tdm = XSCRIPTCONTEXT.getComponentContext().getByName('/singletons/com.sun.star.reflection.theTypeDescriptionManager')  # TypeDescriptionManagerをシングルトンでインスタンス化。
    s_dic = dict()  # key:子サービス名、value:親サービスのタプル。
    i_dic = dict()  # key:子インターフェイス名または子サービス名、value:親インターフェイスのタプル。
    if hasattr(obj, "getSupportedServiceNames"):  # hasattr()は属性の有無をチェックするPythonの組み込み関数。
        t_ss = obj.getSupportedServiceNames()  # サポートしているサービス名のタプルを取得。
        if t_ss:  # サポートしているサービスがある場合。
            lst_std = list(map(tdm.getByHierarchicalName, t_ss))  # t_ssの要素のサービス名からXServiceTypeDescription2インターフェイスをもつオブジェクトのリストを取得。
            for std in lst_std:  # 各サービスについて親のサービスを得る。
                t_std = std.getMandatoryServices()+std.getOptionalServices()  # 直に継承しているサービスのXServiceTypeDescription2インターフェイスをもつオブジェクトのタプルを取得。
                if t_std:  # 直に継承しているサービスがあるのなら
                    s_dic[std.Name] = t_std  # 直に継承しているサービスのTypeDescriptionオブジェクトのタプルを子のサービス名をkeyにしてdicに取得。
            lst_des = [j for j in lst_std if j.Name not in [i.Name for i in chain.from_iterable(s_dic.values())]] # オブジェクトから得たサービスのうち、辞書dicのvalueにない(親になっていない)サービス(=末裔)をリストで取得。
            for std in lst_std:  # 各サービスについてインターフェイスを得て、そのインターフェイスについて順次親を得る。
                if std.isSingleInterfaceBased():  # new-style(single-interface–based)サービスのとき
                    t_itd = std.getInterface(),  # サービスがもつインターフェイスをXInterfaceTypeDescription2インターフェイスをもつオブジェクトでタプルにして取得。
                    if t_itd:  # インターフェイスのタプルがあるとき
                        i_dic[std.Name] = t_itd  # サービス名を子、インターフェイスのタプルを親にしてdicに取得。
                        get_interfaces2(t_itd, i_dic, s_omi)
                else:  # old-style(accumulation-based)サービスのとき
                    lst_itd = [i for i in std.getMandatoryInterfaces() + std.getOptionalInterfaces() if i.Name not in s_omi]  # o_omiを除いてサービスがもつインターフェイスをXInterfaceTypeDescription2インターフェイスをもつオブジェクトのリストを取得。
                    if lst_itd:  # 親インターフェイスが存在するとき。
                        i_dic[std.Name] = lst_itd  # サービス名を子、インターフェイスのタプルを親にしてdicに取得。
                        get_interfaces2(lst_itd, i_dic, s_omi)
            # print([i.Name for i in chain.from_iterable(s_dic.values())])  # 2次元になっている辞書のvalueを1次元に展開し、各要素についてName属性のリストを出力。
            # print("\n".join([str(k) for k in zip(s_dic.keys(), [[j.Name for j in i] for i in s_dic.values()])]))  # s_dicの2次元のvalueを展開してName属性のリストを取得して、keyのリストと組み合わせたリストを生成して各要素を改行文字で結合して出力。
            # print([i.Name for i in lst_des])  # 末裔のリストを出力。
            # print("\n".join([str(k) for k in zip(i_dic.keys(), [[j.Name for j in i] for i in i_dic.values()])]))  # i_dicの2次元のvalueを展開してName属性のリストを取得して、keyのリストと組み合わせたリストを生成して各要素を改行文字で結合して出力。
    else:  # サポートしているサービスがないときはインターフェイス名一覧を得る。
        if hasattr(obj, "getTypes"):
            lst_si = [i.typeName for i in obj.getTypes()]  # インターフェイス名のリストを取得。<Type instance インターフェイス名 (<uno.Enum com.sun.star.uno.TypeClass ('INTERFACE')>)>、といったものが返ってきますが型不明。
            s_si = set(lst_si)  # インターフェイス名一覧を集合型に変換。
            lst_des = s_si.intersection(s_si ^ s_omi)  # 絶対使わなさそうなインターフェイス名を除いてインターフェイス名(=末裔)をリストで取得。
            lst_des = list(map(tdm.getByHierarchicalName, lst_des))  # lst_siの要素のインターフェイス名からXInterfaceTypeDescription2インターフェイスをもつオブジェクトのリストを取得。
            if lst_des:  # インターフェイスのタプルがあるとき
                get_interfaces2(lst_des, i_dic, s_omi)
            # print([i.Name for i in lst_des])  # 末裔のリストを出力。
            # print("\n".join([str(k) for k in zip(i_dic.keys(), [[j.Name for j in i] for i in i_dic.values()])]))  # i_dicの2次元のvalueを展開してName属性のリストを取得して、keyのリストと組み合わせたリストを生成して各要素を改行文字で結合して出力。
def get_interfaces2(t_itd, i_dic, s_omi):
    u = get_interfaces(t_itd, i_dic, s_omi)  # インターフェイスのリストからさらに親のインターフェイスのタプルをdicに得る。戻り値は追加されたインターフェイスのタプルのリスト。
    while u:  # dicに新たに追加された値がある間は実行。
        for t_itd in u:  # 新たに追加されたTypeDescriptionオブジェクトのタプルについて。
            u = get_interfaces(t_itd, i_dic, s_omi) # インターフェイスのタプルからさらに親のインターフェイスのタプルをdicに得る。
def get_interfaces(t_itd, i_dic, s_omi):
    u = list()  # 新たに追加になったdicのvalue(インターフェイスのタプル)を入れるリスト。
    for itd in t_itd:  # 各インターフェイスについて親のインターフェイスを得る。
        lst_itd = [i for i in itd.getBaseTypes()+itd.getOptionalBaseTypes() if i.Name not in s_omi]  # o_omiを除くインターフェイスのリストを取得。
        if lst_itd:  # 親インターフェイスのリストがあるとき
            i_dic[itd.Name] = lst_itd # インターフェイス名を子にして親のインターフェイスのタプルをdicに取得。
            u.append(i_dic[itd.Name])  # dicに新たに追加したインターフェイスのタプルをリストuに追加。
    return u  # uを戻り値で返す。
if __name__ == "__main__":
    import unopy
    XSCRIPTCONTEXT = unopy.connect()
    if not XSCRIPTCONTEXT:
        print("Failed to connect.")
        import sys
        sys.exit(0)
    libreoffice49()
44行目の関数get_interfaces2()では渡された辞書i_dicを変更していますがreturnで返していません。

ところが33行目や43行目ではちゃんと変更されたi_dicの結果が返ってきます。
前提として、Python では引数は代入によって渡されます。代入はオブジェクトへの参照を作るだけなので、呼び出し元と呼び出し先にある引数名の間にエイリアスはありませんし、参照渡しそれ自体はありません。
パラメータを出力する関数 (参照渡し) はどのように書きますか?
プログラミング FAQ — Python 3.3.3 ドキュメントにはこのように書いてありますが、続いていくつか例が載っています。

関数の引数は呼び出し元から渡されると以後呼び出し元とは関連がなくなるのですが、辞書やリストが引数にされたときは、新たに辞書やリストがコピーして生成されるのではなく参照渡しと同様になるそうです。

49行目の関数get_interfaces()はリストuをreturnしています。

これは引数で受け取ったuではないのでreturnしないと呼び出し元には関数内の変更は反映されません。

違うものなのに同じ変数名uを使っているのでややこしいのですね。

ということで関数get_interfaces()内のリストはvに変更しました。
from itertools import chain  # 多次元リストを1次元リストに変換するのに使用。
def libreoffice49():
    obj = XSCRIPTCONTEXT.getDocument()  # 調査対象のオブジェクトを取得。old-styleサービスの例。LibreOfficeドキュメントを開いておく必要がある。
    # obj = XSCRIPTCONTEXT.getDesktop().getCurrentFrame().getContainerWindow().getToolkit() # 調査対象のオブジェクトを取得。new-styleサービスの例。
    # obj = XSCRIPTCONTEXT.getComponentContext()  # 調査対象のオブジェクトを取得。サービスがないオブジェクトの例。
    s_omi = {'com.sun.star.uno.XInterface', 'com.sun.star.uno.XWeak'}  # 絶対使わなさそうなインターフェイス名の集合。
    tdm = XSCRIPTCONTEXT.getComponentContext().getByName('/singletons/com.sun.star.reflection.theTypeDescriptionManager')  # TypeDescriptionManagerをシングルトンでインスタンス化。
    s_dic = dict()  # key:子サービス名、value:親サービスのタプル。
    i_dic = dict()  # key:子インターフェイス名または子サービス名、value:親インターフェイスのタプル。
    if hasattr(obj, "getSupportedServiceNames"):  # hasattr()は属性の有無をチェックするPythonの組み込み関数。
        t_ss = obj.getSupportedServiceNames()  # サポートしているサービス名のタプルを取得。
        if t_ss:  # サポートしているサービスがある場合。
            lst_std = list(map(tdm.getByHierarchicalName, t_ss))  # t_ssの要素のサービス名からXServiceTypeDescription2インターフェイスをもつオブジェクトのリストを取得。
            for std in lst_std:  # 各サービスについて親のサービスを得る。
                t_std = std.getMandatoryServices()+std.getOptionalServices()  # 直に継承しているサービスのXServiceTypeDescription2インターフェイスをもつオブジェクトのタプルを取得。
                if t_std:  # 直に継承しているサービスがあるのなら
                    s_dic[std.Name] = t_std  # 直に継承しているサービスのTypeDescriptionオブジェクトのタプルを子のサービス名をkeyにしてdicに取得。
            lst_des = [j for j in lst_std if j.Name not in [i.Name for i in chain.from_iterable(s_dic.values())]] # オブジェクトから得たサービスのうち、辞書dicのvalueにない(親になっていない)サービス(=末裔)をリストで取得。
            for std in lst_std:  # 各サービスについてインターフェイスを得て、そのインターフェイスについて順次親を得る。
                if std.isSingleInterfaceBased():  # new-style(single-interface–based)サービスのとき
                    t_itd = std.getInterface(),  # サービスがもつインターフェイスをXInterfaceTypeDescription2インターフェイスをもつオブジェクトでタプルにして取得。
                    if t_itd:  # インターフェイスのタプルがあるとき
                        i_dic[std.Name] = t_itd  # サービス名を子、インターフェイスのタプルを親にしてdicに取得。
                        get_interfaces2(t_itd, i_dic, s_omi)
                else:  # old-style(accumulation-based)サービスのとき
                    lst_itd = [i for i in std.getMandatoryInterfaces() + std.getOptionalInterfaces() if i.Name not in s_omi]  # o_omiを除いてサービスがもつインターフェイスをXInterfaceTypeDescription2インターフェイスをもつオブジェクトのリストを取得。
                    if lst_itd:  # 親インターフェイスが存在するとき。
                        i_dic[std.Name] = lst_itd  # サービス名を子、インターフェイスのタプルを親にしてdicに取得。
                        get_interfaces2(lst_itd, i_dic, s_omi)
            # print([i.Name for i in chain.from_iterable(s_dic.values())])  # 2次元になっている辞書のvalueを1次元に展開し、各要素についてName属性のリストを出力。
            # print("\n".join([str(k) for k in zip(s_dic.keys(), [[j.Name for j in i] for i in s_dic.values()])]))  # s_dicの2次元のvalueを展開してName属性のリストを取得して、keyのリストと組み合わせたリストを生成して各要素を改行文字で結合して出力。
            # print([i.Name for i in lst_des])  # 末裔のリストを出力。
            print("\n".join([str(k) for k in zip(i_dic.keys(), [[j.Name for j in i] for i in i_dic.values()])]))  # i_dicの2次元のvalueを展開してName属性のリストを取得して、keyのリストと組み合わせたリストを生成して各要素を改行文字で結合して出力。
    else:  # サポートしているサービスがないときはインターフェイス名一覧を得る。
        if hasattr(obj, "getTypes"):
            lst_si = [i.typeName for i in obj.getTypes()]  # インターフェイス名のリストを取得。<Type instance インターフェイス名 (<uno.Enum com.sun.star.uno.TypeClass ('INTERFACE')>)>、といったものが返ってきますが型不明。
            s_si = set(lst_si)  # インターフェイス名一覧を集合型に変換。
            lst_des = s_si.intersection(s_si ^ s_omi)  # 絶対使わなさそうなインターフェイス名を除いてインターフェイス名(=末裔)をリストで取得。
            lst_des = list(map(tdm.getByHierarchicalName, lst_des))  # lst_siの要素のインターフェイス名からXInterfaceTypeDescription2インターフェイスをもつオブジェクトのリストを取得。
            if lst_des:  # インターフェイスのタプルがあるとき
                get_interfaces2(lst_des, i_dic, s_omi)
            # print([i.Name for i in lst_des])  # 末裔のリストを出力。
            # print("\n".join([str(k) for k in zip(i_dic.keys(), [[j.Name for j in i] for i in i_dic.values()])]))  # i_dicの2次元のvalueを展開してName属性のリストを取得して、keyのリストと組み合わせたリストを生成して各要素を改行文字で結合して出力。
def get_interfaces2(t_itd, i_dic, s_omi):  # 親インターフェイスをs_omiにあるインターフェイスの手前まで取得する。
    u = get_interfaces(t_itd, i_dic, s_omi)  # インターフェイスのリストからさらに親のインターフェイスのタプルをdicに得る。戻り値は追加されたインターフェイスのタプルのリスト。
    while u:  # dicに新たに追加された値がある間は実行。
        for t_itd in u:  # 新たに追加されたTypeDescriptionオブジェクトのタプルについて。
            u = get_interfaces(t_itd, i_dic, s_omi) # インターフェイスのタプルからさらに親のインターフェイスのタプルをdicに得る。
def get_interfaces(t_itd, i_dic, s_omi):  # 辞書i_dicにkey:子インターフェイス名、value:親インターフェイスのリストを追加。
    v = list()  # 新たに追加になったdicのvalue(インターフェイスのタプル)を入れるリスト。
    for itd in t_itd:  # 各インターフェイスについて親のインターフェイスを得る。
        lst_itd = [i for i in itd.getBaseTypes()+itd.getOptionalBaseTypes() if i.Name not in s_omi]  # o_omiを除くインターフェイスのリストを取得。
        if lst_itd:  # 親インターフェイスのリストがあるとき
            i_dic[itd.Name] = lst_itd # インターフェイス名を子にして親のインターフェイスのタプルをdicに取得。
            v.append(i_dic[itd.Name])  # dicに新たに追加したインターフェイスのタプルをリストuに追加。
    return v  # vを戻り値で返す。
if __name__ == "__main__":
    import unopy
    XSCRIPTCONTEXT = unopy.connect()
    if not XSCRIPTCONTEXT:
        print("Failed to connect.")
        import sys
        sys.exit(0)
    libreoffice49()

参考にしたサイト


LibreOffice: com::sun::star Module Reference
LibreOffice 4.2 SDK API Reference

hasattr(object, name)
オブジェクトの属性の有無をチェックするPythonの組み込み関数。

map(function, iterable, ...)
第2引数のリストの各要素に第一引数の関数を適用します。

Python - リストとリストを比較して、共通する要素をリストで取り出す方法 - Qiita
リストを集合に変換して論理積をとる方法が紹介されています。

Pythonでリストをflattenする方法まとめ - Soleil cou coupé
多次元リストを1次元に展開する方法。

パラメータを出力する関数 (参照渡し) はどのように書きますか?
Pythonの関数へリストや辞書を渡すと変更が上書きされます。

次の関連記事:LibreOffice(50)オブジェクトから継承図をたどってメソッド一覧を得る

PR

0 件のコメント:

コメントを投稿