Input/Output to Screen with Python

    From The Document Foundation Wiki


    Python standard console output is not available when running Python macros from Tools ▸ Macros ▸ Run Macro… LibreOffice menu. Presenting the output of a module requires the Python interactive console. Features such as input(), print(), repr() and str() are available from the Python shell.

    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.

    Note pin.svg

    Note:
    User interactions are devoted to LibreOffice dialog editor. The following examples exhibit little user interface benefits, therefore input/output to screen with Python is to be limited to development or test situations.

    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.


    Tip.svg

    Tip:
    Other message box examples using BeanShell, Java, JavaScript or Python can be obtained from Display a message box[4] thread.


    # -*- 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

    Note pin.svg

    Note:
    Download ODT file with macros included

    Notes

    Lorem Ipsum - All the facts

    Pypsum is an interface to lipsum.com written in Python.