Skip to content

Instantly share code, notes, and snippets.

@ikks
Last active September 13, 2025 02:05
Show Gist options
  • Save ikks/91c8340ed98b9f07a3c0c6cff02860d5 to your computer and use it in GitHub Desktop.
Save ikks/91c8340ed98b9f07a3c0c6cff02860d5 to your computer and use it in GitHub Desktop.
Pythonic LibreOffice Dialog with NumericField
# -*- coding: utf-8 -*-
#!env py
#
# Igor Támara
# This file can be freely distributed under MPL1.0 and derivatives
import uno
import unohelper
from com.sun.star.connection import NoConnectException
from com.sun.star.uno import XComponentContext
from typing import TYPE_CHECKING
from typing import Any
from typing import List
from typing import Tuple
if TYPE_CHECKING:
from com.sun.star.awt import UnoControlDialog
from com.sun.star.awt import UnoControlDialogModel
from com.sun.star.awt import UnoControlModel
from com.sun.star.awt import UnoControlNumericFieldModel
from com.sun.star.lang import ExtToolkit
from com.sun.star.lang import ServiceManager
from com.sun.star.awt.tab import UnoControlTabPageModel
from com.sun.star.awt.tab import UnoControlTabPageContainerModel
def create_widget(
container,
typename: str,
identifier: str,
rect: Tuple[int, int, int, int],
component=None,
add_now: bool = True,
additional_properties: List[Tuple[str, Any]] = None,
):
"""
Creates a control Model from typename, with the identifier, positioned with
widthxheight, when add_now is True, inserts the new control inside component,
if component is None, inserts in the container. States property
values from additional_properties to the new created control.
The new created control is returned.
https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1awt.html
"""
if component is None:
component = container
if typename == "TabPageContainer":
cmpt_type = f"com.sun.star.awt.tab.UnoControl{typename}Model"
else:
cmpt_type = f"com.sun.star.awt.UnoControl{typename}Model"
cmpt: UnoControlModel = container.createInstance(cmpt_type)
cmpt.setPropertyValues(
[
"Name",
"PositionX",
"PositionY",
"Width",
"Height",
],
[
identifier,
rect[0],
rect[1],
rect[2],
rect[3],
],
)
if add_now:
component.insertByName(identifier, cmpt)
if additional_properties:
cmpt.setPropertyValues(*zip(*additional_properties))
return cmpt
class DialogWithTab(unohelper.Base):
def __init__(self, ctx=uno.getComponentContext()):
self.context: XComponentContext = ctx
self.insert_in = []
self.sm: ServiceManager = self.context.ServiceManager
self.toolkit: ExtToolkit = self.sm.createInstanceWithContext(
"com.sun.star.awt.ExtToolkit", self.context
)
self.dc: UnoControlDialog = self.sm.createInstanceWithContext(
"com.sun.star.awt.UnoControlDialog", self.context
)
self.dm: UnoControlDialogModel = create_widget(
self.sm,
"Dialog",
"dlg_options",
(47, 10, 165, 116),
add_now=False,
additional_properties=[
("Closeable", True),
("Moveable", True),
("Title", "My Configuration"),
],
)
self.dc.setModel(self.dm)
dm: UnoControlDialogModel = self.dc.getModel()
self.dc.createPeer(self.toolkit, None)
book: UnoControlTabPageContainerModel = create_widget(
dm, "TabPageContainer", "book", (18, 15, 130, 60)
)
page_ad: UnoControlTabPageModel = book.createTabPage(1)
page_ad.Title = "🔧 Parameters"
book.insertByIndex(0, page_ad)
page_ui: UnoControlTabPageModel = book.createTabPage(2)
page_ui.Title = "👀 UI"
book.insertByIndex(1, page_ui)
self.page_ad = page_ad
ctrl: UnoControlNumericFieldModel = create_widget(
page_ad,
"NumericField",
"int_height",
(66, 12, 54, 13),
add_now=False,
additional_properties=[
("StrictFormat", True),
("DecimalAccuracy", 0),
("Value", 384),
("ValueMin", 384),
("ValueMax", 1024),
("ValueStep", 64),
("Spin", True),
],
)
self.first = ctrl
self.insert_in.append((self.page_ad, self.first))
ctrl: UnoControlNumericFieldModel = create_widget(
dm,
"NumericField",
"int_outer",
(88, 82, 54, 13),
add_now=False,
additional_properties=[
("StrictFormat", True),
("DecimalAccuracy", 0),
("Value", 384),
("ValueMin", 384),
("ValueMax", 1024),
("ValueStep", 64),
("Spin", True),
],
)
self.second = ctrl
self.insert_in.append((self.dm, self.second))
def showDialog(self):
self.dc.setVisible(True)
for pair in self.insert_in:
pair[0].insertByName(pair[1].Name, pair[1])
return self.dc.execute()
def run_hello_tab_sample(*args):
try:
# Invoked from External process
ctx: XComponentContext = remote_ctx
except NameError:
# Invoked from LibreOffice UI
ctx: XComponentContext = uno.getComponentContext()
app = DialogWithTab(ctx=ctx)
app.showDialog()
# Execute macro from LibreOffice UI (Tools - Macro)
g_exportedScripts = (run_hello_tab_sample,)
if __name__ == "__main__":
""" Connect to LibreOffice proccess.
1) Start the office in shell with command:
libreoffice "--accept=socket,host=127.0.0.1,port=2002,tcpNoDelay=1;urp;StarOffice.ComponentContext" --norestore
2) Run script
"""
local_ctx: XComponentContext = uno.getComponentContext()
resolver = local_ctx.ServiceManager.createInstance(
"com.sun.star.bridge.UnoUrlResolver"
)
try:
remote_ctx: XComponentContext = resolver.resolve(
"uno:socket,"
"host=127.0.0.1,"
"port=2002,"
"tcpNoDelay=1;"
"urp;"
"StarOffice.ComponentContext"
)
except NoConnectException:
print("""You are not running a libreoffice instance, to do so from a command line that has `libreoffice` in the path run:
libreoffice "--accept=socket,host=127.0.0.1,port=2002,tcpNoDelay=1;urp;StarOffice.ComponentContext" --norestore \n\nAnd try again
""")
run_hello_tab_sample()
@ikks
Copy link
Author

ikks commented Sep 11, 2025

Adding TabPage and TabPageContainer with createPeer

# -*- coding: utf-8 -*-
#!env py
#
# Igor Támara
# This file can be freely distributed under MPL1.0 and derivatives

import uno
import unohelper

from com.sun.star.connection import NoConnectException
from com.sun.star.uno import XComponentContext

from typing import TYPE_CHECKING


if TYPE_CHECKING:
    from com.sun.star.awt import UnoControlDialog
    from com.sun.star.awt import UnoControlDialogModel
    from com.sun.star.awt import UnoControlNumericFieldModel
    from com.sun.star.lang import ExtToolkit
    from com.sun.star.lang import ServiceManager
    from com.sun.star.awt.tab import UnoControlTabPageModel
    from com.sun.star.awt.tab import UnoControlTabPageContainerModel


def create_widget(
    container,
    typename: str,
    identifier: str,
    x: int,
    y: int,
    width: int,
    height: int,
    component=None,
):
    """
    Adds to the dlg a control Model, with the identifier, positioned with
    widthxheight, and put it inside container For typename see UnoControl* at
    https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1awt.html
    """
    if component is None:
        component = container
    cmpt_type = f"com.sun.star.awt.UnoControl{typename}Model"
    cmpt = container.createInstance(cmpt_type)
    cmpt.Name = identifier
    cmpt.PositionX = x
    cmpt.PositionY = y
    cmpt.Width = width
    cmpt.Height = height
    component.insertByName(identifier, cmpt)
    return cmpt


class DialogWithTab(unohelper.Base):
    def __init__(self, ctx=uno.getComponentContext()):
        self.context: XComponentContext = ctx
        self.sm: ServiceManager = self.context.ServiceManager
        self.toolkit: ExtToolkit = self.sm.createInstanceWithContext(
            "com.sun.star.awt.ExtToolkit", self.context
        )

        self.dc: UnoControlDialog = self.sm.createInstanceWithContext(
            "com.sun.star.awt.UnoControlDialog", self.context
        )
        self.dm: UnoControlDialogModel = self.sm.createInstance(
            "com.sun.star.awt.UnoControlDialogModel"
        )
        self.dc.setModel(self.dm)

        self.dm.Name = "the_options"
        self.dm.PositionX = "47"
        self.dm.PositionY = "10"
        self.dm.Width = 165
        self.dm.Height = 116
        self.dm.Closeable = True
        self.dm.Moveable = True
        self.dm.Title = "My Configuration"

    def insert_controls(self):
        dm: UnoControlDialogModel = self.dc.getModel()
        self.dc.createPeer(self.toolkit, None)
        book: UnoControlTabPageContainerModel = dm.createInstance(
            "com.sun.star.awt.tab.UnoControlTabPageContainerModel"
        )
        book.Name = "TabControl"
        book.PositionX = "18"
        book.PositionY = "15"
        book.Width = 130
        book.Height = 60
        dm.insertByName("TabControl", book)
        page_ad: UnoControlTabPageModel = book.createTabPage(1)
        page_ad.Title = "🔧 Parameters"
        book.insertByIndex(0, page_ad)
        page_ui: UnoControlTabPageModel = book.createTabPage(2)
        page_ui.Title = "👀 UI"
        book.insertByIndex(1, page_ui)

        ctrl: UnoControlNumericFieldModel = create_widget(
            page_ad, "NumericField", "int_height", 66, 12, 54, 13
        )
        ctrl.TabIndex = 8
        ctrl.StrictFormat = True
        ctrl.DecimalAccuracy = 0
        ctrl.Value = 384
        ctrl.ValueMin = 384
        ctrl.ValueMax = 1024
        ctrl.Spin = True

        ctrl: UnoControlNumericFieldModel = create_widget(
            dm, "NumericField", "int_outer", 88, 82, 54, 13
        )
        ctrl.TabIndex = 8
        ctrl.StrictFormat = True
        ctrl.DecimalAccuracy = 0
        ctrl.Value = 384
        ctrl.ValueMin = 384
        ctrl.ValueMax = 1024
        ctrl.Spin = True

    def showDialog(self):
        self.insert_controls()
        self.dc.setVisible(True)
        self.dc.execute()


def run_hello_tab_sample(*args):
    try:
        # Invoked from External process
        ctx: XComponentContext = remote_ctx
    except NameError:
        # Invoked from LibreOffice UI
        ctx: XComponentContext = uno.getComponentContext()

    app = DialogWithTab(ctx=ctx)
    app.showDialog()


# Execute macro from LibreOffice UI (Tools - Macro)
g_exportedScripts = (run_hello_tab_sample,)

if __name__ == "__main__":
    """ Connect to LibreOffice proccess.
    1) Start the office in shell with command:
    libreoffice "--accept=socket,host=127.0.0.1,port=2002,tcpNoDelay=1;urp;StarOffice.ComponentContext" --norestore
    2) Run script
    """

    local_ctx: XComponentContext = uno.getComponentContext()
    resolver = local_ctx.ServiceManager.createInstance(
        "com.sun.star.bridge.UnoUrlResolver"
    )
    try:
        remote_ctx: XComponentContext = resolver.resolve(
            "uno:socket,"
            "host=127.0.0.1,"
            "port=2002,"
            "tcpNoDelay=1;"
            "urp;"
            "StarOffice.ComponentContext"
        )
    except NoConnectException:
        print("""You are not running a libreoffice instance, to do so from a command line that has `libreoffice` in the path run:

       libreoffice "--accept=socket,host=127.0.0.1,port=2002,tcpNoDelay=1;urp;StarOffice.ComponentContext" --norestore  \n\nAnd try again
        """)

    run_hello_tab_sample()

@ikks
Copy link
Author

ikks commented Sep 13, 2025

I found a solution, making the dialog visible and afterwards adding the components that had problems calls the necessary code to show them correctly. I will add this as version 3, in order to make it easy to compare the changes. There are additional cosmetic to save some keystrokes.

# -*- coding: utf-8 -*-
#!env py
#
# Igor Támara
# This file can be freely distributed under MPL1.0 and derivatives

import uno
import unohelper

from com.sun.star.connection import NoConnectException
from com.sun.star.uno import XComponentContext

from typing import TYPE_CHECKING
from typing import Any
from typing import List
from typing import Tuple


if TYPE_CHECKING:
    from com.sun.star.awt import UnoControlDialog
    from com.sun.star.awt import UnoControlDialogModel
    from com.sun.star.awt import UnoControlModel
    from com.sun.star.awt import UnoControlNumericFieldModel
    from com.sun.star.lang import ExtToolkit
    from com.sun.star.lang import ServiceManager
    from com.sun.star.awt.tab import UnoControlTabPageModel
    from com.sun.star.awt.tab import UnoControlTabPageContainerModel


def create_widget(
    container,
    typename: str,
    identifier: str,
    rect: Tuple[int, int, int, int],
    component=None,
    add_now: bool = True,
    additional_properties: List[Tuple[str, Any]] = None,
):
    """
    Creates a control Model from typename, with the identifier, positioned with
    widthxheight, when add_now is True, inserts the new control inside component,
    if component is None, inserts in the container. States property
    values from additional_properties to the new created control.

    The new created control is returned.

    https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1awt.html
    """
    if component is None:
        component = container
    if typename == "TabPageContainer":
        cmpt_type = f"com.sun.star.awt.tab.UnoControl{typename}Model"
    else:
        cmpt_type = f"com.sun.star.awt.UnoControl{typename}Model"

    cmpt: UnoControlModel = container.createInstance(cmpt_type)
    cmpt.setPropertyValues(
        [
            "Name",
            "PositionX",
            "PositionY",
            "Width",
            "Height",
        ],
        [
            identifier,
            rect[0],
            rect[1],
            rect[2],
            rect[3],
        ],
    )
    if add_now:
        component.insertByName(identifier, cmpt)
    if additional_properties:
        cmpt.setPropertyValues(*zip(*additional_properties))

    return cmpt


class DialogWithTab(unohelper.Base):
    def __init__(self, ctx=uno.getComponentContext()):
        self.context: XComponentContext = ctx
        self.insert_in = []

        self.sm: ServiceManager = self.context.ServiceManager
        self.toolkit: ExtToolkit = self.sm.createInstanceWithContext(
            "com.sun.star.awt.ExtToolkit", self.context
        )

        self.dc: UnoControlDialog = self.sm.createInstanceWithContext(
            "com.sun.star.awt.UnoControlDialog", self.context
        )
        self.dm: UnoControlDialogModel = create_widget(
            self.sm,
            "Dialog",
            "dlg_options",
            (47, 10, 165, 116),
            add_now=False,
            additional_properties=[
                ("Closeable", True),
                ("Moveable", True),
                ("Title", "My Configuration"),
            ],
        )
        self.dc.setModel(self.dm)
        dm: UnoControlDialogModel = self.dc.getModel()
        self.dc.createPeer(self.toolkit, None)
        book: UnoControlTabPageContainerModel = create_widget(
            dm, "TabPageContainer", "book", (18, 15, 130, 60)
        )
        page_ad: UnoControlTabPageModel = book.createTabPage(1)
        page_ad.Title = "🔧 Parameters"
        book.insertByIndex(0, page_ad)
        page_ui: UnoControlTabPageModel = book.createTabPage(2)
        page_ui.Title = "👀 UI"
        book.insertByIndex(1, page_ui)

        self.page_ad = page_ad
        ctrl: UnoControlNumericFieldModel = create_widget(
            page_ad,
            "NumericField",
            "int_height",
            (66, 12, 54, 13),
            add_now=False,
            additional_properties=[
                ("StrictFormat", True),
                ("DecimalAccuracy", 0),
                ("Value", 384),
                ("ValueMin", 384),
                ("ValueMax", 1024),
                ("ValueStep", 64),
                ("Spin", True),
            ],
        )
        self.first = ctrl

        self.insert_in.append((self.page_ad, self.first))

        ctrl: UnoControlNumericFieldModel = create_widget(
            dm,
            "NumericField",
            "int_outer",
            (88, 82, 54, 13),
            add_now=False,
            additional_properties=[
                ("StrictFormat", True),
                ("DecimalAccuracy", 0),
                ("Value", 384),
                ("ValueMin", 384),
                ("ValueMax", 1024),
                ("ValueStep", 64),
                ("Spin", True),
            ],
        )
        self.second = ctrl
        self.insert_in.append((self.dm, self.second))

    def showDialog(self):
        self.dc.setVisible(True)
        for pair in self.insert_in:
            pair[0].insertByName(pair[1].Name, pair[1])

        return self.dc.execute()


def run_hello_tab_sample(*args):
    try:
        # Invoked from External process
        ctx: XComponentContext = remote_ctx
    except NameError:
        # Invoked from LibreOffice UI
        ctx: XComponentContext = uno.getComponentContext()

    app = DialogWithTab(ctx=ctx)
    app.showDialog()


# Execute macro from LibreOffice UI (Tools - Macro)
g_exportedScripts = (run_hello_tab_sample,)

if __name__ == "__main__":
    """ Connect to LibreOffice proccess.
    1) Start the office in shell with command:
    libreoffice "--accept=socket,host=127.0.0.1,port=2002,tcpNoDelay=1;urp;StarOffice.ComponentContext" --norestore
    2) Run script
    """

    local_ctx: XComponentContext = uno.getComponentContext()
    resolver = local_ctx.ServiceManager.createInstance(
        "com.sun.star.bridge.UnoUrlResolver"
    )
    try:
        remote_ctx: XComponentContext = resolver.resolve(
            "uno:socket,"
            "host=127.0.0.1,"
            "port=2002,"
            "tcpNoDelay=1;"
            "urp;"
            "StarOffice.ComponentContext"
        )
    except NoConnectException:
        print("""You are not running a libreoffice instance, to do so from a command line that has `libreoffice` in the path run:

       libreoffice "--accept=socket,host=127.0.0.1,port=2002,tcpNoDelay=1;urp;StarOffice.ComponentContext" --norestore  \n\nAnd try again
        """)

    run_hello_tab_sample()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment