Event Listener for a button in python extension

Hi, I’ve been working on a extension for libreoffice, using scriptforge, So far it has helped me to build things without taking too long to grok on documentation in different places.

I found something that I’m not understanding: How to bind an action to a button. From the docs it seems that I need to have a macro to be launched from the event.

onhelp = "service:org.fectp.StableHordeForLibreOffice?get_help"



        dlg = CreateScriptService(
            "NewDialog", "AIHordeOptionsDialog", (47, 10, 265, 206)
        )
        button_help = dlg.CreateButton("CommandButton1", (23, 15, 13, 10))
        button_help.Caption = "?"
        button_help.OnKeyPressed = onhelp # <a macro here> This reference does not work



class AiHordeForLibreOffice(unohelper.Base, XJobExecutor, XEventListener):
    """Service that creates images from text. The url to be invoked is:
    service:org.fectp.AIHordeForLibreOffice
    """

    def trigger(self, args):
        print(args)
        if args == "create_image":
            generate_image(self.desktop, self.context)
        if args == "validate_form":
            print("validate_event")
        if args == "get_help":
            print("get_help")




g_ImplementationHelper = unohelper.ImplementationHelper()
g_ImplementationHelper.addImplementation(
    AiHordeForLibreOffice,
    LIBREOFFICE_EXTENSION_ID,
    ("com.sun.star.task.JobExecutor",),
)

g_exportedScripts = (get_help,)

The problem is that I need to have a reference to the running process in order to let it be notified, and I was understanding that a macro is outside the context, and I don’t see how can I pass a reference to the macro. I’m not able either to publish a macro from my extension. in addition, I tried creating a function and using g_exportedScripts with no luck.

I haven’t found documentation on this particular purpose or a different way to bind an event without a macro to a control with scriptforge.

Basically What I’m doing is running a thread and I want to let the user cancel the thread pushing a button, but I haven’t managed to put a listener. and react inside the same extension.

The only option is to avoid scriptforge and rebuild the dialog? Or is there a choice with scriptforge that I’m maybe missing?

I have never used ScriptForge, but if you have a standard UNO object in this variable (button_help), then you just need to add the respective listener.

For example:

button_help.addMouseListener(EventsMouse())

You can see EventoMouse and other classes controllers at:

Only like example:

import easymacro as app


class Events():

    def __init__(self, dialog):
        self.d = dialog

    def cmd_help_click(self, event):
        app.msgbox('Click')
        return


def main():

    opt = {
        'Title': 'AIHordeOptionsDialog',
        'Width': 100,
        'Height': 50,
    }
    dialog = app.dialog.create(opt)

    opt = {
        'Type': 'Button',
        'Name': 'cmd_help',
        'Label': '?',
        'Width': 50,
        'Height': 15,
        'X': 10,
        'Y': 10,
    }
    dialog.add_control(opt)

    dialog.events = Events
    dialog.open()

    return

dialog

You don’t have to use easymacro, just follow my source code to reproduce the event.

2 Likes

As of 2025 September the solution is to move away from scriptforge to be able to manage events from different widgets. Scriptforge is good for the most cases given that it lowers the learning curve. It’s good for macros, but for extensions there is a lot of surface area to be worked on…

For the particular migration from ScriptForge to the base unolibraries, look at this PR. Recommended to view as split.

As in many UI libraries, there are three steps:

  1. Implement the Interface, in this case, XActionListener, in a class
from com.sun.star.awt import XActionListener

class LibreOfficeInteraction(unohelper.Base, InformerFrontend, XActionListener):
  1. with its corresponding actionPerformed function. This will be called when your component fires the event.
 def actionPerformed(self, oActionEvent):
        """
        Function invoked when an event is fired from a widget
        """
        if oActionEvent.ActionCommand == "btn_ok_OnClick":
            your_actions....
  1. Register the Listener with your component, that fires it, when acted upon. In the particular sample, addActionListener with setActionCommand.
dc.getControl("btn_ok").addActionListener(self)
dc.getControl("btn_ok").setActionCommand("btn_ok_OnClick")

There are plenty of Listeners from most common use cases.

P.D.: A great and inspiring resource is unodit that will allow you to transform a mousedesigned form to python and will give you events for the buttons. :heart_eyes:

I see why @elmau has implemented libraries to ease and make much more pythonic and have a higher level set of classes and functions to develop faster. In his repository he has valuable samples and a lot of useful code. Thanks for all :slight_smile:

1 Like