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

Don’t get why attaching to open document event would not satisfy this.
.
Have programmatically been able to insert on the fly listener changes in a Calc file. Will look for that. Still not really sure what you are attempting here. Sounds a bit of over complication.

@Ratslinger
Thanks. Do you mean doing something like this:

    real_open = form_0.open
    def overridden_open(*args, **kwargs):
        print(f'args {args} kwargs {kwargs}')
        real_open(*args, **kwargs)
    form_0.open = overridden_open

… this failed with

Traceback (most recent call last):

File “start_base_auto.py”, line 84, in main
form_0.open = overridden_open
uno.RuntimeException: Couldn’t convert <function main..overridden_open at 0x000001CACC3A5040> to a UNO type; caught exception: <class ‘AttributeError’>: ‘function’ object has no attribute ‘getTypes’, traceback follows
no traceback available

… it may be obvious to you why this is. Or it may be that this framework is missing certain environmental elements. For example, XSCRIPTCONTEXT is not available.


… or you may mean something entirely different by “attaching to open document event”. What does this mean to you, if not substituting a method Pythonically as in the above snippet? (I really want to know!). However, please note that this is NOT an “open document” event. A Form is not the document. LO Base files can contain multiple Forms. They implement (for example) the interface XSubDocument, among many others…

I may be completely off track here, but if you are steering LibreOffice over socket/pipe, why do you need a listener? It should be your program, wich opens a form, so there is no need to be notified, when the form opens. (You may get an error-code, if it isn’t opening)
.
This is different from the approach of internal macros, wich are triggered by buttons or events, when user interacts with the program. So we don’t know, what user is doing, and react to events.

@Wanderer
Thanks.


No, the Form is not opened programmatically (or not necessarily).


The Base file displays as normal on running the Python script, and the user is free to click on a form (of which there can be many in one Base file) at will, to open it. There IS DEFINITELY a mechanism to listen into that event, otherwise it would not be possible to connect up the event, for a specific form, “When loading”, which you can do manually.


If you can do it manually there must be a way to listen for and respond to that event programmatically.

Not sure where you are looking. I find it but not certain where or what you are looking at. Again, is this what you are looking for → How to use macro to open form in Base? - #14 by Ratslinger
.
Each comment you present seems to be more confusion as to what is actually needed.
.
Edit:
Also unclear is:

.
Then what is it? Another wonderful situation which has been present since long, long ago. Same title for two different items. Base can have tables, queries reports and forms. These forms are documents within Base. Now in that Form are internal forms which contain controls. There can be many forms within the form document and sub forms for each and sub sub forms… and these internal forms do not get opened or closed.
.
So have unclear picture as to what this means:

.
Second edit:
.
So here is a link which shows how to set events(listeners) in controls programmatically. Yes the code is in Basic but that should not be any problem to convert. I am still trying to figure out just what it is you actually want.
.

@vib
Thanks.
Regarding your search tool, I installed this, but it failed because of an attempt to import sqlite3, which is a notorious package. In theory it should be possible to install module pysqlite3, but on this occasion this also failed.


Concerning your window listener class: interesting, but this is about loading the initial window. I’m not sure how applicable it is to something like a form, which starts like this:

pyuno object (com.sun.star.ucb.XContent)0x1eb8928e018{implementationName=com.sun.star.comp.sdb.Content, supportedServices={com.sun.star.ucb.Content}, supportedInterfaces={ ...

Note that, strangely, the API docs do not contain a section on com.sun.star.comp.


I have found that there is an interface XLoadListener, and also an interface XLoadable, which has a method addLoadListener(XLoadListener listener). I would bet that these are the things I need to find but the question is how to find this interface from the form object: the object itself does not implement XLoadable.


Having done some searching it appears that “script events” may be the key here, see this answer in a forum thread.

I don’t understand much yet about “property change listeners” either: is it possible to get a list of listenable properties for an object?

https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1form_1_1XLoadable.html#a2664eb513cdbb9cd90b615bdeb5596e9
https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1form_1_1XLoadListener.html

@Ratslinger

Let me try and clarify. It’s VERY simple. Using the script framework ooo dev tools developed by vib, I have managed, by running the script, to open an .odb document which contains 1 form, so this opens Base as normal. I confirm that in this arrangement, XSCRIPTCONTEXT is not available: this is not the situation where a macro is connected normally to an event, by editing an .odb file.


So I simply want, programmatically, in this script, and having obtained a reference to the form object in my Python code, to set up a listener on it, one method of which will fire when the form is loaded (opened): whether programmatically, or more likely when the user clicks on the form. This is equivalent to the “When loading” event which you can find under “Events” when you edit a form.


PS it may be that this form is a “document” for you, or by convention of UNO terminology. I had assumed that when we talk about “documents” here this means, essentially, the .ods file, .odt file or .odb file (in the case of Base). Obviously an .odb file can contain multiple forms, and they are loaded by the user double-clicking on one of them.

@Villeroy
Thanks, but I had already provided those links to the API documents in my last post. Why did you list them?

As stated earlier, this is not part of LibreOffice but rather a third party add on. Questions on this should be asked of that author and not here on the LO Ask site.

@Ratslinger

My question is in no way specific to the framework. This is so apparent it is hardly worth saying.


It seems like you are unable to answer the question "set up a listener on [the Form object], one method of which will fire when the form is loaded (opened)", but for some reason don’t want to say so. Such an object can be obtained in a normal macro, as you must be aware.


As you can see from the tentative answer I have given, there is a way of detecting, using a listener, when a form is opened.

No. The problem I have is trying to figure out just what you are trying to accomplish. Have one more post for you → Base macro that opens a new/clean record in another form using python
.
Also did present Calc file showing methods to modify listeners using code. Have come at various angles with this and have no idea why none of this is seemingly near to shat is wanted.

With OooDev, in script mode, after a document is loaded the Lo.XSCRIPTCONTEXT is available (or Lo.xscript_context).
The same is true for Lo.this_component.

In Macro Mode Lo.XSCRIPTCONTEXT and Lo.this_component are available when the document is opened.

Usually these properties are not needed because OooDev gives you access to what you need via it various modules such as Calc, Write, Char2 etc.

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”.