2020-01-26

35: Execute UNO Dispatch Command and Get All Information in Python

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

Your lazy way may be missing some useful information that is available from the execution.

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 execute any UNO dispatch command and get the result information and the related information in Python.
Stage Direction
Here are Hypothesizer 7, Objector 35A, and Objector 35B in front of a computer.


Orientation


Hypothesizer 7
In this article, we will know how to execute any UNO dispatch command and get the result information and the related information, in Python.

Objector 35B
"the related information"? Why should I desire that? You know, I need to know whether the execution has succeeded, but that should be all I want to know.

Hypothesizer 7
Madam, as for many UNO dispatch commands, actually, that is all you can know, but as for some UNO dispatch commands, the related information is really useful, or is the entire purpose of the execution.

Objector 35B
What do you mean by "the entire purpose"?

Hypothesizer 7
For example, the UNO dispatch command, '.uno:FontNameList', does nothing but list the font names as the related information; executing the command without getting the related information is futile.

Objector 35B
Hmm, I don't think that I will use the command. . . . Anyway, when is the related information "really useful"?

Hypothesizer 7
For example, the UNO dispatch command, '.uno:GoToCell', reports the previous current cell position in the related information.

Objector 35B
Is that information "really useful"?

Hypothesizer 7
Yes, at least for me. In fact, I do not know how to know the current cell position otherwise, although the selected cells positions can be known easily.

Objector 35B
Huh? Isn't the selected cell the current cell?

Hypothesizer 7
You know, there can be multiple selected cells, and the current cell, which is the cell into which key inputs go, may be one of them or even be not one of them.

Objector 35B
Ah, you mean the current cell . . .

Hypothesizer 7
Yes, I always mean the current cell when I use the term, 'current cell'.

Objector 35A
If I don't want the related information, I can just use the dispatch helper, can't I?

Hypothesizer 7
Sir, you seem to already know the way that uses the UNO dispatch helper. Certainly, you can use the UNO dispatch helper if you do not need the related information, although the way is not particularly the most efficient.

Objector 35A
What do you mean by "not particularly the most efficient"?

Hypothesizer 7
The helper does not reuse the URL object or the dispatcher that could be reused when a command URL is used multiple times, but whether such inefficiency is significant for your usage is what you will decide, although I do not use the UNO dispacth helper becuase I do not have any reason to use any way that is less informative and less efficient than the best way.

Objector 35A
. . .


Main Body


1: Getting a UNO Dispatchers Provider UNO Proxy of the Frame


Hypothesizer 7
As any UNO dispatch command is executed on a frame, first, we have to get the frame. If we want to execute the UNO dispatch command on a document, we will have to get the frame that contains the document; otherwise, we will use the UNO desktop frame.

Even if you use the UNO dispatch helper, you will have to do that.

Objector 35B
What do you mean by "the UNO desktop frame"?

Hypothesizer 7
The UNO desktop is a frame and is the root ancestor of all the other frames.

Objector 35B
You mean, there inevitably exists such an object?

Hypothesizer 7
Yes, it exists whether you like it or not.

In order to get the frame that contains any document, first, we have to get a 'com.sun.star.frame.XModel' UNO proxy of the document UNO object.

While there are some ways to get access to the UNO object of the document, I will not delve into all of them here (I will do so in a future article). Instead, we will get access to the UNO object of the current document, like this, where 'l_remoteUnoObjectsContext' is the UNO objects context and 'l_unoDocumentInXModel' is the 'com.sun.star.frame.XModel' UNO proxy of the document UNO object.

@Python Source Code
from typing import cast
from com.sun.star.frame import XDesktop2
from com.sun.star.frame import XModel

					l_unoDesktopInXDesktop2: XDesktop2 = cast (XDesktop2, l_remoteUnoObjectsContext.getServiceManager ().createInstanceWithContext ("com.sun.star.frame.Desktop", l_remoteUnoObjectsContext))
					l_unoDocumentInXModel: XModel = cast (XModel, l_unoDesktopInXDesktop2.getCurrentComponent ())

Objector 35B
"cast"?

Hypothesizer 7
As I use variable type annotations and mypy, I use 'cast', but actually, 'cast' has no runtime effect. In fact, I say like "'l_unoDocumentInXModel' is the 'com.sun.star.frame.XModel' UNO proxy", but actually, that is an abstract statement that does not exactly correspond to the runtime behavior.

Objector 35B
So, 'l_unoDocumentInXModel' is not really a 'com.sun.star.frame.XModel' UNO proxy?

Hypothesizer 7
Probably not. In Python, necessary UNO proxies are gotten implicitly as in Basic, instead of being gotten explictly as in Java, C++, and .NET Framework.

Objector 35B
Hmm . . ., whatever.

Hypothesizer 7
We will get a 'com.sun.star.frame.XDispatchProvider' UNO proxy of the frame UNO object, like this.

@Python Source Code
from com.sun.star.frame import XController
from com.sun.star.frame import XDispatchProvider

					l_controllerInXController: XController = cast (XController, l_unoDocumentInXModel.getCurrentController ())
					l_frameInXDispatchProvider: XDispatchProvider  = cast (XDispatchProvider, l_controllerInXController.getFrame ())

Objector 35B
Abstractly speaking . . .

Hypothesizer 7
On the other hand, a 'com.sun.star.frame.XDispatchProvider' UNO proxy of the UNO desktop frame UNO object, can be gotten like this.

@Python Source Code
from com.sun.star.frame import XDispatchProvider

						l_frameInXDispatchProvider: XDispatchProvider = cast (XDispatchProvider, l_remoteUnoObjectsContext.getServiceManager ().createInstanceWithContext ("com.sun.star.frame.Desktop", l_remoteUnoObjectsContext))

Objector 35B
Hmm.


2: Executing Any UNO Dispatch Command and Getting the Result Information and the Related Information


Hypothesizer 7
As the result information and the related information are informed to a 'com.sun.star.frame.XDispatchResultListener' and a 'com.sun.star.frame.XStatusListener', respectively, we create a class that that implements 'com.sun.star.frame.XDispatchResultListener' and create one or more classes that implement 'com.sun.star.frame.XStatusListener', where a single class can implement both of the UNO interfaces.

For example, these are a 'com.sun.star.frame.XDispatchResultListener' implementation and a 'com.sun.star.frame.XStatusListener' implementation, where 'Test1Test' is the class in which they are defined.

@Python Source Code
import sys
from com.sun.star.frame import XDispatchResultListener
from com.sun.star.frame import XStatusListener
from com.sun.star.lang import EventObject as com_sun_star_lang_EventObject
from com.sun.star.frame import DispatchResultEvent
from com.sun.star.frame import FeatureStateEvent
from unohelper import Base as UnoBase

	class ResultInformationListener (UnoBase, XDispatchResultListener):
		def __init__ (a_this: "Test1Test.ResultInformationListener") -> None:
			None
		
		def dispatchFinished (a_this: "Test1Test.ResultInformationListener", a_dispatchResultEvent: DispatchResultEvent) -> None:
			sys.stdout.write ("### The dispatch execution is finished: {0:s}.\n".format (str (a_dispatchResultEvent)))
		
		def disposing (a_this: "Test1Test.ResultInformationListener", a_source: com_sun_star_lang_EventObject) -> None:
			None
	
	class RelatedInformationListener (UnoBase, XStatusListener):
		def __init__ (a_this: "Test1Test.RelatedInformationListener") -> None:
			None
		
		def statusChanged (a_this: "Test1Test.RelatedInformationListener", a_featureStateEvent: FeatureStateEvent) -> None:
			sys.stdout.write ("### A dispatch execution related information piece is offered: {0:s}.\n".format (str (a_featureStateEvent)))
		
		def disposing (a_this: "Test1Test.RelatedInformationListener", a_source: com_sun_star_lang_EventObject) -> None:
			None
	
					l_resultInformationListener: "Test1Test.ResultInformationListener" = Test1Test.ResultInformationListener ()
					l_relatedInformationListener: "Test1Test.RelatedInformationListener"  = Test1Test.RelatedInformationListener ()

Objector 35B
What are the specifications of those event objects?

Hypothesizer 7
Please look those classes up in the UNO API document (here for 'com.sun.star.frame.DispatchResultEvent', here for 'com.sun.star.lang.EventObject', and here for 'com.sun.star.frame.FeatureStateEvent').

Objector 35B
I see.

Hypothesizer 7
Then, we set up a 'com.sun.star.util.URL' object (a non-UNO object), like this.

@Python Source Code
from typing import Match
from typing import Optional
from typing import Pattern
import re
from com.sun.star.util import URL as com_sun_star_util_URL

					l_url: str = ".uno:GoToCell"
					l_urlInURL: com_sun_star_util_URL = com_sun_star_util_URL ()
					l_urlInURL.Complete = l_url
					l_urlInURL.Main = l_url
					l_regularExpressionPattern: Pattern = re.compile ("(.*:)(.*)")
					l_regularExpressionMatch: Optional [Match] = l_regularExpressionPattern.match (l_urlInURL.Complete, 0)
					if l_regularExpressionMatch is not None:
						l_urlInURL.Protocol = l_regularExpressionMatch.groups () [0]
						l_urlInURL.User = ""
						l_urlInURL.Password = ""
						l_urlInURL.Server = ""
						l_urlInURL.Port = 0
						l_urlInURL.Path = l_regularExpressionMatch.groups () [1]
						l_urlInURL.Name = ""
						l_urlInURL.Arguments = ""
						l_urlInURL.Mark = ""

Objector 35B
. . . Well.

Hypothesizer 7
Anyway, then, we get a 'com.sun.star.frame.XNotifyingDispatch' UNO proxy of the dispatcher UNO object, like this.

@Python Source Code
from com.sun.star.frame import XNotifyingDispatch

					l_dispatchInXNotifyingDispatch: XNotifyingDispatch = cast (XNotifyingDispatch, l_frameInXDispatchProvider.queryDispatch (l_urlInURL, "_self", -1))

"_self" and "-1" are the target frame name of the command and the search flag for finding the frame.

Objector 35B
I thought, we have already gotten the target frame in the previous section . . .

Hypothesizer 7
We have, as we have intended to specify '_self' here.

Objector 35B
What if we had intended otherwise?

Hypothesizer 7
Of course, we could have started with another frame and could specify the target frame name and the search flag appropriately here.

Objector 35B
What names can I specify as the target frame name?

Hypothesizer 7
'_self' is not any proper name, but a special frame name that means the concerned frame itself, which is 'l_frameInXDispatchProvider' in this case.

In order to understand the other special frame names, we have to know how frames are organized: any frame belongs to a hierarchy of frames, and any hierarchy is under the root frame (which is the desktop).

'_parent' means the direct parent of the concerned frame, '_top' means the top frame of the hierarchy to which the concerned frame belongs (Note that '_top' does not mean the root frame, but a frame under the root frame), and '_blank' means a new top frame.

Objector 35B
What hierarchy does a Calc document have?

Hypothesizer 7
As for any Calc document, the frame we have gotten is a top frame, which has no child.

Objector 35B
Is the proper name the file name or something?

Hypothesizer 7
Actually, the proper name is blank unless you have set it explicitly.

Objector 35B
Then, if I start with the root frame, how can I uniquely identify the Calc document with the targer frame name and the search flag?

Hypothesizer 7
If some multiple documents with the blank names are opened, I do not think that you can.

Objector 35B
. . . What does that "-1" mean as the search flag?

Hypothesizer 7
Actually, that "-1" means nothing, as the argument is ignored when any special frame name is specified in the previous argument.

Objector 35B
I see . . .

You have said that I can reuse the dispatcher.

Hypothesizer 7
For the same URL, yes; for a different URL, no in general, although yes if the dispatcher happens to also be a dispatcher of the different URL.

Objector 35B
How can I know whether it happens to be so?

Hypothesizer 7
The easiest way will be to try reusing the dispatcher.

Objector 35B
Ah, so.

Hypothesizer 7
Then, we register the related information listener(s) into the dispatcher, and finally, we ask the dispatcher to dispatch our command, like this.

@Python Source Code
from com.sun.star.beans import PropertyValue

					l_dispatchInXNotifyingDispatch.addStatusListener (l_relatedInformationListener, l_urlInURL)
					l_dispatchArgumentProperties: List [PropertyValue] = []
					l_dispatchArgumentProperties.append (PropertyValue ())
					l_dispatchArgumentProperties [0].Name = "SynchronMode"
					l_dispatchArgumentProperties [0].Value = True
					l_dispatchArgumentProperties.append (PropertyValue ())
					l_dispatchArgumentProperties [1].Name = "ToPoint"
					l_dispatchArgumentProperties [1].Value = "$B$2"
					l_dispatchInXNotifyingDispatch.dispatchWithNotification (l_urlInURL, l_dispatchArgumentProperties, l_resultInformationListener)
					l_dispatchInXNotifyingDispatch.removeStatusListener (l_relatedInformationListener, l_urlInURL)

Objector 35A
It seems I can make it synchronous or asynchronous.

Hypothesizer 7
Yes.

The 'statusChanged' method can be called multiple times (or may not be called at all), and the 'dispatchFinished' method is called finally once.

In my UNO utility project, 'unoUtilitiesToBeDisclosed', which is used by my samples (a console Python UNO client, a files converter, etc.), the UNO document wrapper class, 'theBiasPlanet.unoUtilities.documentsHandling.UnoDocument', implements both of the listener interfaces and also has a method, 'dispatch', which executes any UNO dispatch command and returns the result information and the related information packed in the return value.

Objector 35B
Good for you.

Hypothesizer 7
'UnoDispatchSlotsConstantsGroup.BaseDispatchSlot a_dispatchSlot' as the first argument of the 'dispatch' method is an object that contains the URL and the argument names of the UNO dispatch command to be executed. In fact, the 'theBiasPlanet.unoUtilities.constantsGroups.UnoDispatchSlotsConstantsGroup' class holds such objects. I thought that putting such information in one place would be more favorable than scattering such information as string literals in programs.


3: The Way That Uses the UNO Dispatch Helper


Hypothesizer 7
As an aside, we will look at also the way that uses the UNO dispatcher helper.

As the above way is never worse (if better as it is in some aspects) in any aspect except that it requires several more lines of code, than this way, I do not use this way, but anyway, someone might want to use this way.

Objector 35A
It's definitely more than several lines, I say . . .

Hypothesizer 7
Well, the preparation of the URL object requires about 20 lines, but you can put them into a function (in fact, 'theBiasPlanet.unoUtilities.connectionsHandling.UnoObjectsContext.createUrlInURL' in my UNO utility project is such a function); the implementation of the listeners requires about 30 lines, but it can be skipped if you are not interested in getting the information; except them, the difference is just several lines.

Objector 35A
"except them" . . .

Hypothesizer 7
Anyway, even if it is 100 lines or 1,000 lines, it does not matter, because you can just copy them only once into your code (the number of lines to be copied will not make much diffrence in your trouble).

Objector 35A
. . .

Hypothesizer 7
Anyway, we get a 'com.sun.star.frame.XDispatchHelper' UNO proxy of a UNO dispatch helper UNO object, like this.

@Python Source Code
from com.sun.star.frame import XDispatchHelper

					l_dispatchHelperInXDispatchHelper: XDispatchHelper = cast (XDispatchHelper, l_remoteUnoObjectsContext.getServiceManager ().createInstanceWithContext ("com.sun.star.frame.DispatchHelper", l_remoteUnoObjectsContext))

Objector 35A
Ah-ha.

Hypothesizer 7
Then, we execute the UNO dispatch command, specifying the frame, the command URL, the target frame name, the target frame search flag, and the command arguments, like this.

@Python Source Code
from com.sun.star.beans import PropertyValue

					l_dispatchArgumentProperties: List [PropertyValue]  = []
					l_dispatchArgumentProperties.append (PropertyValue ())
					l_dispatchArgumentProperties [0].Name = "ToPoint"
					l_dispatchArgumentProperties [0].Value = "$B$2"
					l_commandResult: object  = l_dispatchHelperInXDispatchHelper.executeDispatch (l_frameInXDispatchProvider, ".uno:GoToCell", "_self", -1, l_dispatchArgumentProperties)


4: The Conclusion and Beyond


Hypothesizer 7
Now, we know how to execute any UNO dispatch command and get the result information and the related information in Python.

Of course, we can execute any UNO dispatch command also in some other programming languages (Java, C++, .Net Framework (C# and Visual.Basic, usually), LibreOffice Basic or Apache OpenOffice Basic, BeanShell, and JavaScript); we have learned how to do so in Java, in C++, in C#, and in LibreOffice or Apache OpenOffice Basic in some previous articles.

There are an unfinished (so far) list of UNO dispatch commands for the LibreOffice foundations and the specifications of the listed commands and an unfinished (so far) list of UNO dispatch commands for LibreOffice Calc and the specifications of the listed commands.

Although executing some UNO dispatch commands is a way to handle any LibreOffice or Apache OpenOffice instance, it does not cover all of what we can do to handle the LibreOffice or Apache OpenOffice instance: we can do more by directly handling some UNO objects (please refer to the table of contents of this series).


References


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