-
Notifications
You must be signed in to change notification settings - Fork 7
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.
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.
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.
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.
Macros have to be written in Python language and stored with ".py" file extension under user's repository. See above section about repositories.
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".
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. """
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
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()
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
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.
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.
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. |
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.
MRI keeps css.uno.XComponentContext interface in ctx instance variable. It can be used to make new instances of UNO services.
MRI keeps current target in current instance variable, which is instance of mytools_Mri.engine.Entry class.
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))
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)
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. |
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)
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)
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 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)
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. |
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)
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. |
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
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
There are examples in bundle.py placed under Macros directory of the extension package.
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 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)