Skip to content
hanya edited this page Jan 26, 2012 · 1 revision

Table of Contents

Macros

MRI has macro function to control MRI itself. They can be written by users in Python.

The macros are allowed to access to all functions of MRI and they might be crash your office. However MRI is not for general use, it does not serious problem.

MRI macro is highly depends on MRI implementation, so it is hard to write your macro without knowledge of the MRI internal structure. In this document, only important parts are described, see source code of the MRI for more detail.

Macros menu entry

Open MRI window, Macros menu entry is there on the main menu of the window. The entries of the menu are created dynamically.

Macros should be stored under following directory.

  • Extension's repository, which is come with MRI.
  • User's repository, it can be set in Tools - Configuration... entry of the main menu.
For user's repository, you can choose any directory which you have access right. MRI suppose to use $(user)/Scripts/python/mri_macros directory or something else.

Import macros

If you have a file which includes MRI macros, there is an easy way to put it into your macros directory. Choose Macros - bundle.py - Import Macros entry in the main menu, selected files are copied to user's directory of MRI macros.

If macro files are archived into ZIP, tar.gz or tar.bz2 archive, import function unpack it and it's contents are imported. And if README file is there in the archive, its content is shown in license dialog to confirm before importing its contents.

Reload macros entries

The Macros menu is created dynamically but it is static after the first loading. If you have changes in your macros repository, try to reload it. Choose Macros - bundle.py - Reload Macros Menu entry. Which erase loaded entries and a flag inhibit to load.

File type of macros

Macros have to be written in Python language and stored with ".py" file extension under user's repository. See above section about repositories.

Types of Macro

There are two kind of macros which can be executed on MRI. First one is simple code which extends MRI itself. Another one is record-able macro, processing by macros on the entries are recorded as generated code. In this document, first type is called "Normal Macro" and second type is called "Action Macro".

Normal Macro

Here is an example of normal macro.

# # hello.py example
# Entries of __all__ are shown in Macros menu.
# The order in the list is kept in the menu and empty items 
# are converted into separator of menu entries.
__all__ = ["hello_world", "", "another_function"]

def hello_world(mri):
    """ Greeting
        Description of this method """
    # The document string of the function is used to specify 
    # its title and description. First line is used for title of it 
    # and second line is used for description of the function.
    mri.message("Hello World!")

def another_function(mri):
    pass

def private_function(mri):
   """ This function is not shown in the list of function. """
Your macro file is loaded as a module by imp.load_source function. Your function should be take an argument, which is mri object. Any MRI functions are accessible though it, see following sections described about them.

The doc string of the function is used for the title and the tool tip text of the entry in the menu or other place.

variable is used to get list of functions exported by the file. In the above example, hello_world and another_function are shown in the sub menu entries of hello.py entry, they are separated with a separator which is specified by the entry of empty string.

Here is an another example working with current target but API calls are not recorded by the code generator.

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

Action Macro

Aciton macro allows to generate code for API calls. It is the same as normal macro except for working with Entry instances. If you work with an entry which does not have its position in the history, register it to the history by generator statement.

Here is an simple example.

def action_macro(mri):
    current = mri.current # current is a Writer document object
    text = current.getText()
    cursor = text.createTextCursor()
Calling getText and createTextCursor methods are recorded automatically and return value of each methods is being entries of the history.

When you work with new instance like a service instance, register it to the history.

def macro_test(mri):
    current = mri.current
    transformer = mri.create_service(
        "com.sun.star.util.URLTransformer")
    yield current, transformer
The generator have to return tuple of a parent entry and an entry to register as history entry. The first element, a parent entry should be registered in the history.

There are some restrictions on action macros and code generation with it. For example, a listener like custom UNO component can not be used with it.

MRI object

All macro takes current MRI instance as first argument, which is core program of MRI. The instance of MRI does not support any user interface, the window of MRI can be accessed though ui instance variable of it.

Instance variables

MRI instance keeps something in its instance variables.

Variable Type Description
ctx pyuno The component context.
config mytools_Mri.config.Config Settings
mytools_Mri.web.IDL Class for web-browser to open web pages.
engine mytools_Mri.engine.Engine Introspection and reflection.
history mytools_Mri.node.Root History hierarch.
current mytools_Mri.engine.Entry Current entry.
cg mytools_Mri.cg.CodeGenerator Manages code generators.
mode bool True for normal mode, otherwise macro mode.
open_new bool If True, next target is opened in new MRI.
macros mytools_Mri.macros.Macros Manages macros.
ui mytools_Mri.ui.MRIUi UI instance.

Methods

The following methods return Entry instance and calling them makes code entry according to the method.

 get_component_context() -> Entry

Returns the component context.

 assign_element(k, value, append=False) -> None

Assign an element to current sequence. This is called by the Entry instance of [] method.

 create_service(name, *args, **kwds) -> Enry | pyuno

Returns new instance of a service specified by its name. If nocode=True is specified, the pyuno object of the service instance is returned.

 create_struct(name, *args, **kwds) -> Entry | pyuno

Returns new instance of a struct specified by its name. If nocode=True is specified, the object of the struct instance is returned.

 create_sequence(type_name, length, var=None) -> Entry

Returns new sequence of with its content type and length.

 declare_variable(type_name, value) -> Entry

Make an entry into the code generated with specific variable name.

ComponentContext

MRI keeps css.uno.XComponentContext interface in ctx instance variable. It can be used to make new instances of UNO services.

Current entry

MRI keeps current target in current instance variable, which is instance of mytools_Mri.engine.Entry class.

Engine object

The engine is the core object to get object information of UNO objects. It uses css.beans.Introspection, css.reflection.CoreReflection and css.reflection.TypeDescription services, implemented in mytools_Mri.engine.Engine class. And the instance of the engine can be taken from engine instance variable of MRI.

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 object

The web object helps you to open web page in user's web browser which specified in the configuration. It is implemented in mytools_Mri.web.IDL class.

def open_url(mri):
    """ Allows to open any URL in your web browser. """
    url = "http://example.com/"
    mri.web.open_url(url)

def open_ref(mri):
    """ Open IDL reference with its name, 
        second argument is optional. """
    idl = "com.sun.star.awt.Rectangle"
    name = "X"
    mri.web.open_idl_reference(idl, name)

MRI UI object

The mytools_Mri.ui.MRIUi class manages the window of MRI.

Variable Type Description
ctx pyuno The component context.
main mytools_Mri.MRI MRI instance.
desktop pyuno The desktop instance.
listeners dict Listener isntances.
property_mode bool True for getting property value otherwise set.
tree mytools_Mri.ui.pages.HistoryTreeUi Sub window for tree history.
dlgs mytools_Mri.ui.dialogs.Dialogs Provides dialogs.
status_eraser threading.Timer To delete status line.
frame pyuno Frame of the MRI window.
cont pyuno Container window of the MRI window.
pages mytools_Mri.ui.pages.InfoUi/GridUi Provides access to contents of controls.

Dialogs

There are some dialog in the mytools_Mri.ui.dialogs.Dialogs class which can be used though dlgs instance variable of MRIUi instance.

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)

Second argument of dialog_select method specifies title of the dialog. In the above case, two Item 1 entries, if you want to know which one is selected, pass True for third argument, it makes return value to index of the selected value. None is returned if canceled.

If you want to get text input by users, use dialog_input method to execute input dialog.

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

There is a dialog which allows to choose an entry from the history.

def history_selector(mri):
    entry = mri.ui.dlgs.history_selector("Select Entry")
    if entry:
        mri.ui.message(entry.name)
Return value of the method is an instance of mytools_Mri.engine.Entry class.

If you want to use dialog box to show something or query for user, message method is provided for this task. Which provides shortcut to css.awt.XMessageBoxFactory, see reference for more information. The definition of the method is like the following:

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

Entry

Entry class is used to keep current and other target to inspect by MRI. For example, the current target can be accessed through current instance variable of MRI instance. Entry class is defined in mytools_Mri.engine module and here is its inheritance structure.

mytools_Mri.engine.Entry < 
  (mytools_Mri.engine.EntryBase, mytools_Mri.node.Node)
Entry class inherits two parent classes but there is not additional method defined itself. Node class provides functions to construct hierarch of history structure. EntryBase provides important methods to write macros with its instances.

The following table describes instance variables of Entry class.

Variable Type Description
target object Real value wrapped by this instance.
inspected pyuno css.beans.XIntrospectionAccess interface.
type pyuno IDL type by css.reflection.CoreReflection.
mri mytools_Mri.MRI Reference to living MRI.
code_entry mytools_Mri.cg.CodeEntry Keeps informations for code generation.

And there are methods provides functions described in the following table:

Method Description
get_target() Use target instance variable to get its real value.
has_interface(name) Check the target exports the interface.
supports_service(name) Check the target supports the service.
append(obj) Append obj to the list, works only on an entry created by create_sequence method of MRI instance.

Pages

The pages instance variable of MRIUi keeps mytools_Mri.pages.InfoUi or GridUi instance depends on the controls shown on the MRI window, which provides access to contents of controls. Their class inheritance is little bit complicated. Here are inheritance structure of them and all classes are defined in mytools_Mri.ui.pages module.

InfoUi < (Ui, PageStatus, 
   Pages < PagesBase)
GridUi < (Ui, PageStatus, 
   GridPages < GridPagesBase < PagesBase)
Ui class provides access to type, implementation name, search edit and history list box controls. The following table describes important methods defined by the Ui class.
Method Description
get_type_name() Returns type name.
set_type_name(name) Sets type name.
get_imple_name() Returns implementation name.
set_imple_name(name) Sets implementation name.
get_code() Get current code text.
set_code(txt) Sets current code text.

PageStatus class manages updated status of information pages.

PagesBase class defines group of methods should be provided by its child class, they provides access to information controls. These methods needs index as an argument. They are bound like Properties: 0, Methods: 1, Interfaces: 2, Services 3.

Method Description
get_selected(index=None) Get selected text.
get_current_line(index=None) Get text of current line.
get_first_word(index=None) Get head word in the current line.
activate(index) Set active page index.
get_active() Get active page index.
search(search_text, index=None) Search inside.
Keep in mind, the content of their controls are updated at first time which is activated.

Controller of MRI Window

MRI uses custom controller which is bound to frame of windows. It is written in Python and it allows to access an instance of MRI. All MRI windows are registerd on the desktop frame and they can be taken though css.frame.XFrame interface.

def do_something_with(mri):
    # find MRI window from the desktop
    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()
        # MRIUi instance of another MRI
        ui = controller.ui
        mri2 = ui.main # instance of mytools_Mri.MRI
This way can be used in any Python code working on the same Python instance of the office. The bundle.py - Diff macro uses this way to get an entry from the another MRI.

Structure of the MRI Window

Here is hierarch of the MRI window.

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
The structure of subcont is changed depending of using tab control on the window. The pages instance variable of the MRIUi class provides access to their content simply.

Examples

There are examples in bundle.py placed under Macros directory of the extension package.

Example 1

Here is a piece of code to inspect child shapes of the current shape collection.

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)
The supports_service method of Entry class is used to check the current target is correct type. If the target does not support css.lang.XServiceInfo interface, False is returned. If you want to warn for user about it, raise an exception or show message with mri.message method.

Example 2

The Entry instance wraps its real value in target instance variable. If you need real value of the target, please refer it.

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