2021-07-11

64: Create Any UNO Component in Python

<The previous article in this series | The table of contents of this series | The next article in this series>

Typically, a listener in a macro or a LibreOffice or OpenOffice client, for example for dispatch commands, but not necessarily so.

Topics


About: UNO (Universal Network Objects)
About: LibreOffice
About: Apache OpenOffice
About: Python

The table of contents of this article


Starting Context



Target Context


  • The reader will know how to create and use his or her own UNO component in Python.

Orientation


There is an article on how to create a console Python UNO client.

There is an article on how to execute any UNO dispatch command and get the whole information from the execution in Python.

There is an article on how to use an external Python for LibreOffice or OpenOffice.

There is an article on how to create any user/application-owned Python macro.

There is an article on how to create any in-document Python macro.

There is an article on how to create any LibreOffice or Apache OpenOffice extension that contains Python macros.

There is an article on how to import any module into your Python macro.

There is an article on how to invoke any macro from your program.

There is an article on how to create and register any UNO Interface and generate the mapping images.


Main Body

Stage Direction
Here are Hypothesizer 7, Objector 64A, and Objector 64B in front of a computer.


1: Motivation


Hypothesizer 7
The primal motivation to create any UNO component is to let an instance of it (a UNO object) be handled from another programming language environment.

Objector 64B
Then, I don't need to create any, because I do programming only in Python.

Hypothesizer 7
It is not so in 2 points, madam. 1st, when I say "another programming language environment", it is 'another (programming language environment)', not '(another programming language) environment'.

Objector 64B
Huh? What's the difference?

Hypothesizer 7
If you have a Python macro and a console Python UNO client, you will have 2 programming language environments.

Objector 64B
. . . If I have 2 Python macros . . .

Hypothesizer 7
Then you will have only one programming language environment.

Objector 64B
Ah, so, I don't need to create any, anyway!

Hypothesizer 7
. . . 2nd, it is not just about your own programs; your program usually connects to a LibreOffice or Apache OpenOffice main body, which is another programming language environment.

Objector 64B
So? I didn't create LibreOffice.

Hypothesizer 7
Whether you created it or not, it is there as a part of the whole picture.

Objector 64B
. . . So, why does LibreOffice want to meddle in my program?

Hypothesizer 7
LibreOffice does not particularly want to meddle, but if your program wants to be informed of LibreOffice events, the principal mechanism is the broadcaster-listener pattern.

Objector 64B
LibreOffice has to meddle in my program in that pattern?

Hypothesizer 7
Your program registers a listener (which lives in your program) into a broadcaster (which lives in the LibreOffice main body), and the broadcaster informs the listener of events, where informing means calling a method of the listener, so, the listener has to be able to be handled from the LibreOffice main body.

Objector 64B
. . . So, do I have to create a UNO component? It's a shame!

Hypothesizer 7
It is not particularly any shame; it is really easy.

Objector 64B
Really? I will do anything if it is easy.

Hypothesizer 7
Please do not do a bad thing just because it is easy.

Anyway, I would like to remind you that although listener is a typical case, you are free to devise any other kind of useful UNO components whose instances can be handled from another programming language environment.

Objector 64B
How can such "useful UNO components" be used? I mean, where can they be registered into?

Hypothesizer 7
As they are not listeners, they do not particularly need to be registered; for example, a UNO object can be a macro return, which will be able to be handled from the caller in another programming language environment.


2: A Note Concerning UNO Service


Hypothesizer 7
To be sure, we are talking about UNO component, not about UNO service.

If you cannot distinguish them, please refer to a previous article.

Objector 64A
In fact, I want to talk about UNO service.

Hypothesizer 7
Ah, sir, as far as I know, any Python UNO component cannot be registered as a global UNO service, because the global UNO services manager does not have the ability to instantiate any Python class.

Objector 64A
Huh? It's a shame!

Hypothesizer 7
But you can implement your own non-global UNO services manager, or a more general UNO objects factory, if you like.

Objector 64A
Huh? How?

Hypothesizer 7
If you create a UNO component that has implemented the 'com.sun.star.lang.XMultiComponentFactory' UNO interface, it will be a UNO services manager; if you want a more general UNO objects factory, you can create a UNO component that has implemented your own factory UNO interface.

Of course, you will have to instantiate the factory somehow, store it somewhere, and disclose it somehow.

Objector 64A
"somehow"? What-how?

Hypothesizer 7
You can prepare a macro that does so, for example.

Objector 64A
"does so"? "does" which?

Hypothesizer 7
It instantiates the factory and stores the instance in a static member of the factory class, if it has not yet, and return the instance from the static member.

Objector 64A
Ah, so, it does all the 3 operations, instantiating the factory only at the 1st call.


3: Preparation


Hypothesizer 7
If your UNO component implements some of your UNO interfaces, you have to have created them according to a previous article.

As the mapping images for Python are generated dynamically, you can generate them in the code, as is described in the previous article.


4: Create a UNO Component


Hypothesizer 7
This is a typical code piece that creates a UNO component.

@Python Source Code
from typing import List
import uno
from com.sun.star.lang import XServiceInfo
from unohelper import Base as UnoBase

class TestUnoComponent (UnoBase, XServiceInfo):
	def __init__ (a_this: "TestUnoComponent") -> None:
		
		UnoBase.__init__ (a_this)
	
	def __del__ (a_this: "TestUnoComponent") -> None:
		None
	
	def getImplementationName (a_this: "TestUnoComponent") -> str:
		return "not specified"
	
	def supportsService (a_this: "TestUnoComponent", a_serviceName: str) -> bool:
		return False
	
	def getSupportedServiceNames (a_this: "TestUnoComponent") -> List [str]:
		return []

"XServiceInfo" is used just as a harmless example, and you usually do not need to use it.

Objector 64B
Then, what should I use?

Hypothesizer 7
You should be creating a UNO component because you want to implement some UNO interfaces, so you can use them.

Objector 64B
Actually, I don't want to implement any UNO interface right now.

Hypothesizer 7
That is because you are not creating any UNO component right now; if you are creating a listener, you will want to implement the UNO interfaces that the broadcaster requires.

Objector 64A
What is that "UnoBase"?

Hypothesizer 7
It is really 'unohelper.Base' (I have given the alias to it because "Base" is too general such that it can easily conflict with other modules) and is a helper class that has already implemented a UNO interface ('com.sun.star.lang.XTypeProvider', to be exact), and if your UNO component does not need to implement the UNO interface, it does not need to extend 'unohelper.Base', but I do not see any reason why your UNO component should not.

Objector 64B
Does my UNO component need that UNO interface?

Hypothesizer 7
'XTypeProvider' is for making it accessible from macro language environments.

Objector 64B
Python is already a macro language environment.

Hypothesizer 7
We are talking about other programming language environments from which any instance of the UNO component is handled.

Objector 64B
For example, a LibreOffice main body?

Hypothesizer 7
Yes.

Objector 64B
Is it a macro language environment?

Hypothesizer 7
No. So, if you are creating a listener to the LibreOffice main body, the UNO interface does not need to be implemented.


5: Usage


Hypothesizer 7
The module file that contains the UNO component, of course, has to be imported into your program.

Objector 64B
What do you mean by "your program"? I mean, which one? Another programming language environment?

Hypothesizer 7
Of course not: another programming language environment is, for example, a Java environment, which cannot import the Python module, obviously.

Objector 64B
Then, how can the Java environment use the UNO component?

Hypothesizer 7
You should understand how UNO works; the Java environment does not recognize the UNO component.

Objector 64B
Huh? How can the UNO component be possibly utilized by the Java environment without being recognized?

Hypothesizer 7
. . . As I said, you should understand how UNO works.

Objector 64A
How can my Python macro import the UNO component module? I mean, an arbitrary module can't be just imported into my macro!

Hypothesizer 7
Please refer to a previous article in order for it to be.

Anyway, in order to create any instance of the UNO component in your program, you can just call any constructor of the class.

Objector 64B
Huh? Don't I have to do an incantation like shaking the service manager?

Hypothesizer 7
UNO services manager is for instantiating a UNO component in a remote environment; you should understand the motivation for UNO services manager.

Objector 64B
. . . And how can I use the instance?

Hypothesizer 7
As the instance is a Python object, you can use it as an ordinary Python object in the local environment, and you can pass it to a remote environment via an argument of a remote UNO object method.

Objector 64B
And how can the remote environment handle the instance?

Hypothesizer 7
The remote environment does not care where the instance is; so, it does not do anything special just because the instance is a Python UNO object.


References


<The previous article in this series | The table of contents of this series | The next article in this series>