How do I find programmatically (Python) the "when loading" event for a Form in a Base document?

sqlite3 is a built in package of python. The issues is (on Windows) when installing the search tool in a vritual environment set for LibreOffice the sqlite package is missing. This is a LibreOffice thing, LibreOffice does not ship with complete python. See bug 116412. If you install the search tool in a normal python environment it should work fine.

A possible answer. But I think it’s a workaround, not the real answer.

When I click on the form icon and the form opens, I have found that a listener is indeed triggered, but not on the form itself:

    doc = Base.open_doc('test_db.odb', loader)
    GUI.set_visible(is_visible=True, odoc=doc)
    class DocEventListener(unohelper.Base, XDocumentEventListener):
        def documentEventOccured(self, event):
            print_out_element(event, 'doc event')
    doc.addDocumentEventListener(DocEventListener())

… one possible hypothesis is thus that the “property” “When loading” which you can use to attach a macro to the event of a form being opened is in fact a sort of pseudo-property: this event is actually attached to the object at the top of the hierarchy, which I shall refer to as the “ODB document”, with the following spec: implementationName=com.sun.star.comp.dba.ODatabaseDocument, supportedServices={com.sun.star.sdb.OfficeDatabaseDocument,com.sun.star.document.OfficeDocument}

Interestingly, before a form is opened, although it implements the interface XComponent, actually getComponent() returns None. After a form is opened, however, this returns an object, which is essentially a Writer document, since Base forms are obviously modelled on Writer documents. This might explain why form objects implement XCloseListener, but (apparently) nothing to listen in on the event of opening.

I tentatively call it a “pseudo property”, because the user who edits the form, and attaches a macro to this event, is no doubt tempted to think that they are configuring an event of the form itself. Which then begs the question, are all these form events pseudo properties, or just this one: for example, what about the event “After record change”, which I also need to configure?

A bit later: I now don’t think this is right. Clearly the Writer document which is used to make the component when a form is loaded must be stored somewhere inside the .odb file. Having examined the component after opening, this has a method getEvents, one of which is OnLoad (however, there is nothing there which corresponds to the form’s “After record change” event: maybe this can be found from the form’s data provider objects …).

So the question is: how to obtain a reference to this Writer document, before the form is loaded? It must be present in some storage location.

Embedded form opening can be listened to with database document event OnSubComponentOpened.
Interface XDatabaseDocumentUI provides access to the user interface of a database document.

Thanks. I saw from exploration that doc.getEvents() includes OnSubComponentOpened (type str)… but I don’t know how you set up a listener on that. This doesn’t work:

    def on_sub_opened(*args, **kwargs):
        print(f'args {args} kwargs {kwargs}')
    doc.getEvents().OnSubComponentOpened = on_sub_opened

uno.RuntimeException: Couldn’t convert <function main..on_sub_opened at 0x00000125C29F99D0> to a UNO type; caught exception: <class ‘AttributeError’>: ‘function’ object has no attribute ‘getTypes’

I’ve previously added a function-attribute (in the Python sense) getTypes to the function here. But then I get a fail with

uno.RuntimeException: Couldn’t convert <function main..on_sub_opened at 0x00000275E415C9D0> to a UNO type

… could you show what I’m meant to do?


Re your other point, could you illustrate how I obtain an object which implements that interface? The com.sun.star.comp.dba.ODatabaseDocument object doesn’t appear to do so.

This is because you start with the most complicated (and underdeveloped and unexplored) component of all. IMHO, whatever you try to do here, will turn out as virtually impossible to do.

@Villeroy
That’s the 3rd pointless post you’ve made here. Perhaps you could just not post a fourth?

In the standard way.
Open the database document and Menu / Tools / Customize… / Events.
Event: “Loaded a sub component”.

@sokol92
Oh dear. From your previous answer I thought you had understood. Please review the posts in this thread, and read the question thoroughly. I am trying to do this programmatically.

You may be the first person ever who wants to do that programmatically. Go ahead and don’t forget to tell us what you found out.

@Villeroy
I’ve already shown one way of doing this, using XDocumentEventListener.


I don’t know why someone who seems to have no interest in programming UNO using Python, and without the courtesy to have read the question in the first place, would want to contaminate a thread with pointless posts. I’ve heard your opinion. Could you stop posting now? Thanks.

There are many similar examples on the forum.
I stop posting now, Thanks.
For future readers of the topic:

Sub OnSubFormOpened(oEvent)
  Msgbox "OnSubFormOpened"
End Sub

Sub AddEventListener
  Dim oEvents
  Dim mEventProps(1) as new com.sun.star.beans.PropertyValue
  mEventProps(0).Name = "EventType"
  mEventProps(0).Value = "Script"
  mEventProps(1).Name = "Script"
  mEventProps(1).Value = "vnd.sun.star.script:Standard.Module1.OnSubFormOpened?language=Basic&location=document"
  oEvents=ThisComponent.Events
  oEvents.replaceByName "OnSubComponentOpened", mEventProps
End Sub

@sokol92

Thank you for showing how to attach a function to events designated by strings.

But in Python … hmmm! Very intriguing. I’ve just looked at the API and searching to see how a com.sun.star.beans.PropertyValue is created in Python UNO… and the gist seems to be

event_props = PropertyValue(Name='EventType', Value='Script')

… but as yet I haven’t found an example where, seemingly, you have TWO entries involved in a single PropertyValue. …

Again, you start at the wrong end. How to create UNO structs in your preferred language should be known before solving esoteric issues.
import uno
prop = uno.createUnoStruct(whatever)

Thanks. You gave me a clue. I’m certainly out of my depth. However, I have a certain belief that most of the objects I want to expose and manipulate in Base shouldn’t be that difficult to get to.

    def on_sub_form_opened(*args, **kwargs):
        print(f'args {args} kwargs {kwargs}')
    
    sortproptuple=()
    PropVal = uno.createUnoStruct('com.sun.star.beans.PropertyValue')
    PropVal.Name = 'EventType'
    PropVal.Value = 'Script'
    sortproptuple+=(PropVal,)
    
    PropVal = uno.createUnoStruct('com.sun.star.beans.PropertyValue')
    PropVal.Name = 'Script'
    # this next, of course, is very unlikely to result in a call to the above function:
    PropVal.Value = 'on_sub_form_opened' 
    sortproptuple+=(PropVal,)
    
    doc.getEvents().replaceByName('OnSubComponentOpened', sortproptuple)

… last line raised exception:

Traceback (most recent call last):
  File "start_base_auto.py", line 337, in <module>
    raise SystemExit(main())
  File "start_base_auto.py", line 176, in main
    doc.getEvents().replaceByName('OnSubComponentOpened', sortproptuple)
__main__.IllegalArgumentException: []any

Its a frustating job with you!
On the one hand, you think you know everything better… on the other hand, you need support at a beginner level.

from com.sun.star.beans import PropertyValue as PropVal

pv_0 = PropVal()
pv_0.Name = "EventType"
pv_0.Value = "Script"

pv_1 = PropVal()
pv_1.Name = "Script"
pv_1.Value = ("vnd.sun.star.script:Standard"
              ".Module1.OnSubFormOpened?"
              "language=Basic&location=document")

doc = XSCRIPTCONTEXT.getDocument()
events = doc.Events
events.replaceByName("OnSubComponentOpened", (pv_0, pv_1) )

@karolus

Thanks for trying. Unfortunately the same exception gets raised on the last line:

__main__.IllegalArgumentException: []any

OooDev has a Props Class making it much easier to create properties an much more.

# gets a tuple of PropertyValue
props = Props.make_props(
  EventType="Script",
  Script="vnd.sun.star.script:Standard.Module1.OnSubFormOpened?language=Basic&location=document"
)

Get a single PropertyValue

pv = Props.make_prop_value("Overwrite", True)

@vib

…but that delivers a single PropertyValue, right (since you have a “kwargs” argument)?
When multiple such PropertyValues are put in a tuple this is failing with the above cryptic message “IllegalArgumentException: []any”.


Making a PropertyValue is not hard, so I’m not sure I understand the need for that kind of aliasing in your framework.

I have found in some cases that uno.invoke is required.

Something along the lines of:

props = Props.make_props(
  EventType="Script",
  Script="vnd.sun.star.script:Standard.Module1.OnSubFormOpened?language=Basic&location=document"
)

events = doc.getEvents()

any_props = uno.Any("[]com.sun.star.beans.PropertyValue", props)
uno.invoke(events, "replaceByName", ("OnSubComponentOpened", any_props))
1 Like

@vib

Yes!


How much do you know about streams? Following my theory that, although not yet loaded as a form object, the object based on a Writer document which will be loaded as the form is stored somewhere in the .odb file, I am trying to find a way of accessing it, in order to try to interfere with various configured events.

I therefore tried this:

    forms_ss = doc.getDocumentSubStorage('forms', ElementModes.READWRITE)
    ss_el_names = forms_ss.getElementNames()
    print(f'ss_el_names {ss_el_names} type {type(ss_el_names)}')
    for ss_el_name in ss_el_names:
        form_ss = forms_ss.getByName(ss_el_name)
        print(f'ss_el_name {ss_el_name} form_ss {form_ss} type {type(form_ss)}')
        # this doesn't work... I can't find out what "stream names" may be available
        # stream_el = form_ss.openStreamElement(ss_el_name, ElementModes.READWRITE)

Any thoughts?