マクロ Edit

MRI 1.0.0 に追加予定の機能です。

Python で書いたコードを MRI を拡張したりするためにマクロとして実行できます。

MRI のマクロでは MRI の全ての機能にアクセスできますが、オフィースがクラッシュする可能性があります。しかし、MRI は普段利用するツールではないので大きな問題ではありません。

MRI のマクロは MRI の実装に強く依存しています。そのため、MRI の内部構造の知識がなければマクロを書くのは難しいでしょう。このドキュメントでは重要な部分のみ説明しています。詳細についてはソースコードを参照してください。

マクロメニュー Edit

MRI ウィンドウを開くと Macros メニューが MRI ウィンドウのメインメニューにあります。このメニューの項目は動的に作成されます。

マクロは以下のディレクトリに保存されていなければいけません。

  • 拡張機能のレポジトリ、これは MRI に付属のものです。
  • ユーザーのレポジトリ。Tools - Configuration... で設定できます。

ユーザーのレポジトリにはアクセス権限のあるディレクトリを指定してください。初期設定は $(user)/Scripts/python/mri です。

マクロのインポート Edit

MRI 用のマクロが含まれているファイルをユーザーのディレクトリに簡単にコピーする方法があります。Macros - bundle.py - Import Macros を選択してください。ファイルの選択ダイアログが開くので、マクロのファイルを選択してください。

マクロが ZIP、tar.gz、tar.bz2 アーカイブにまとめられている場合にはインポート機能がアーカイブを解凍してアーカイブの内容をインポートします。アーカイブ中に README ファイルがある場合にはアーカイブの内容をインポートするまえに README ファイルの内容をライセンスダイアログに表示します。

マクロメニュー項目のリロード Edit

Macros メニューは動的に作成されますが、一度読み込んだ後は MRI を再起動しなければ変更されません。マクロに変更があった場合にはリロードしてください。Macros - bundle.py - Reload Macros Menu を選択してください。マクロの項目を削除して再読込フラグを有効にします。

ファイルの種類 Edit

マクロは Python で書き、ファイルの拡張子 ".py" をつけてユーザーのリポジトリに保存してください。リポジトリに関しては前のセクションを参照してください。

マクロの種類 Edit

MRI で実行できるのマクロには二種類あります。一つ目は普通のマクロで、MRI を拡張するためのものです。もうひとつのものはアクションマクロです。これは API 呼び出しがコードとして記録されます。

普通のマクロ Edit

普通のマクロの簡単な例を示します。

# # hello.py example
# __all__ 変数の項目がメニューに表示されます。
# 項目の順はそのままに、空文字列の項目は区切り
# となります。
__all__ = ["hello_world", "", "another_function"]

def hello_world(mri):
    """ はろー
        メソッドの説明 """
    # ドキュメント文字列は関数のタイトルと説明として
    # 利用されます。最初の行はタイトルとして利用されます。
    # 二行目以降は関数の説明として利用されます。
    mri.message("Hello World!")

def another_function(mri):
    pass

def private_function(mri):
   """ この関数は関数リストに表示されません。 """

マクロのファイルは imp.load_source 関数でモジュールとして読み込まれます。関数は MRI のインスタンスを引数として受け取る様にしてください。MRI の全ての機能にその引数からアクセスできます。MRI については後のセクションで説明しています。

関数のドキュメント文字列はメニューや他の場所でタイトルと項目のツールチップなどに利用されます。

__all__ モジュール変数はそのファイルからエクスポートされている関数を示すために利用します。前出の例では hello_world と another_function 関数は hello.py 項目の子項目として表示されます。二つの項目は区切り線で区切って表示されます。この区切り線は空文字列の項目から生成されたものです。

もうひとつ例を示します。以下の例では現在のターゲットを利用しますが、API 呼び出しはコードジェネレータによって記録されません。

def another_example(mri):
   target = mri.current.target # real value of the current target 
   sheets = target.getSheets()
   sheet = sheets.getSheets()
   # To do something

アクションマクロ Edit

アクションマクロは API 呼び出しがコードとして記録されるものです。Entry インスタンスを利用する以外は普通のマクロと違いはありません。エントリが履歴に登録されていないなら、ジェネレータ構文で履歴に登録してください。

以下に簡単な例を示します。

def action_macro(mri):
    current = mri.current # current is a Writer document object
    text = current.getText()
    cursor = text.createTextCursor()

getText と createTextCursor メソッドの呼び出しは自動的に記録されてメソッドの返り値は履歴の項目となります。

サービスなどの新しい Entry インスタンスを利用するときには、履歴に登録してください。

def macro_test(mri):
    current = mri.current
    transformer = mri.create_service(
        "com.sun.star.util.URLTransformer")
    yield current, transformer

ジェネレータでは親エントリと子エントリのタプルを返してください。子エントリが親エントリの子ノードとして履歴に登録されます。最初の要素、親エントリは履歴に登録されていなければいけません。

このアクションマクロにはかなり制限があります。たとえば、リスナーなどの独自の UNO コンポーネントはアクションマクロでは利用できません。

MRI オブジェクト Edit

全てのマクロは現在の MRI インスタンスを最初の引数として受け取ります。この引数の値は MRI のコアプログラムです。MRI のインスタンスはユーザーインターフェースをサポートしていませんが、ui インスタンス変数から現在のユーザーインターフェースにアクセスできます。

インスタンス変数 Edit

MRI インスタンスは様々な値をインスタンス変数に保持しています。

VariableTypeDescription
ctxpyunoコンポーネントコンテキスト
configmytools_Mri.config.Config設定
webmytools_Mri.web.IDLウェブページなどをウェブブラウザで開く
enginemytools_Mri.engine.Engineイントロスペクションとリフレクション
historymytools_Mri.node.Root履歴の階層構造
currentmytools_Mri.engine.Entry現在のエントリ
cgmytools_Mri.cg.CodeGeneratorコードジェネレータを管理
modeboolノーマルモードのとき True、False ではマクロモード
open_newboolTrue のとき次のターゲーットを新規ウィンドウで開きます
macrosmytools_Mri.macros.Macrosマクロを管理
uimytools_Mri.ui.MRIUiユーザーインターフェース

メソッド Edit

ComponentContext Edit

css.uno.XComponentContext インターフェースを ctx インスタンス変数に保持しています。新規サービスが必要な場合などに利用できます。

Current エントリ Edit

MRI は現在のターゲーットを current インスタンス変数に保持しています。この値は mytools_Mri.engine.Entry クラスのインスタンスです。

Engine オブジェクト Edit

エンジンは UNO オブジェクトの情報を取得するためのコアオブジェクトです。このクラスでは css.beans.Introspection、 css.reflection.CoreReflection と css.reflection.TypeDescription サービスを利用しています。mytools_Mri.engine.Engine クラスで実装されています。エンジンインスタンスは MRI の engine インスタンス変数から取得できます。

def use_engine(mri):
    """ Shows type name of the current target. """
    engine = mri.engine
    current = mri.current
    mri.ui.message(engine.get_type_name(current))

Web オブジェクト Edit

ウェブオブジェクトは設定で指定されたウェブブラウザを開く手助けをしてくれます。mytools_Mri.web.IDL クラスで実装されています。

def open_url(mri):
    """ URL をウェブブラウザで開く """
    url = "http://example.com/"
    mri.web.open_url(url)

def open_ref(mri):
    """ IDL リファレンスを開きます。
        二番目の引数はオプションです。 """
    idl = "com.sun.star.awt.Rectangle"
    name = "X"
    mri.web.open_idl_reference(idl, name)

MRI UI オブジェクト Edit

mytools_Mri.ui.MRIUi クラスは MRI のウィンドウを管理します。

VariableTypeDescription
ctxpyunoコンポーネントコンテキスト
mainmytools_Mri.MRIMRI インスタンス
desktoppyunoデスクトップインスタンス
listenersdictリスナー
property_modeboolTrue のときプロパティを取得。False で設定
treemytools_Mri.ui.pages.HistoryTreeUiツリー構造の履歴
dlgsmytools_Mri.ui.dialogs.Dialogsダイアログを提供します
status_eraserthreading.Timerステータス文字列を消去するタイマー
framepyunoMRI ウィンドウの入ったフレーム
contpyunoコンテナウィンドウ
pagesmytools_Mri.ui.pages.InfoUi/GridUiコントロールへのアクセスを提供

ダイアログ Edit

mytools_Mri.ui.dialogs.Dialogs クラスが提供するダイアログが利用できます。MRIUi インスタンスの dlgs インスタンス変数からアクセスできます。

def dialog_select(mri):
    items = ("Item 1", "Item 2", "Item 3", "Item 1")
    item = mri.ui.dlgs.dialog_select(items, "Title")
    mri.ui.dlgs.dialog_info("%s is selected. " % item)

dialog_select メソッドの二番目の引数はダイアログのタイトルを指定します。この例では二つの Item 1 がありますが、これらを区別したいときには三番目の引数に True を指定してください。そうすると返り値として選択された値ではなく選択された位置が返ります。キャンセルされたときには None が返ります。

ユーザーに入力させたいときには dialog_input メソッドを利用してください。

def dialog_input(mri):
    text = mri.ui.dlgs.dialog_input(
          "Service Name", "Input a service name.", 
          "com.sun.star.awt.")
    mri.ui.message(text)

全ての引数は省略可能です。

履歴から項目を選択させるダイアログがあります。

def history_selector(mri):
    entry = mri.ui.dlgs.history_selector("Select Entry")
    if entry:
        mri.ui.message(entry.name)

返り値は mytools_Mri.engine.Entry クラスのインスタンスです。

メッセージを表示したり、ユーザーの反応が必要なときには message メソッドを利用できます。このメソッドは css.awt.XMessageBoxFactory へのショートカットを提供します。詳細についてはリファレンスを参照してください。メソッドは次のように定義されています。

def message(self, message, title="", type="messbox", buttons=1):

エントリ Edit

Entry クラスは現在またはそれ以外のターゲットを保持するために利用されています。例えば、現在のターゲットは MRI インスタンスの current インスタンス変数から取得できます。Entry クラスは mytools_Mri.engine モジュールで定義されており、次のような継承関係を持っています。

mytools_Mri.engine.Entry < 
  (mytools_Mri.engine.EntryBase, mytools_Mri.node.Node)

Entry クラスは二つの親クラスを継承していますが、自身ではメソッドを定義していません。Node クラスは履歴の階層構造を構築するためのものです。EntryBase クラスはそのインスタンスを利用したマクロを書くための重要なメソッドを定義しています。

次の表では Entry クラスのインスンタンス変数を説明します。

VariableTypeDescription
targetobjectインスタンスがラップしている実際の値
inspectedpyunocss.beans.XIntrospectionAccess インターフェース
typepyunocss.reflection.CoreReflection による IDL 型情報
mrimytools_Mri.MRIMRI への参照
code_entrymytools_Mri.cg.CodeEntryコード生成用の情報

次の表に示したメソッドも提供しています。

MethodDescription
get_target()target インスタンス変数を利用してください
has_interface(name)ターゲットが指定したインターフェースをエクスポートしているかどうか調べます。
supports_service(name)ターゲットが指定したサービスをサポートしているかどうか調べます。
append(obj)obj をリストに追加します。このメソッドは MRI インスタンスの create_sequence メソッドで作成されたエントリのみで動作します

ページ Edit

MRIUi の pages インスタンス変数は mytools_Mri.pages.InfoUi または GridUi クラスのインスタンスを保持しています。どちらの値かどうかはウィンドウに表示されているコントロールに依存します。これらのクラスの継承構造は少し複雑です。次に示す継承構造のクラスは全て mytools_Mri.ui.pages モジュールで定義されています。

InfoUi < (Ui, PageStatus, 
   Pages < PagesBase)
GridUi < (Ui, PageStatus, 
   GridPages < GridPagesBase < PagesBase)

Ui クラスは型や実装名、検索と履歴のコントロールへのアクセスを提供します。次の表に Ui クラスで定義されている重要なメソッドを示します。

MethodDescription
get_type_name()型名を返します
set_type_name(name)型名を設定します
get_imple_name()実装名を返します
set_imple_name(name)実装名を設定します
get_code()現在のコードのテキストを返します
set_code(txt)コードのテキストを設定します

PageStatus クラスは情報ページのステータスを保持しています。

PagesBase クラスはその子クラスで定義するべきクラスを定義しており、情報のコントロールへのアクセスを提供します。これらのメソッドは index を引数に取ります。これらの値は Properties: 0, Methods: 1, Interfaces: 2, Services 3 です。

MethodDescription
get_selected(index=None)選択されているテキストを返します
get_current_line(index=None)現在の行のテキストを返します
get_first_word(index=None)現在の行の最初の単語を返します
activate(index)アクティブなページを設定します
get_active()アクティブなページを設定します
search(search_text, index=None)検索します

これらのコントロールの内容は最初にアクティブになったときに更新されることに注意してください。

MRI ウィンドウのコントローラ Edit

MRI はウィンドウのフレームに接続される独自のコントローラを利用しています。コントローラ自身は Python で書かれており、MRI インスタンスへのアクセスを可能にします。全ての MRI ウィンドウはデスクトップのフレームに登録されており、css.frame.XFrame インターフェースからアクセスできます。

def do_something_with(mri):
    # デスクトップから MRI ウィンドウを探す
    found = None
    frames = mri.ui.desktop.getFrames()
    for i in range(frames.getCount()):
        frame = frames.getByIndex(i)
        if frame.getTitle() == "MRI":
            found = frame
            break
    if found:
        controller = found.getController()
        # もうひとつの MRI の MRIUi インスタンス
        ui = controller.ui
        mri2 = ui.main # instance of mytools_Mri.MRI

この方法はオフィースの同じ Python インスタンスで動作している全ての Python コードで利用できます。bundle.py - Diff マクロはこの方法で他の MRI からターゲットエントリを取得します。

MRI ウィンドウの構造 Edit

MRI ウィンドウの構造を示します。

frame: css.frame.Frame
  - Controller: mytools_Mri.ui.controller.Controller
    - Model: always None
  - Container Window: general window
  - Component Window: css.awt.UnoControlContainer
    - splitter: does not added in the container
    - label_status: FixedText keeps status text.
    - edit_type: Edit shows type information.
    - edit_in: Edit shows implementation name.
    - list_hist: List of history.
    - btn_back: Back button.
    - btn_forward: Forward button.
    - edit_search: Search edit.
    - btn_search: Search Button.
    - btn_tree: Open tree button.
    - btn_index_acc: Index button.
    - btn_name_acc: Name button.
    - subcont: css.awt.UnoControlContainer (without tab)
      - info_0: 
      - info_1: 
      - info_2: 
      - info_3: 
   - subcont: css.awt.UnoControlContainer (with tab)
      - page_0
        - info_0
      - page_1
        - info_1
      - page_2
        - info_2
      - page_3
        - info_3

subcont の構造はタブコントロールを利用しているかどうかで変化します。MRIUi クラスの pages インスタンスがコントロールへの簡単なアクセス方法を提供しています。

Edit

拡張機能パッケージの Macros ディレクトリにある bundle.py ファイルにいくつか例があります。

例1 Edit

以下の短いコードで現在の図形描写コレクションから子図形描写オブジェクトをインスペクトします。

def inspect_shapes(mri):
    """ Inspect Shapes
        Inspect all shapes in this container. """
    shape = mri.current # current target
    # check current shape is correct type
    if shape.supports_service("com.sun.star.drawing.ShapeCollection"):
        # getCount call is not collected to code
        for i in range(shape.target.getCount()):
             child = shape.getByIndex(i)

Entry クラスの supports_service メソッドはそのターゲットが正しい型かどうかを調べるために利用できます。ターゲットが css.lang.XServiceInfo インターフェースをサポートしていなければ False が返ります。型が無効なことをユーザーに示したければ、例外を raise するか mri.message メソッドでメッセージを表示してください。

例2 Edit

Entry インスタンスは実際の値をラップしています。ターゲットの実際の値が必要なら target インスタンス変数を参照してください。

def show_property_value(mri):
    current = mri.current
    ui = mri.ui
    ui.message(current.target)

Reload   New Lower page making Edit Freeze Diff Upload Copy Rename   Front page List of pages Search Recent changes Backup   Help   RSS of recent changes