How to use macro to open form in Base?

(LO 7.4.6, OS W10)
Just taking first steps with automating Base using Python macros. To get this to work I started with this tutorial page.

This macro works and prints out interesting info on the objects here.

def Explore(*args):
    desktop = XSCRIPTCONTEXT.getDesktop()
    model = desktop.getCurrentComponent()
    form_docs = model.getFormDocuments()
    # form_docs turns out to be iterable. There is one form.
    
    # time.sleep(1)
    # form_docs[0].open()
    # time.sleep(1)
    
    output_file_path_str = r'D:\My Documents\temp\output.txt'
    with open(output_file_path_str, 'a') as f:
        f.write(f"""
now {datetime.datetime.now()}
form_docs {form_docs} type {type(form_docs)}
dir(form_docs) {dir(form_docs)}
        """)
    return None

When I uncomment those lines around form_docs[0], I get a “WrappedTargetException”.

vnd.sun.star.tdoc:/3027442269224/Scripts/python/my_base_script.py (<class ‘ooo_script_framework.com.sun.star.lang.WrappedTargetException’>:
File “D:\apps\LibreOffice\LibreOffice_7.4.6\program\pythonscript.py”, line 915, in invoke
ret = self.func( *args )

NB I know that form_docs[0] has this method open(), because I also did an print out on it, its type and its dir, so it turns out that one of its base classes is XSubDocument where the method open() is documented. It also explicitly says that this method can raise a WrappedTargetException, “if an error occurs during opening the document”. That’s quite a “general” explanation :slightly_smiling_face:

Any idea what I’m doing wrong? Opening a form inside an .odb file might be thought to be a fairly ordinary thing to want to do.

later

  1. Once… it opened the form. Out of maybe 30 runs.
  2. I tried putting the command in an independent thread… no good
  3. I am puzzled by the API info for WrappedTargetException: I have printed out the following in the except block:

f.write(f'e {e} type {type(e)}\ndir {dir(e)}\ndir(e.__class__) {dir(e.__class__)}\nstack_trace {stack_trace}\n')

… there is no sign of TargetException, Message or Context (or getters for these) in dir(e), output:

now 2023-04-21 08:42:09.722323e  type <class 'ooo_script_framework.com.sun.star.lang.WrappedTargetException'>
dir ['__cause__', '__class__', '__context__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', 
'__format__', '__ge__', '__getattr__', '__getattribute__', '__gt__', '__hash__', '__init__', 
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__pyunointerface__', '__pyunostruct__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', 
'__str__', '__subclasshook__', '__suppress_context__', '__traceback__', '__weakref__', 'args', 'typeName', 'value', 'with_traceback']
dir(e.__class__) ['__cause__', '__class__', '__context__', '__delattr__', '__dict__', '__dir__', '__doc__', 
'__eq__', '__format__', '__ge__', '__getattr__', '__getattribute__', '__gt__', '__hash__', '__init__', 
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__pyunointerface__',
'__pyunostruct__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__',
'__str__', '__subclasshook__', '__suppress_context__', '__traceback__', '__weakref__', 'args', 
'typeName', 'with_traceback']

Create push buttons.
Enter the name of the form or report you want to open into the button’s “Additional info”. If forms/reports are organized in folders: Folder/Subfolder/Report Name
Assign the right script to open the form or report.

# coding: utf-8
from __future__ import unicode_literals

def Open_Report_Button(e):
    '''specify the hierarical name in the button's "Additional info" field'''
    sName = e.Source.Model.Tag
    OpenEmbedded(e.Source, sName, True)

def Open_Form_Button(e):
    '''specify the hierarical name in the button's "Additional info" field'''
    sName = e.Source.Model.Tag
    OpenEmbedded(e.Source, sName, False)

def OpenEmbedded(src, sHierachicalName, bReport):
    odb = getDBDocument(src)
    if bReport:
        container = odb.ReportDocuments
    else:
        container = odb.FormDocuments
    obj = container.getByHierarchicalName(sHierachicalName)
    obj.open()


def getDBDocument(src):
    oModel = src.getModel()
    # a button's parent is always a form
    oForm = oModel.getParent()
    oActiveConnection = oForm.ActiveConnection  
    oDataSource = oActiveConnection.getParent()
    return oDataSource.DatabaseDocument

g_exportedScripts = Open_Form_Button, Open_Report_Button
1 Like

Thanks. This worked.

Push buttons… but is there no way of opening a form without the user having to click something? (I mean, using a user-written Python macro (obviously there is a simple way, using VB, of opening an initial form by connecting the event OpenDocument).)


Also, do you know how I dig into the underlying exception of WrapTargetException … as I say, dir(e) does not seem to show TargetException, Message etc. as being accessible. Surely there should be a way of gaining access to the target exception?


I’m really keen to get at the cause because I want to understand what sort of phenomenon this is. It smells like it might be something to do with threads, because as I say, it worked (opened the form) once, and these sorts of transient errors are typical of what happens with, for example, PyQt, if you do things in non-GUI threads incorrectly.


later

I adapted your code as follows to make a Python macro which was then attached to the OpenDocument event:

def open_contacts_form(e):
    odb = e.Source
    container = odb.FormDocuments
    obj = container.getByHierarchicalName('kernel.contacts')
    obj.open()

From printouts to files I could see that obj was the right kind of object… but again I get WrappedTargetException. Whereas the VB code to do the same thing works without a hitch. Frustrating.

Copy the Basic code and adjust it to Python syntax. The API is the same.
ThisComponent = XSCRIPTCONTEXT.getDocument()

Thanks again.
Turns out that the controller doesn’t have an attribute Connect. But it does have an attribute connect.

form_type = 2 # looked this up in the API docs. No way to import com.sun, etc.
odb = e.Source
odb.CurrentController.connect()
odb.CurrentController.loadComponent(form_type, 'kernel.contacts', False)

Method connect.

Methods are attributes (in Python).

By the way, sorry to hear about the, er, unfortunate incident (in your city).

The UNO API has methods and properties like in Java, so connect is a method, even if it could be treated as an attribute in the Python integration. The API is about more than just Python, and calling it an attribute is misleading and inconsistent with the API docs.


Anyway, as I wrote on SO, you didn’t mention any introspection tool such as MRI, so it seems like that may be what you’re missing in order to understand how to progress further.

the main difference (from the python POV) is:

  • Api-Properties (==AttributeAccess) starts Uppercase and you need NO ()Braces to access
  • Api-methods (==method-access ) starts lowercase, and you need at least ()Braces behind to CALL them.
  • and of course: some of the methods needs (Arguments) for invocation.

Methods are still attributes in Python even if they are not in the UNO API.


Since I have the line odb.CurrentController.connect() it is fairly obvious that I can see that that attribute functions as a method. My point about that is that is only by using dir that I was able to find it. In UNO VBA it (the method) is used with a capital C, Connect, as in the referenced snippet by ratslinger. So “translating” from UNO VBA to UNO Python is not straightforward.


Thanks for reference to UNO MRI. Not surprisingly, I’ve never heard of this.

In VBA or in Basic it is complete irrelevant if you call …ConNeCt , connect or CoNNect also it is irrelevant if you append ()braces or not to Properties or METHODS (except these METHODS which needs Argument[s] )

And you may believe or not, in python you have to use braces behind Functions or some_object.methods to CALL them.

Don’t download from there — it’s outdated. Get MRI from the source instead.

Yup, thanks, had figured that out! :slightly_smiling_face:

@mrodent

.
Might this be what you are wanting → python_load_form.odb (11.2 KB)
.
Code in .odb - Q&D