Transfer from Basic to Python
Translation Español
If you feel some displeasure at StarBasic/OpenOffice Basic or you meet requirement of custom UNO component, now it is the time to transfer coding environment from Basic to Python.
In StarBasic, there are some runtime function provides shortcut to do something. But using Python-UNO bridge, you have to do such task by API ways.
Let's start with Python's tutorial and return back to here when you have learned a bit about Python language.
Contents
- 1 Macro Location
- 2 Macro Editor
- 3 Short Example
- 4 Usable Modules
- 5 Script Context
- 6 Component Context
- 7 Service Manager
- 8 Service Instance
- 9 Service with Constructor
- 10 Current Document
- 11 Desktop
- 12 Struct Instance
- 13 Enum
- 14 Constants
- 15 Sequence
- 16 Boolean
- 17 String
- 18 Char
- 19 Type
- 20 Byte Sequence
- 21 Exception
- 22 Empty Value
- 23 Listener and Interface
- 24 Containers
- 25 URL and System Path
- 26 Arguments and Return Value
- 27 Functions to be Executed
- 28 Importing Modules
- 29 Dialog
- 30 Message Box
- 31 Input Box
- 32 Executing Macros from Toolbar Buttons
Macro Location
On the current version, script organizer for Python do not provide the way to manage script files. So you have to manage yourself. Macros written in Python should be placed in the specific directory under your user's profile, shared profile or script directory of your document.
The python directory (in all lowercase) should be created in Scripts directory and you can put your python scripts that contain python code.
path_to_directory/Scripts/python/your_file.py
You can make subdirectories to put macro files in them under Scripts/python directory.
You have to add "py" file extension to your file name.
Macro Editor
There is no built-in editor dedicated to code in Python. But you can choose your favorite text editor application to edit your Python script.
This is discussed in another page.
Short Example
Here is a small example written in Python.
def hello(): XSCRIPTCONTEXT.getDocument().getText().setString("Hello!")
Each callable function is the executable unit of Python macro (you can choose which function should be listed in the GUI, see other document).
You can access the current document through getDocument method of XSCRIPTCONTEXT variable of the module. And it changes the document content to "Hello!" of the Writer document.
Usable Modules
import uno import unohelper
Script Context
StarBasic is embedded runtime and it always works with living instance of the office. So the context is always the match with the office's one. But Python-UNO bridge provides the way to work as RPC client. At that time, remote Python instance does have different context from the office instance. Therefore you have to use correct component context to tell to the multi component factory to instantiate a service.
In StarBasic, you have no matching object provided.
In Python macro, you have to use XSCRIPTCONTEXT module variable that provides com.sun.star.script.provider.XScriptContext interface.
- com.sun.star.frame.XModel getDocument()
- Returns current document object.
- com.sun.star.document.XScriptInvocationContext getInvocationContext()
- Returns invocation dependent object.
- com.sun.star.frame.XDesktop getDesktop()
- Returns instance of com.sun.star.frame.Desktop service.
- com.sun.star.uno.XComponentContext getComponentContext()
- Returns current component context that is used to tell the which context is.
Please keep in mind, XSCRIPTCONTEXT variable is defined in module level and it is not provided to imported modules from your script.
Component Context
In StarBasic, you can get component context as follows:
ctx = GetDefaultContext()
But you do not need to access to it until you need to get singletons.
In Python, you can get access to the component context through the script context.
ctx = XSCRIPTCONTEXT.getComponentContext()
You need this value to instantiate services.
Service Manager
In StarBasic, you can get service manager as follows:
smgr = GetProcessServiceManager()
But not so many people having to access to it.
In Python, this instance is most important value to instantiate services to work with the office APIs.
ctx = XSCRIPTCONTEXT.getComponentContext() smgr = ctx.getServiceManager()
You need both the component context and the service manager to instantiate services.
Service Instance
In StarBasic, you can instantiate services by built-in function as follows:
sfa = CreateUnoService("com.sun.star.ucb.SimpleFileAccess")
or with initialize arguments:
arg = com.sun.star.ui.dialogs.TemplateDescription.FILESAVE_SIMPLE file_picker = CreateUnoServiceWithArguments("com.sun.star.ui.dialogs.FilePicker", Array(arg))
In Python, you have to work with com.sun.star.lang.XMultiComponentFactory interface as follows:
ctx = XSCRIPTCONTEXT.getComponentContext() smgr = ctx.getServiceManager() sfa = smgr.createInstanceWithContext("com.sun.star.ucb.SimpleFileAccess", ctx)
If you need to initialize the instance, use createInstanceWithArgumentsAndContext method:
from com.sun.star.ui.dialogs.TemplateDescription import FILESAVE_SIMPLE file_picker = smgr.createInstanceWithArgumentsAndContext("com.sun.star.ui.dialogs.FilePicker", (FILESAVE_SIMPLE,) ctx)
or initialize after the instantiation:
file_picker = smgr.createInstanceWithContext("com.sun.star.ui.dialogs.FilePicker", ctx) file_picker.initialize((FILESAVE_SIMPLE,))
Service with Constructor
In StarBasic, you can call service construct from its module:
shell_execute = com.sun.star.system.SystemShellExecute.create()
In Python, you have to instantiate it with XMultiComponentFactory::createInstanceWithArgumentsAndContext method with initial arguments or instantiate after the instantiation.
The constructor calling does type checking before to pass arguments to createInstanceWithArgumentsAndContext method.
Current Document
In StarBasic, ThisComponent runtime function provides access to the current document:
doc = ThisComponent
Its return value is bound to the macro location. If the macro stored in a document, the result of ThisComponent is the document that the storage of the macro belongs to. And if your macro is stored in application wide, the returned value is the document model of currently active frame, this is the same result taken from StarDesktop.getCurrentComponent().
In Python, you can access to the current document through the script context:
doc = XSCRIPTCONTEXT.getDocument()
If your macro is embedded in the document, the document model is match with the document that the macro stored in. If your macro is stored in user or shared location, the document object is from active frame.
Desktop
In StarBasic, StarDesktop runtime function is provided:
desktop = StarDesktop()
In Python, you can get access to the desktop through the script context:
desktop = XSCRIPTCONTEXT.getDesktop()
Struct Instance
In Basic, instance of struct can be instantiated in two ways:
Dim a As New com.sun.star.awt.Point a = CreateUnoStruct("com.sun.star.awt.Point")
With Dim statement and New keyword, you can instantiate a struct or array of structs. Or CreateUnoStruct method provides the way to instantiate a struct at runtime. You can not initialize the instance at the creation, you have to set its value of members.
In Python, you can use the following ways to instantiate a struct. Import struct class and call it.
from com.sun.star.awt import Point a = Point() # instantiate with default values, X=0, Y=0 b = Point(100, 200) # initialize with initial values, X=100, Y=200
Calling the class to create new instance of the struct, you can empty its arguments or you have to pass values for all fields. In other words, you can not pass insufficient number of arguments to initialize. And its order should be match with definition of the struct in its IDL. For example, instance of struct b having X=100 and Y=200 in the above piece of code.
You can initialize without to import the class of your target struct with uno.createUnoStruct function as follows:
import uno a = uno.createUnoStruct("com.sun.star.awt.Point") b = uno.createUnoStruct("com.sun.star.awt.Point", 100, 200)
This makes the same result with the above example. The first parameter of the createUnoStruct method is the name of the struct to initialize. The following arguments are initial values for new instance.
Enum
In Basic, you can access to the module of the enum as follows:
ITALIC = com.sun.star.awt.FontSlant.ITALIC
In Python, the following ways can be used:
import uno from com.sun.star.awt.FontSlant import ITALIC ITALIC = uno.getConstantByName("com.sun.star.awt.FontSlant.ITALIC") ITALIC = uno.Enum("com.sun.star.awt.FontSlant", "ITALIC")
All of the way results the instance of uno.Enum class.
Constants
In StarBasic, you can get access to constants through its module:
BOLD = com.sun.star.awt.FontWeight.BOLD
In Python, the following ways are provided:
import uno from com.sun.star.awt.FontWeight import BOLD BOLD = uno.getConstantByName("com.sun.star.awt.FontWeight.BOLD")
Sequence
The sequence is sequential value of the same type.
In StarBasic, array is used.
In Python, tuple is chosen to represent UNO's sequence. Note, list is not allowed to pass as sequence value.
Boolean
In Python, True or False.
String
Python's string can contain over 64K bytes.
If you have to write non Ascii 7 bit characters in your script, write magic comment at the head of your file. This is standard Python instructions.
# -*- coding: utf_8 -*-
Please read Python's documentation for more detail.
You have to use unicode string to write some unicode characters in Python 2.X. And strings coming from UNO is decoded in unicode in Python 2.X.
Char
There is no dedicated value for char type in StarBasic.
In Python, uno.Char class is defined for char type.
import uno c = uno.Char("a")
Type
"Type" type is meta type of UNO that represents type of UNO. Since 3.4, if you pass string value to method as an argument that should be type, the bridge of Basic read it as type.
oMap = com.sun.star.container.EnumerableMap.create("string", "string")
In Python, the following ways can be used to create new valu of type.
import uno t = uno.getTypeByName("string") t = uno.Type("string", uno.Enum("com.sun.star.uno.TypeClass", "STRING"))
Byte Sequence
The byte sequence is sequence of byte type.
In Basic, array of byte is used to represent it.
In Python, it is represented by str wrapped by uno.ByteSequence class. If you takes some byte sequences from UNO, they are the instance of uno.ByteSequence. If you need to get real value of them, refer its value instance variable.
Exception
In StarBasic, you get thrown exception as some error. And On Error statement is used to catch it.
Sub ErrorExample On Error GoTo Handler ' ... error Exit Sub Handler: End Sub
And you can not throw any exceptions from StarBasic.
In Python, the exception thrown in UNO world can be treated as normal Python's exception. Here is an example:
from com.sun.star.container import IndexOutOfBoundsException try: obj.getByIndex(100) # raises IndexOutOfBoundsException except IndexOutOfBoundsException as e: print(e)
If getByIndex method raises IndexOutOfBoundsException, it can be caught in except statement because all exceptions inherit Python's Exception class.
And also you can throw UNO's exception from your Python code as follows:
from com.sun.star.uno import RuntimeException raise RuntimeException("Some message", None)
Empty Value
In Basic, there are some situations to meet variables that they do not contain any value (this is not correct). Null, empty, missing, nothing and so on.
In Python, None is used. If a method defined as void return value in its IDL, it results None if you call it. If you need to pass invalid interface as an argument for the method that takes some interface, pass None for it. The result of this behavior is fully dependent to the implementation of the method.
Listener and Interface
In StarBasic, you can create new listener using CreateUnoListener runtime function with some subroutines or functions.
Sub Add d = CreateUnoDialog(DialogLibraries.Standard.Dialog1) listener = CreateUnoListener("ActionListener", "com.sun.star.awt.XActionListener") d.getControl("CommandButton1").addActionListener(listener) d.execute() d.dispose() End Sub Sub ActionListener_actionPerformed(ev) End Sub Sub ActionListener_disposing(ev) End Sub
In Python, you have to define your own class with desired interfaces. With helper class, you can define easily as follows:
import unohelper from com.sun.star.awt import XActionListener class ActionListener(unohelper.Base, XActionListener): def __init__(self): pass def disposing(self, ev): pass def actionPerformed(self, ev): pass
unohelper.Base class provides required interface for UNO components.
Containers
In StarBasic, container object provides the way to access to its contents in sequentially.
In Python, there is no shortcut provided.
If you need to access to elements of indexed container, use range function to generate sequential indexes.
for i in range(container.getCount()): obj = container.getByIndex(i)
URL and System Path
If you work with a file stored in your local file system, you have to get its corresponding URL.
In StarBasic, ConvertToURL runtime function is prepared for this task. And there is ConvertFromURL runtime function for reverse conversion.
In Python, the following functions are defined in uno module for such task.
import uno path = "/home/foo/Documents/file.odt" url = uno.systemPathToFileUrl(path) path = uno.fileUrlToSystemPath(url)
Arguments and Return Value
In StarBasic, mode of the first argument of parseStrict method is "inout" in the following code:
aURL = CreateUnoStruct("com.sun.star.util.URL") aURL.Complete = ".uno:Paste" CreateUnoService("com.sun.star.util.URLTransformer").parseStrict(aURL)
The content of aURL variable is updated after calling the method.
In Python, out mode parameter is returned as part of return value.
from com.sun.star.util import URL aURL = URL() aURL.Complete = ".uno:Paste" dummy, aURL = smgr.createInstanceWithContext("com.sun.star.util.URLTransformer", ctx).parseStrict(aURL) # Definition of com.sun.star.util.XURLTransformer::parseStrict method: # void parseStrict([inout] com.sun.star.util.URL aURL);
If a method has out mode in its parameters, its return is always tuple that contains original return value and values for out parameters.
Here is a potential example:
# boolean getSomeValue([in] string aName, [out] short aNum, [inout] long aNum2); result, num, num2 = obj.getSomeValue("foo", 100, 200)
In the above example, result variable takes original return value, num takes output value for second parameter and num2 takes output value for third parameter. The method takes 100 as the second parameter but it is not used as input of value. No entry in returned tuple for in mode parameter.
Functions to be Executed
In Basic, you can not choose routines to be executed by users.
In Python, define g_exportedScripts variable that contains tuple of callable in your macro file.
def func_a(): pass def func_b(): pass def func_hidden(): pass # not shown in the UI g_exportedScripts = func_a, func_b
In the above code, func_hidden is not shown in execution dialog of macros.
Importing Modules
In Basic, you can call subroutines defined in other modules or other libraries that has been loaded.
BasicLibraries.loadLibrary("Library1") Library1.Foo()
In Python, you can import some modules that can be found in sys.path list. If you want to import your own module placed inside Scripts/python directory, put your module in pythonpath directory nearby your script file.
- Scripts/ - python/ - macro.py - pythonpath/ # this directory is added automatically before your macro executed - your_module.py # this module can be found
When you execute the macro from your script file, the internal executor adds the pythonpath/ directory to sys.path list to be used as one of lookup location.
Please keep in mind, the name of module conflict each other if you have the same named module in the some location, this is the standard python mechanism of importing modules, put your each modules in differently named library to avoid the name conflict.
Dialog
In Basic, there is CreateUnoDialog runtime function to instantiate the dialog.
DialogLibraries.loadLibrary("Standard") dialog = CreateUnoDialog(DialogLibraries.Standard.Dialog1) dialog.execute() dialog.dispose()
In Python, such shortcut function is not provided but you can easily instantiate your dialog using com.sun.star.awt.DialogProvider service.
def dialog_example(): ctx = XSCRIPTCONTEXT.getComponentContext() smgr = ctx.getServiceManager() dp = smgr.createInstanceWithContext("com.sun.star.awt.DialogProvider", ctx) dialog = dp.createDialog("vnd.sun.star.script:Standard.Dialog1?location=user") dialog.execute() dialog.dispose()
Message Box
In Basic, you can use MsgBox runtime function to show some message to the users.
Msgbox "Hello."
In Python, no shortcut function is provided but you can use com.sun.star.awt.XMessageBoxFactory interface through the toolkit.
def messagebox(ctx, parent, message, title, message_type, buttons): """ Show message in message box. """ toolkit = parent.getToolkit() older_imple = check_method_parameter( ctx, "com.sun.star.awt.XMessageBoxFactory", "createMessageBox", 1, "com.sun.star.awt.Rectangle") if older_imple: msgbox = toolkit.createMessageBox( parent, Rectangle(), message_type, buttons, title, message) else: message_type = uno.getConstantByName("com.sun.star.awt.MessageBoxType." + { "messbox": "MESSAGEBOX", "infobox": "INFOBOX", "warningbox": "WARNINGBOX", "errorbox": "ERRORBOX", "querybox": "QUERYBOX"}[message_type]) msgbox = toolkit.createMessageBox( parent, message_type, buttons, title, message) n = msgbox.execute() msgbox.dispose() return n def check_method_parameter(ctx, interface_name, method_name, param_index, param_type): """ Check the method has specific type parameter at the specific position. """ cr = create_service(ctx, "com.sun.star.reflection.CoreReflection") try: idl = cr.forName(interface_name) m = idl.getMethod(method_name) if m: info = m.getParameterInfos()[param_index] return info.aType.getName() == param_type except: pass return False
Input Box
No function provided for Python, you have to make your own one. Here is an example:
def inputbox(message, title="", default="", x=None, y=None): """ Shows dialog with input box. @param message message to show on the dialog @param title window title @param default default value @param x dialog positio in twips, pass y also @param y dialog position in twips, pass y also @return string if OK button pushed, otherwise zero length string """ WIDTH = 600 HORI_MARGIN = VERT_MARGIN = 8 BUTTON_WIDTH = 100 BUTTON_HEIGHT = 26 HORI_SEP = VERT_SEP = 8 LABEL_HEIGHT = BUTTON_HEIGHT * 2 + 5 EDIT_HEIGHT = 24 HEIGHT = VERT_MARGIN * 2 + LABEL_HEIGHT + VERT_SEP + EDIT_HEIGHT import uno from com.sun.star.awt.PosSize import POS, SIZE, POSSIZE from com.sun.star.awt.PushButtonType import OK, CANCEL from com.sun.star.util.MeasureUnit import TWIP ctx = uno.getComponentContext() def create(name): return ctx.getServiceManager().createInstanceWithContext(name, ctx) dialog = create("com.sun.star.awt.UnoControlDialog") dialog_model = create("com.sun.star.awt.UnoControlDialogModel") dialog.setModel(dialog_model) dialog.setVisible(False) dialog.setTitle(title) dialog.setPosSize(0, 0, WIDTH, HEIGHT, SIZE) def add(name, type, x_, y_, width_, height_, props): model = dialog_model.createInstance("com.sun.star.awt.UnoControl" + type + "Model") dialog_model.insertByName(name, model) control = dialog.getControl(name) control.setPosSize(x_, y_, width_, height_, POSSIZE) for key, value in props.items(): setattr(model, key, value) label_width = WIDTH - BUTTON_WIDTH - HORI_SEP - HORI_MARGIN * 2 add("label", "FixedText", HORI_MARGIN, VERT_MARGIN, label_width, LABEL_HEIGHT, {"Label": str(message), "NoLabel": True}) add("btn_ok", "Button", HORI_MARGIN + label_width + HORI_SEP, VERT_MARGIN, BUTTON_WIDTH, BUTTON_HEIGHT, {"PushButtonType": OK, "DefaultButton": True}) add("btn_cancel", "Button", HORI_MARGIN + label_width + HORI_SEP, VERT_MARGIN + BUTTON_HEIGHT + 5, BUTTON_WIDTH, BUTTON_HEIGHT, {"PushButtonType": CANCEL}) add("edit", "Edit", HORI_MARGIN, LABEL_HEIGHT + VERT_MARGIN + VERT_SEP, WIDTH - HORI_MARGIN * 2, EDIT_HEIGHT, {"Text": str(default)}) frame = create("com.sun.star.frame.Desktop").getCurrentFrame() window = frame.getContainerWindow() if frame else None dialog.createPeer(create("com.sun.star.awt.Toolkit"), window) if not x is None and not y is None: ps = dialog.convertSizeToPixel(uno.createUnoStruct("com.sun.star.awt.Size", x, y), TWIP) _x, _y = ps.Width, ps.Height elif window: ps = window.getPosSize() _x = ps.Width / 2 - WIDTH / 2 _y = ps.Height / 2 - HEIGHT / 2 dialog.setPosSize(_x, _y, 0, 0, POS) edit = dialog.getControl("edit") edit.setSelection(uno.createUnoStruct("com.sun.star.awt.Selection", 0, len(str(default)))) edit.setFocus() ret = edit.getModel().Text if dialog.execute() else "" dialog.dispose() return ret
This function can be called as follows:
inputbox("Please input some value", "Input", "Default value")
Executing Macros from Toolbar Buttons
In Basic, you can call any subroutines or functions through toolbar buttons.
Sub WriteHello() ThisComponent.getText().getEnd().setString("Hello!") End Sub
In Python, you have to define your Python function with an argument (or variable length arguments) to take an argument passed when the function executed through assigned toolbar button.
def writeHello(*args): # writeHello(arg): is also ok but if you want to call this function # by some ways, define your function takes variable arguments. XSCRIPTCONTEXT.getDocument().getText().getEnd().setString("Hello!")