* マクロ [#b1ef2b82]

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

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

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

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

#contents
** マクロメニュー [#ma741df6]
MRI ウィンドウを開くと Macros メニューが MRI ウィンドウのメインメニューにあります。このメニューの項目は動的に作成されます。

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

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

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

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

マクロが ZIP、tar.gz、tar.bz2 アーカイブにまとめられている場合にはインポート機能がアーカイブを解凍してアーカイブの内容をインポートします。アーカイブ中に README ファイルがある場合にはアーカイブの内容をインポートするまえに README ファイルの内容をライセンスダイアログに表示します。
** マクロメニュー項目のリロード [#o88667a9]
Macros メニューは動的に作成されますが、一度読み込んだ後は MRI を再起動しなければ変更されません。マクロに変更があった場合にはリロードしてください。Macros - bundle.py - Reload Macros Menu を選択してください。マクロの項目を削除して再読込フラグを有効にします。
** ファイルの種類 [#efb58f98]
マクロは Python で書き、ファイルの拡張子 ".py" をつけてユーザーのリポジトリに保存してください。リポジトリに関しては前のセクションを参照してください。
** マクロの種類 [#c0856cdb]
MRI で実行できるのマクロには二種類あります。一つ目は普通のマクロで、MRI を拡張するためのものです。もうひとつのものはアクションマクロです。これは API 呼び出しがコードとして記録されます。

*** 普通のマクロ [#l114e69b]
普通のマクロの簡単な例を示します。

 # # 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
*** アクションマクロ [#n24b5f51]

アクションマクロは 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 オブジェクト [#kd111181]
全てのマクロは現在の MRI インスタンスを最初の引数として受け取ります。この引数の値は MRI のコアプログラムです。MRI のインスタンスはユーザーインターフェースをサポートしていませんが、ui インスタンス変数から現在のユーザーインターフェースにアクセスできます。
*** インスタンス変数 [#aa1f86f5]
MRI インスタンスは様々な値をインスタンス変数に保持しています。

|Variable|Type|Description|h
|ctx|pyuno|コンポーネントコンテキスト|
|config|mytools_Mri.config.Config|設定|
|web|mytools_Mri.web.IDL|ウェブページなどをウェブブラウザで開く|
|engine|mytools_Mri.engine.Engine|イントロスペクションとリフレクション|
|history|mytools_Mri.node.Root|履歴の階層構造|
|current|mytools_Mri.engine.Entry|現在のエントリ|
|cg|mytools_Mri.cg.CodeGenerator|コードジェネレータを管理|
|mode|bool|ノーマルモードのとき True、False ではマクロモード|
|open_new|bool|True のとき次のターゲーットを新規ウィンドウで開きます|
|macros|mytools_Mri.macros.Macros|マクロを管理|
|ui|mytools_Mri.ui.MRIUi|ユーザーインターフェース|

*** メソッド [#t90bff33]
*** ComponentContext [#ebcc7b8c]

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

MRI は現在のターゲーットを current インスタンス変数に保持しています。この値は mytools_Mri.engine.Entry クラスのインスタンスです。
*** Engine オブジェクト [#lfc167ae]
エンジンは 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 オブジェクト [#cfcfc1a5]
ウェブオブジェクトは設定で指定されたウェブブラウザを開く手助けをしてくれます。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 オブジェクト [#x0756b95]
mytools_Mri.ui.MRIUi クラスは MRI のウィンドウを管理します。

|Variable|Type|Description|h
|ctx|pyuno|コンポーネントコンテキスト|
|main|mytools_Mri.MRI|MRI インスタンス|
|desktop|pyuno|デスクトップインスタンス|
|listeners|dict|リスナー|
|property_mode|bool|True のときプロパティを取得。False で設定|
|tree|mytools_Mri.ui.pages.HistoryTreeUi|ツリー構造の履歴|
|dlgs|mytools_Mri.ui.dialogs.Dialogs|ダイアログを提供します|
|status_eraser|threading.Timer|ステータス文字列を消去するタイマー|
|frame|pyuno|MRI ウィンドウの入ったフレーム|
|cont|pyuno|コンテナウィンドウ|
|pages|mytools_Mri.ui.pages.InfoUi/GridUi|コントロールへのアクセスを提供|
*** ダイアログ [#d02d0f73]
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):
** エントリ [#q45546b3]

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

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

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

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

|Variable|Type|Description|h
|target|object|インスタンスがラップしている実際の値|
|inspected|pyuno|css.beans.XIntrospectionAccess インターフェース|
|type|pyuno|css.reflection.CoreReflection による IDL 型情報|
|mri|mytools_Mri.MRI|MRI への参照|
|code_entry|mytools_Mri.cg.CodeEntry|コード生成用の情報|

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

|Method|Description|h
|get_target()|target インスタンス変数を利用してください|
|has_interface(name)|ターゲットが指定したインターフェースをエクスポートしているかどうか調べます。|
|supports_service(name)|ターゲットが指定したサービスをサポートしているかどうか調べます。|
|append(obj)|obj をリストに追加します。このメソッドは MRI インスタンスの create_sequence メソッドで作成されたエントリのみで動作します|
** ページ [#a2272a6d]
MRIUi の pages インスタンス変数は mytools_Mri.pages.InfoUi または GridUi クラスのインスタンスを保持しています。どちらの値かどうかはウィンドウに表示されているコントロールに依存します。これらのクラスの継承構造は少し複雑です。次に示す継承構造のクラスは全て mytools_Mri.ui.pages モジュールで定義されています。

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

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

|Method|Description|h
|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 です。

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

これらのコントロールの内容は最初にアクティブになったときに更新されることに注意してください。
** MRI ウィンドウのコントローラ [#n06feac2]
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 ウィンドウの構造 [#b3197135]
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 インスタンスがコントロールへの簡単なアクセス方法を提供しています。
** 例 [#i8ddd1ea]
拡張機能パッケージの Macros ディレクトリにある bundle.py ファイルにいくつか例があります。

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

 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 [#zd9dabef]
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