Input/Output to Screen with Python
TDF LibreOffice Document Liberation Project Community Blogs Weblate Nextcloud Redmine Ask LibreOffice Donate
Python standard console output is not available when running Python macros from
LibreOffice Basic proposes InputBox(), Msgbox() and Print() screen I/O functions. Python alternatives exist that rely either on LibreOffice API Abstract Windowing Toolkit, either on msgbox.py LibreOffice shared module, either on Python to Basic function calls. Proposed method signatures in the coming examples are intentionally close to that of Basic. The API Scripting Framework can be used to perform Basic, BeanShell, JavaScript and Python inter-languages function calls.
Screen InputBox() function
Cloning InputBox() Basic function can be achieved using LibreOffice API calls or Python to Basic inter-languages calls.
Using Application Programming Interface (API)
There is no InputBox() function provided for Python. The following example code is borrowed from Transfer from Basic to Python. The script builds the complete dialog plus its controls using the API abstract windowing toolkit. An instance of com.sun.star.awt.UnoControlDialogModel[1] service gets created, and various com.sun.star.awt.UnoControlxxxModel controls are added. com.sun.star.awt.Toolkit[2] service displays the dialog as com.sun.star.frame.Desktop[3] environment locates the current window.
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 optional dialog position in twips
@param y optional dialog position in twips
@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
Example:
reply = InputBox("Please input some value", "Input", "Default value")
Using X-Scripting
That mechanism is described in Input/Output to Screen help page. screen_io.InputBox() function signature is identical to that of LibreOffice Basic InputBox() function.
ScriptForge Python module calls Basic routines in order to provide InputBox, MsgBox and Print functions that exhibit identical method signatures to that of LibreOffice Basic. See …/program/scriptforge.py Python module in LibreOffice installation directory for source code insights.
MsgBox() function
MsgBox() function alternatives exist using either LibreOffice API, either msgbox.py module or Python to Basic calls. Available features differ depending on the solution. They are summarized herewith:
features | using the API | using msgbox.py | using x-Scripting |
---|---|---|---|
Auto-rendering | yes | no | yes |
Bundled module | yes | yes | no |
icons | yes | no | yes |
Dialog types | pre-defined | no | pre-defined |
Dialog buttons | pre-defined | user-defined | pre-defined |
Default button | yes | 1st button | yes |
Closeable dialog | yes | no | yes |
Sizable dialog | no | yes | no |
Return | button value | button text | button value |
Using the API
The message dialog is performed by calling the createMessageBox method of the com.sun.star.awt.Toolkit service. A very similar example is present in Python Guide - Useful functions page. The Developer's Guide illustrates that same approach using Java.
# -*- coding: utf-8 -*-
import uno
from com.sun.star.awt.MessageBoxType import \
MESSAGEBOX, INFOBOX, ERRORBOX, WARNINGBOX, QUERYBOX
from com.sun.star.awt.MessageBoxButtons import \
BUTTONS_OK, BUTTONS_OK_CANCEL, BUTTONS_YES_NO, \
BUTTONS_YES_NO_CANCEL, BUTTONS_RETRY_CANCEL, \
BUTTONS_ABORT_IGNORE_RETRY
from com.sun.star.awt.MessageBoxButtons import \
DEFAULT_BUTTON_OK, DEFAULT_BUTTON_CANCEL, DEFAULT_BUTTON_RETRY, \
DEFAULT_BUTTON_YES, DEFAULT_BUTTON_NO, DEFAULT_BUTTON_IGNORE
from com.sun.star.awt.MessageBoxResults import \
CANCEL, OK, YES, NO, RETRY, IGNORE
def MsgBox(prompt: str, title: str, boxtype=MESSAGEBOX,
buttons=BUTTONS_OK, windowPeer=None) -> int:
"""return value of MessageBox selected button"""
''' adapted from 'apso_utils' by Jean-Marc Zambon
https://extensions.libreoffice.org/extensions/apso-alternative-script-organizer-for-python'''
tk = _createUnoService("com.sun.star.awt.Toolkit")
if not windowPeer:
desktop = _createUnoService("com.sun.star.frame.Desktop")
frame = desktop.ActiveFrame
if frame.ActiveFrame: # top window is a subdocument
frame = frame.ActiveFrame
windowPeer = frame.ComponentWindow
box = tk.createMessageBox(windowPeer, boxtype, buttons, title, prompt)
return box.execute()
def _createUnoService(service, ctx=None, args = None):
''' from 'apso_utils' by Jean-Marc Zambon
https://extensions.libreoffice.org/extensions/apso-alternative-script-organizer-for-python'''
if not ctx:
ctx = uno.getComponentContext()
smgr = ctx.getServiceManager()
if ctx and args:
return smgr.createInstanceWithArgumentsAndContext(service, args, ctx)
elif args:
return smgr.createInstanceWithArguments(service, args)
elif ctx:
return smgr.createInstanceWithContext(service, ctx)
else:
return smgr.createInstance(service)
Examples:
def desk_msg():
MsgBox('no active document present', 'Desktop only')
def doc_msg():
""" Adapted from 'Dialogue message étendu' by B.Marcelly
https://forum.openoffice.org/fr/forum/viewtopic.php?f=15&t=47603 """
doc = XSCRIPTCONTEXT.getDocument()
parentwin = doc.CurrentController.Frame.ContainerWindow
res = YES
while res != NO:
s = "Do you want to continue?"
t = "A message from Python"
res = MsgBox(s, t, QUERYBOX,
BUTTONS_YES_NO_CANCEL + DEFAULT_BUTTON_NO, parentwin)
s = "Response : " +str(res)
MsgBox(s, t, INFOBOX, windowPeer=parentwin)
def long_msg():
"""Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut semper mi sit amet turpis sollicitudin blandit. Aliquam ligula ex, maximus at sapien et, tristique lacinia justo. Sed venenatis tincidunt massa sed imperdiet. Suspendisse vitae odio viverra, euismod tellus ac, vestibulum sapien. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ultrices ac enim at sodales. Pellentesque sollicitudin convallis lacinia. Ut hendrerit vel augue vel malesuada. Aenean ut est a neque sodales porttitor sit amet at magna. Duis tristique dolor quis nunc elementum dapibus. Curabitur dapibus aliquam condimentum. Nam porta, quam vel porta posuere, eros lectus aliquet eros, sed eleifend magna sem sed metus. Quisque fermentum maximus porta. Vestibulum feugiat neque malesuada purus condimentum convallis. Integer leo justo, congue in rutrum vel, dictum et tortor. Fusce eget iaculis sapien. Donec imperdiet ut lacus ac semper. Nulla facilisi. Ut efficitur, nulla at pellentesque luctus, ligula est aliquet metus, scelerisque condimentum erat nunc eu enim. Pellentesque mattis augue vel dictum accumsan. Ut convallis vestibulum ipsum, sit amet blandit nisi rutrum sit amet. Vestibulum non lacus eu nunc luctus dignissim. Maecenas tincidunt enim vitae hendrerit iaculis. Nullam lacinia massa quis enim venenatis vulputate nec id nibh. Aliquam consequat dignissim interdum. Proin consectetur sem sit amet dolor lobortis ullamcorper ac vitae odio.
Integer aliquam, arcu condimentum venenatis lobortis, felis nisl ullamcorper elit, eu luctus ligula mi sit amet ante. Donec eu ante facilisis, tempor metus vitae, dignissim libero. Praesent euismod varius libero ac feugiat. Maecenas tempor lorem eget venenatis vehicula."""
reply = MsgBox(long_msg.__doc__, 'Lorem ipsum …', WARNINGBOX,
BUTTONS_ABORT_IGNORE_RETRY + DEFAULT_BUTTON_OK)
MsgBox( reply, 'response', ERRORBOX)
g_exportedScripts = desk_msg, doc_msg, long_msg
Using msgbox.py
This module comes bundled with LibreOffice. Use it to build custom message dialogs that require user-defined buttons. Return value is that of the selected button. Dialog closing is inhibited. Its MsgBox() class method signatures are:
- MsgBox(ctx: “Component context”)
- addButton(caption: str)
- renderFromBoxSize(size: num = 150)
- renderFromButtonsSize(size: int = 50)
- show(message: str, decoration: int , title: str): str
Examples:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import uno, unohelper # PyUno itself
from msgbox import MsgBox # LibreOffice shared module
def large_msg():
"""
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut semper mi sit amet turpis sollicitudin blandit. Aliquam ligula ex, maximus at sapien et, tristique lacinia justo. Sed venenatis tincidunt massa sed imperdiet. Suspendisse vitae odio viverra, euismod tellus ac, vestibulum sapien. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ultrices ac enim at sodales. Pellentesque sollicitudin convallis lacinia. Ut hendrerit vel augue vel malesuada. Aenean ut est a neque sodales porttitor sit amet at magna. Duis tristique dolor quis nunc elementum dapibus. Curabitur dapibus aliquam condimentum. Nam porta, quam vel porta posuere, eros lectus aliquet eros, sed eleifend magna sem sed metus. Quisque fermentum maximus porta. Vestibulum feugiat neque malesuada purus condimentum convallis. Integer leo justo, congue in rutrum vel, dictum et tortor. Fusce eget iaculis sapien. Donec imperdiet ut lacus ac semper. Nulla facilisi. Ut efficitur, nulla at pellentesque luctus, ligula est aliquet metus, scelerisque condimentum erat nunc eu enim. Pellentesque mattis augue vel dictum accumsan. Ut convallis vestibulum ipsum, sit amet blandit nisi rutrum sit amet. Vestibulum non lacus eu nunc luctus dignissim. Maecenas tincidunt enim vitae hendrerit iaculis. Nullam lacinia massa quis enim venenatis vulputate nec id nibh. Aliquam consequat dignissim interdum. Proin consectetur sem sit amet dolor lobortis ullamcorper ac vitae odio.
Integer aliquam, arcu condimentum venenatis lobortis, felis nisl ullamcorper elit, eu luctus ligula mi sit amet ante. Donec eu ante facilisis, tempor metus vitae, dignissim libero. Praesent euismod varius libero ac feugiat. Maecenas tempor lorem eget venenatis vehicula."""
myBox = MsgBox(uno.getComponentContext())
myBox.addButton("Yes")
myBox.addButton("No")
myBox.addButton("May be")
myBox.renderFromBoxSize(450)
#myBox.numberOfLines = 8
_msg( myBox.show(large_msg.__doc__ + chr(10)+chr(10)+"Do you agree ?",0,"That message is too long. Please increase the box size!"))
def short_msg():
myBox = MsgBox(XSCRIPTCONTEXT.getComponentContext())
myBox.addButton("oK")
myBox.renderFromButtonSize()
myBox.numberOflines = 2
_msg( myBox.show("A small message",0,"Dialog title"))
def _msg(prompt='', title=''):
mb = MsgBox(uno.getComponentContext())
mb.addButton('Howdy')
mb.show(prompt,0,title)
g_exportedScripts = large_msg, short_msg
Using X-Scripting
That mechanism is described in Input/Output to Screen help page. screen_io.MsgBox() function signature is identical to that of LibreOffice Basic MsgBox() function or statement.
ScriptForge Python module calls Basic routines in order to provide InputBox, MsgBox and Print functions that exhibit identical method signatures to that of LibreOffice Basic. See …/program/scriptforge.py Python module in LibreOffice installation directory for source code insights.
Print() display function
Cloning Print() function can be achieved with Python to Basic inter-languages calls or using msgbox.py Python module.
Using msgbox.py
This module comes bundled with LibreOffice. Use it to build custom message dialogs that require user-defined buttons. Return value is that of the selected button. Dialog closing is inhibited. Its MsgBox() class method signatures are:
- MsgBox(ctx: “Component context”)
- addButton(caption: str)
- renderFromBoxSize(size: num = 150)
- renderFromButtonsSize(size: int = 50)
- show(message: str, decoration: int , title: str): str
Examples:
import uno
from msgbox import MsgBox
def prt():
Print( 'prolog', 'test ', 'epilog', sep="\n")
def Print(*args, sep=''):
""" Print arguments using given separator """
mb = MsgBox(uno.getComponentContext())
mb.addButton('Ok')
mb.addButton('Cancel')
prompt = ''
for arg in args:
prompt = prompt+arg+sep
return mb.show(prompt,0,'Warning')
g_exportedScripts = prt,
Using X-Scripting
That mechanism is described in Input/Output to Screen help page. screen_io.Print() function signature is intentionally close to that of LibreOffice Basic Print statement.
ScriptForge Python module calls Basic routines in order to provide InputBox, MsgBox and Print functions that exhibit identical method signatures to that of LibreOffice Basic. See …/program/scriptforge.py Python module in LibreOffice installation directory for source code insights.
ODT file to run scripts
Notes
Pypsum is an interface to lipsum.com written in Python.