Difference between revisions of "UNO component packaging"

From Apache OpenOffice Wiki
Jump to: navigation, search
(More information on scripts, reorganization of the page)
(See also)
 
(15 intermediate revisions by 2 users not shown)
Line 1: Line 1:
{{Documentation/DevGuide/ScriptingTOC
+
=Python UNO component=
|ShowPrevNext=block
+
This page shows you how to pack and deploy already written UNO component. I'm using my ''Wavelet'' class as an example of a very simple component.  
|PrevPage=Documentation/DevGuide/Scripting/Macro Recording
+
|NextPage=Documentation/DevGuide/Scripting/How the Scripting Framework Works
+
}}
+
{{Documentation/DevGuideLanguages|Documentation/DevGuide/Scripting/{{SUBPAGENAME}}}}
+
{{DISPLAYTITLE:Writing Macros}}
+
__NOTOC__
+
=== The HelloWorld macro ===
+
Here is a comparison of HelloWorld macros in the different script languages available in {{PRODUCTNAME}}.
+
  
==== Basic ====
+
More detailed info is in the [[Documentation/DevGuide/WritingUNO/Deployment_Options_for_Components|Developers Guide]].
<source lang="oobas">
+
Sub writeHelloWorld()
+
Dim oDoc As Object, xText As Object, xTextRange As Object
+
  
oDoc = ThisComponent
+
=Python loader=
xText = oDoc.getText()
+
The Python loader is not able load classes that are not it its own path. Thus it's necessary to copy any classes or modules you will use into the OPenOffice Python lib directory, and it may be necessary to restart OOo before they are picked up.
xTextRange = xText.getEnd()
+
xTextRange.CharBackColor = 1234567
+
xTextRange.CharHeight = 16.0
+
' CharUnderline receives a group constant
+
xTextRange.CharUnderline = com.sun.star.awt.FontUnderline.WAVE
+
' CharPosture receives an enum
+
xTextRange.CharPosture = com.sun.star.awt.FontSlant.ITALIC
+
xTextRange.setString( "Hello World (in Basic)" )
+
End Sub
+
</source>
+
Basic interprets pairs of get and set methods at UNO objects as object properties if they follow this pattern:
+
  
  SomeType getSomeProperty()
+
=Sample Python component=
  void setSomeProperty(SomeType aValue)
+
  
Using these facilities some lines can be simplified:
+
==Wavelet class==
<source lang="oobas">
+
We will use my sample ''Wavelet'' class which replaces space with non-breaking space before the Czech prepositions. Put the following code in the ''Wavelet.py'' file.
xText = oDoc.Text
+
xTextRange = xText.End
+
xTextRange.String = "Hello World (in Basic)"
+
</source>
+
  
Service properties can be directly accessed in Basic, it is not necessary to use <code>getPropertyValue</code> or <code>setPropertyValue</code> methods.
+
<syntaxhighlight lang="python">
  
==== BeanShell ====
+
import uno
As BeanShell accepts typeless variables, the code is simplified compared to Java.
+
import unohelper
<source lang="java">
+
from com.sun.star.task import XJobExecutor
import com.sun.star.uno.UnoRuntime;
+
import com.sun.star.text.XTextDocument;
+
import com.sun.star.text.XText;
+
import com.sun.star.text.XTextRange;
+
import com.sun.star.beans.XPropertySet;
+
import com.sun.star.awt.FontSlant;
+
import com.sun.star.awt.FontUnderline;
+
  
oDoc = XSCRIPTCONTEXT.getDocument();
+
class Wavelet( unohelper.Base, XJobExecutor ):
 +
    def __init__( self, ctx ):
 +
        self.ctx = ctx
 +
 +
    def trigger( self, args ):
 +
        desktop = self.ctx.ServiceManager.createInstanceWithContext(
 +
            "com.sun.star.frame.Desktop", self.ctx )
 +
 +
        doc = desktop.getCurrentComponent()
 +
 +
        try:
 +
            search = doc.createSearchDescriptor()
 +
            search.SearchRegularExpression = True
 +
            search.SearchString = "\\<(k|s|v|z|o|u|i|a) "
 +
           
 +
            found = doc.findFirst( search )
 +
            while found:
 +
                found.String = found.String.replace( " ", u"\xa0" )
 +
                found = doc.findNext( found.End, search)
 +
 +
        except:
 +
            pass
 +
</syntaxhighlight>
  
xTextDoc = UnoRuntime.queryInterface(XTextDocument.class,oDoc);
+
==Wavelet class registration==
xText = xTextDoc.getText();
+
You have to tell the {{AOo}} what is the main class of your component. Put the following code at the end of your ''Wavelet.py'' file.  
xTextRange = xText.getEnd();
+
pv = UnoRuntime.queryInterface(XPropertySet.class, xTextRange);
+
  
pv.setPropertyValue("CharBackColor", 1234567);
+
<syntaxhighlight lang="python">
pv.setPropertyValue("CharHeight", 16.0);
+
g_ImplementationHelper = unohelper.ImplementationHelper()
// CharUnderline receives a group constant
+
g_ImplementationHelper.addImplementation(
pv.setPropertyValue("CharUnderline", com.sun.star.awt.FontUnderline.WAVE);
+
        Wavelet,
// CharPosture receives an enum
+
        "name.vojta.openoffice.Wavelet",
pv.setPropertyValue("CharPosture", com.sun.star.awt.FontSlant.ITALIC) ;
+
        ("com.sun.star.task.Job",),)
 +
</syntaxhighlight>
  
xTextRange.setString( "Hello World (in BeanShell)" );
+
=Component integration=
return 0;
+
</source>
+
  
BeanShell interprets pairs of get and set methods at UNO objects as object properties if they follow this pattern:
+
==Icons==
 +
You can create your own icons for the Menu item or the Toolbar button. The easiest way is to create two true color BMP files - the first one should be 26×26 and the second one should be 16×16.
  
  SomeType getSomeProperty()
+
If you want to use alpha (transparent color), use ''#FF00FF'' (RGB) color.
  void setSomeProperty(SomeType aValue)
+
  
Using these facilities some lines can be simplified:
+
This possibility is optional and you're not forced to create icons.
  
<source lang="java">
+
==Addons.xcu==
  xText = xTextDoc.Text;
+
Component integration configuration is in the ''Addons.xcu'' file.  
  xTextRange = xText.End;
+
  xTextRange.String = "Hello World (in BeanShell)";
+
</source>
+
  
Service properties are only accessed with <code>getPropertyValue</code> or <code>setPropertyValue</code>.
+
===Header===
 +
The header of this file should contain:
  
==== JavaScript ====
+
<syntaxhighlight lang="xml">
As JavaScript accepts typeless variables, the code is simplified compared to Java.
+
<?xml version="1.0" encoding="UTF-8"?>
<source lang="javascript">
+
<oor:component-data xmlns:oor="http://openoffice.org/2001/registry"
importClass(Packages.com.sun.star.uno.UnoRuntime)
+
  xmlns:xs="http://www.w3.org/2001/XMLSchema" oor:name="Addons"
importClass(Packages.com.sun.star.text.XTextDocument)
+
  oor:package="org.openoffice.Office">
importClass(Packages.com.sun.star.text.XText)
+
importClass(Packages.com.sun.star.text.XTextRange)
+
    <node oor:name="AddonUI">
importClass(Packages.com.sun.star.beans.XPropertySet)
+
</syntaxhighlight>
importClass(Packages.com.sun.star.awt.FontSlant)
+
importClass(Packages.com.sun.star.awt.FontUnderline)
+
  
oDoc = XSCRIPTCONTEXT.getDocument()
+
===Office Menu Bar===
xTextDoc = UnoRuntime.queryInterface(XTextDocument,oDoc)
+
Following part creates new menu item ''Czech'' (or ''Cestina'', depends on the UI language) and the ''Wavelet'' menu item.
xText = xTextDoc.getText()
+
xTextRange = xText.getEnd()
+
pv = UnoRuntime.queryInterface(XPropertySet, xTextRange)
+
  
pv.setPropertyValue("CharHeight", 16.0) // Double
+
This menu item is associated with the ''name.vojta.openoffice.Wavelet?execute'' ''URL''. It means, that when you click on the menuitem, ''Wavelet.trigger'' method will be executed with the ''execute'' as an argument.  
// CharBackColor receives an Integer
+
pv.setPropertyValue("CharBackColor", new java.lang.Integer(1234567))
+
// CharUnderline receives a group constant
+
pv.setPropertyValue("CharUnderline",
+
  new java.lang.Short(Packages.com.sun.star.awt.FontUnderline.WAVE))
+
// CharPosture receives an enum
+
pv.setPropertyValue("CharPosture", Packages.com.sun.star.awt.FontSlant.ITALIC)
+
xTextRange.setString( "Hello World (in JavaScript)" )
+
</source>
+
  
JavaScript does not accept the simplifications seen in Basic and BeanShell.
+
This menu will be visible in the {{AOo}} Writer only.  
  
Setting an integer value to a property requires to use a java class.
+
<syntaxhighlight lang="xml">
 +
        <node oor:name="OfficeMenuBar">
 +
            <node oor:name="name.vojta.openoffice.Wavelet" oor:op="replace">
 +
                <prop oor:name="Title" oor:type="xs:string">
 +
                    <value/>
 +
                    <value xml:lang="en-US">Czech</value>
 +
                    <value xml:lang="cs">Cestina</value>
 +
                </prop>
 +
                <prop oor:name="Target" oor:type="xs:string">
 +
                    <value>_self</value>
 +
                </prop>
 +
                <prop oor:name="ImageIdentifier" oor:type="xs:string">
 +
                    <value/>
 +
                </prop>
 +
                <node oor:name="Submenu">
 +
                    <node oor:name="m1" oor:op="replace">
 +
                        <prop oor:name="URL" oor:type="xs:string">
 +
                            <value>service:name.vojta.openoffice.Wavelet?execute</value>
 +
                        </prop>
 +
                        <prop oor:name="Title" oor:type="xs:string">
 +
                            <value/>
 +
                            <value xml:lang="en-US">Wavelet</value>
 +
                            <value xml:lang="cs">Vlnka</value>
 +
                        </prop>
 +
                        <prop oor:name="Target" oor:type="xs:string">
 +
                            <value>_self</value>
 +
                        </prop>
 +
                        <prop oor:name="Context" oor:type="xs:string">
 +
                            <value>com.sun.star.text.TextDocument</value>
 +
                        </prop>
 +
                    </node>
 +
                </node>
 +
            </node>
 +
</node>
 +
</syntaxhighlight>
  
==== Java ====
+
===Office Toolbar===
Other sections of this document provide numerous Java examples. Here is the HelloWorld provided in {{PRODUCTNAME}}
+
Following part creates new toolbar (with name ''Add-on 1'', ''Add-on 2'', …) and one button. This button is associated with the ''name.vojta.openoffice.Wavelet?execute'' ''URL''. It means that the ''Wavelet.trigger'' method will be executed when you click on the button on the toolbar. ''execute'' will be passed as an argument.  
<source lang="java">
+
  import com.sun.star.uno.UnoRuntime;
+
  import com.sun.star.frame.XModel;
+
  import com.sun.star.text.XTextDocument;
+
  import com.sun.star.text.XTextRange;
+
  import com.sun.star.text.XText;
+
  import com.sun.star.script.provider.XScriptContext;
+
 
+
  public class HelloWorld {
+
      public static void printHW(XScriptContext xScriptContext)
+
      {
+
          XModel xDocModel = xScriptContext.getDocument();
+
   
+
          // getting the text document object
+
          XTextDocument xtextdocument = (XTextDocument) UnoRuntime.queryInterface(
+
              XTextDocument.class, xDocModel);
+
     
+
          XText xText = xtextdocument.getText();
+
          XTextRange xTextRange = xText.getEnd();
+
          xTextRange.setString( "Hello World (in Java)" );
+
      }
+
  }
+
</source>
+
  
===== Compiling and Deploying Java macros =====
+
{{Note | You can't rename the toolbar. It's due to the backwards compatibility with the 1.1.x version.}}
  
Because Java is a compiled language it is not possible to execute Java source code as a macro directly from within {{PRODUCTNAME}}. The code must first be compiled and then deployed within a {{PRODUCTNAME}} installation or document. The following steps show how to create a Java macro using the HelloWorld example code:
+
<syntaxhighlight lang="xml">
 +
        <node oor:name="OfficeToolBar">
 +
            <node oor:name="name.vojta.openoffice.Wavelet" oor:op="replace">
 +
                <node oor:name="m1" oor:op="replace">
 +
                    <prop oor:name="URL" oor:type="xs:string">
 +
                        <value>service:name.vojta.openoffice.Wavelet?execute</value>
 +
                    </prop>
 +
                    <prop oor:name="ImageIdentifier" oor:type="xs:string">
 +
                        <value/>
 +
                    </prop>
 +
                    <prop oor:name="Title" oor:type="xs:string">
 +
                        <value/>
 +
                        <value xml:lang="en-US">Wavelet</value>
 +
                        <value xml:lang="cs">Vlnka</value>
 +
                    </prop>
 +
                    <prop oor:name="Target" oor:type="xs:string">
 +
                        <value>_self</value>
 +
                    </prop>
 +
                    <prop oor:name="Context" oor:type="xs:string">
 +
                        <value>com.sun.star.text.TextDocument</value>
 +
                    </prop>
 +
                </node>
 +
            </node>
 +
</node>
 +
</syntaxhighlight>
  
* Create a ''HelloWorld'' directory for your macro
+
===Icons===
* Create a ''HelloWorld.java'' file using the HelloWorld source code
+
You can associate your icons with any ''URL''. We will associate our icons with ''name.vojta.openoffice.Wavelet?execute'' ''URL''. It means that all controls (menu item, button, …) associated with the same ''URL'' will use these icons.  
* Compile the ''HelloWorld.java'' file. The following jar files from the ''program/classes'' directory of a {{PRODUCTNAME}} installation must be in the classpath: ''ridl.jar'', ''unoil.jar'', ''jurt.jar''
+
* Create a ''HelloWorld.jar'' file containing the ''HelloWorld.class'' file
+
* Create a ''parcel-descriptor.xml'' file for your macro
+
<source lang="xml">
+
  <?xml version="1.0" encoding="UTF-8"?>
+
 
+
  <parcel language="Java" xmlns:parcel="scripting.dtd">
+
    <script language="Java">
+
      <locale lang="en">
+
        <displayname value="HelloWorld"/>
+
        <description>
+
          Prints "Hello World".
+
        </description>
+
      </locale>
+
      <functionname value="HelloWorld.printHW"/>
+
      <logicalname value="HelloWorld.printHW"/>
+
      <languagedepprops>
+
          <prop name="classpath" value="HelloWorld.jar"/>
+
      </languagedepprops>
+
    </script>
+
  </parcel>
+
</source>
+
The ''parcel-descriptor.xml'' file is used by the Scripting Framework to find macros. The functionname element indicates the name of the Java method which should be executed as a macro. The classpath element can be used to indicate any jar or class files which are used by the macro. If the classpath element is not included, then the directory in which the ''parcel-desciptor.xml'' file is found and any jar files in that directory will be used as the classpath. The necessary Java UNO classes are available automatically.
+
  
* Copy the HelloWorld directory into the ''share/Scripts/java'' directory of a {{PRODUCTNAME}} installation or into the ''user/Scripts/java'' directory of a user installation. If you want to deploy the macro to a document you need to place it in a ''Scripts/java'' directory within the document zip file.
+
{{Note | %origin% is the UNO component package. We will explain it later.}}
* If {{PRODUCTNAME}} is running, you will need to restart it in order for the macro to appear in the Macro Selector dialog.
+
  
{{Documentation/Note|The ''parcel-descriptor.xml'' file is also used to detect BeanShell and JavaScript macros. It is created automatically when creating macros using the Organizer dialogs for BeanShell and JavaScript.}}
+
<syntaxhighlight lang="xml">
 +
        <node oor:name="Images">
 +
            <node oor:name="name.vojta.openoffice.Wavelet.image1" oor:op="replace">
 +
                <prop oor:name="URL">
 +
                    <value>service:name.vojta.openoffice.Wavelet?execute</value>
 +
                </prop>
 +
                <node oor:name="UserDefinedImages">
 +
                    <prop oor:name="ImageSmallURL" oor:type="xs:string">
 +
                        <value>%origin%/images/WaveletSmall.bmp</value>
 +
                    </prop>
 +
                    <prop oor:name="ImageBigURL" oor:type="xs:string">
 +
                        <value>%origin%/images/WaveletBig.bmp</value>
 +
                    </prop>
 +
                </node>
 +
            </node>
 +
</node>
 +
</syntaxhighlight>
  
==== Python ====
+
===Footer===
 +
Close all opened sections.
  
A Python module may contain several scripts.
+
<syntaxhighlight lang="xml">
<source lang="python">
+
    </node>
def HelloPython( ):
+
</oor:component-data>
import uno
+
</syntaxhighlight>
  
def HelloPython( ):
+
=UNO component package=
    oDoc = XSCRIPTCONTEXT.getDocument()
+
UNO component package is a simple ZIP file. Obviously it contains the ''Addons.xcu'' file, ''images'' directory with icons and the Python component implementation.  
    xText = oDoc.getText()
+
    xTextRange = xText.getEnd()
+
   
+
    xTextRange.CharHeight = 16.0
+
    xTextRange.CharBackColor = 1234567
+
    # CharUnderline receives a group constant
+
    xTextRange.CharUnderline = uno.getConstantByName("com.sun.star.awt.FontUnderline.WAVE")
+
    # CharPosture receives an enum
+
    xTextRange.CharPosture = uno.getConstantByName("com.sun.star.awt.FontSlant.ITALIC")
+
   
+
    xTextRange.setString( "Hello World (in Python)" )
+
    return None
+
</source>
+
Python interprets pairs of get and set methods at UNO objects as object properties if they follow this pattern:
+
  
  SomeType getSomeProperty()
+
==Packing==
  void setSomeProperty(SomeType aValue)
+
You can pack our sample package with the following command:
  
Using these facilities some lines can be simplified:
+
<syntaxhighlight lang="bash">
<source lang="python">
+
zip -r Wavelet.uno.zip Addons.xcu Wavelet.py images
    xText = oDoc.Text
+
</syntaxhighlight>
    xTextRange = xText.End
+
The final ''Wavelet.uno.zip'' package should contain these files:
    xTextRange.String = "Hello World (in Python)"
+
</source>
+
Service properties can be directly accessed in Python, no need to use <code>getPropertyValue</code> or <code>setPropertyValue</code>.
+
  
=== Features of script languages supported by {{PRODUCTNAME}} ===
+
<syntaxhighlight lang="bash">
 +
Wavelet.uno.zip
 +
  Addons.xcu
 +
  Wavelet.py
 +
  images/WaveletBig.bmp
 +
  images/WaveletSmall.bmp
 +
</syntaxhighlight>
  
{|border="1" cellpadding=4 style="border-collapse:collapse;"
+
{{Note | The ''%origin%'' is ''Wavelet.uno.zip'', thus the ''%origin%/images/WaveletBig.bmp'' points to the ''WaveletBig.bmp'' file in your ''Wavelet.uno.zip'' file.}}
|-bgcolor=#EDEDED
+
!Language feature
+
!Basic
+
!BeanShell
+
!JavaScript
+
!Java macro
+
!Python
+
|-
+
|Interpreted
+
|Yes
+
|Yes
+
|Yes
+
|No
+
|Yes
+
|-
+
|Integrated editor
+
|Yes, colored
+
|Yes
+
|Yes
+
|No
+
|No
+
|-
+
|Integrated debugger
+
|Yes
+
|No
+
|Yes
+
|No
+
|No
+
|-
+
|Script organizer
+
|Yes
+
|Yes
+
|Yes
+
|No
+
|No
+
|-
+
|Script encryption
+
|Possible
+
|No
+
|No
+
|No
+
|No
+
|-
+
|Typeless variables
+
|Yes: Variant
+
|Yes
+
|Yes
+
|No
+
|Yes
+
|-
+
|Getter/Setter
+
|Yes
+
|Yes
+
|No
+
|No
+
|Yes
+
|-
+
|Direct property access
+
|Yes
+
|No
+
|No
+
|No
+
|Yes
+
|-
+
|Function for Calc formula
+
|Yes
+
|No
+
|No
+
|No
+
|No
+
|-
+
|UNO component creation
+
|No
+
|No
+
|No
+
|Yes
+
|Yes
+
|}
+
  
 +
==Installation==
 +
You can install your UNO component with the Extension manager (somewhere in the ''Tools'' menu) or with the ''unopkg'' tool.
  
=== Using the {{PRODUCTNAME}} API from macros ===
+
<syntaxhighlight lang="bash">
 +
/opt/openoffice.org1.9.103/program/unopkg add Wavelet.uno.zip
 +
</syntaxhighlight>
  
BeanShell, JavaScript, Java, Python macros are supplied with a variable of type <idl>com.sun.star.script.provider.XScriptContext</idl> which can be used to access the {{PRODUCTNAME}} API. This interface has three methods:
+
==Uninstallation==
 +
You can uninstall your UNO component with the Extension manager (somewhere in the ''Tools'' menu) or with the ''unopkg'' tool.  
  
* <idl>com.sun.star.frame.XModel</idl> <code>getDocument( )</code>
+
<source lang=bash>
:Returns the <code>XModel</code> interface of the document for which the macro was invoked (see [[Documentation/DevGuide/OfficeDev/Using the Component Framework|Using the Component Framework]])
+
/opt/openoffice.org1.9.103/program/unopkg remove Wavelet.uno.zip
* <idl>com.sun.star.frame.XDesktop</idl> <code>getDesktop( )</code>
+
</source>
:Returns the <code>XDesktop</code> interface for the application which can be used to access open document, and load documents (see [[Documentation/DevGuide/OfficeDev/Using the Desktop|Using the Desktop]])
+
* <idl>com.sun.star.uno.XComponentContext</idl> <code>getComponentContext( )</code>
+
:Returns the <code>XComponentContext</code> interface which is used to create instances of services (see [[Documentation/DevGuide/ProUNO/Service Manager and Component Context|Service Manager and Component Context]])
+
 
+
Depending on the language the macro accesses <code>XScriptContext</code> in different ways:
+
 
+
* '''BeanShell''': Using the global variable <code>XSCRIPTCONTEXT</code>
+
  oDoc = XSCRIPTCONTEXT.getDocument();
+
 
+
* '''JavaScript''': Using the global variable <code>XSCRIPTCONTEXT</code>
+
  oDoc = XSCRIPTCONTEXT.getDocument();
+
 
+
* '''Java''': The first parameter passed to the macro method is always of type <code>XScriptContext</code>
+
  Xmodel xDocModel = xScriptContext.getDocument();
+
 
+
* '''Python''': Using the global variable <code>XSCRIPTCONTEXT</code>
+
  oDoc = XSCRIPTCONTEXT.getDocument()
+
 
+
=== Handling arguments passed to macros ===
+
 
+
In certain cases arguments may be passed to macros, for example, when a macro is assigned to a button in a document. In this case the arguments are passed to the macro as follows:
+
 
+
* '''BeanShell''': In the global <code>Object[]</code> variable <code>ARGUMENTS</code>
+
  event = (ActionEvent) ARGUMENTS[0];
+
 
+
* '''JavaScript''': In the global <code>Object[]</code> variable <code>ARGUMENTS</code>
+
  event = ARGUMENTS[0];
+
 
+
* '''Java''': The arguments are passed as an <code>Object[]</code> in the second parameter to the macro method
+
  public void handleButtonPress(
+
      XScriptContext xScriptContext, Object[] args)
+
 
+
Each of the arguments in the <code>Object[]</code> are of the UNO type Any. For more information on how the Any type is used in Java see [[Documentation/DevGuide/ProUNO/Java/Type Mappings|Type Mappings]].
+
 
+
The ButtonPressHandler macros in the Highlight library of a {{PRODUCTNAME}} installation show how a macro can handle arguments.
+
 
+
* '''Python''': The arguments are passed as parameters of the called function.
+
 
+
 
+
=== Creating dialogs from macros ===
+
 
+
Dialogs which have been built in the Dialog Editor can be loaded by macros using the <idl>com.sun.star.awt.XDialogProvider</idl> API. The method <code>createDialog()</code> from interface <code>XDialogProvider</code> uses a string as a parameter. This string is the URL to the dialog. It is formed as follows:
+
 
+
  vnd.sun.star.script:DIALOGREF?location=[application|document]
+
  
where <code>DIALOGREF</code> is the name of the dialog that you want to create, and location is either application or document depending on where the dialog is stored.
+
=Python component testing=
 +
It's not necessary to install the Python UNO component to test it. You can connect to the running {{AOo}} instance and test your UNO component directly.  
  
For example if you wanted to load dialog called MyDialog, which is in a Dialog Library called MyDialogLibrary in the {{PRODUCTNAME}} dialogs area of your installation then the URL would be:
+
==Code==
 +
Append the following code at the end of the ''Wavelet.py'' file.
  
  vnd.sun.star.script:MyDialogLibrary.MyDialog?location=application
+
<syntaxhighlight lang="python">
 +
if __name__ == "__main__":
 +
    import os
 +
 +
    # Start OpenOffice, listen for connections and open testing document
 +
    os.system( "/etc/openoffice.org-1.9/program/soffice '-accept=socket,host=localhost,port=2002;urp;' -writer ./WaveletTest.odt &" )
 +
 +
    # Get local context info
 +
    localContext = uno.getComponentContext()
 +
    resolver = localContext.ServiceManager.createInstanceWithContext(
 +
        "com.sun.star.bridge.UnoUrlResolver", localContext )
 +
 +
    ctx = None
 +
 +
    # Wait until the OpenOffice starts and connection is established
 +
    while ctx == None:
 +
        try:
 +
            ctx = resolver.resolve(
 +
                "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext" )
 +
        except:
 +
            pass
 +
 +
    # Trigger our job
 +
    wavelet = Wavelet( ctx )
 +
    wavelet.trigger( () )
 +
</syntaxhighlight>
  
If you wanted to load a dialog called MyDocumentDialog which in a library called MyDocumentLibrary which is located in a document then the URL would be:
 
  
  vnd.sun.star.script:MyDocumentLibrary.MyDocumentDialog?location=document
 
  
The following code shows how to create a dialog from a Java macro:
+
==Testing==
<source lang="java">
+
To test your UNO component, just run your ''Wavelet.py'' in the Python interpreter.  
  public XDialog getDialog(XScriptContext context)
+
  {
+
      XDialog theDialog;
+
 
+
      // We must pass the XModel of the current document when creating a DialogProvider object
+
      Object[] args = { context.getDocument() };
+
   
+
      Object obj;
+
      try {
+
          obj = xmcf.createInstanceWithArgumentsAndContext(
+
              "com.sun.star.awt.DialogProvider", args, context.getComponentContext());
+
      }
+
      catch (com.sun.star.uno.Exception e) {
+
          System.err.println("Error getting DialogProvider object");
+
          return null;
+
      }
+
 
+
      XDialogProvider xDialogProvider = (XDialogProvider)
+
          UnoRuntime.queryInterface(XDialogProvider.class, obj);
+
     
+
      // Got DialogProvider, now get dialog
+
      try {
+
          theDialog = xDialogProvider.createDialog(
+
              "vnd.sun.star.script:MyDialogLibrary.MyDialog?location=application");
+
      }
+
      catch (java.lang.Exception e) {
+
          System.err.println("Got exception on first creating dialog: " + e.getMessage());
+
      }
+
      return theDialog;
+
  }
+
</source>
+
  
=== Calling a macro with the Scripting Framework ===
+
<syntaxhighlight lang="bash">
 +
/opt/openoffice.org1.9.103/program/python ./Wavelet.py
 +
</syntaxhighlight>
  
The service <idl>com.sun.star.script.provider.ScriptProvider</idl> exports interface <idls>com.sun.star.script.provider.XScriptProvider</idls> which offers method <code>getScript( )</code>. This method returns an interface <idls>com.sun.star.script.provider.XScript</idls> which offers the method <code>invoke( )</code> which will call the script with parameters if necessary.
+
==Resume==
 +
This part does this:
  
The identity and location of the called script is contained in an URI, which follows a [[Documentation/DevGuide/Scripting/Scripting_Framework_URI_Specification|particular syntax]].
+
starts {{AOo}} and opens the ''WaveletTest.odt'' file
  
 +
loop until the connection will be established
  
Here are links to some code examples written by the community:
+
start your component in the running {{AOo}} context
* [http://codesnippets.services.openoffice.org/Office/Office.HowToCallJavaProgramUsingScriptingFramework.snip Java calling a Java script]
+
* [http://www.oooforum.org/forum/viewtopic.phtml?t=69328 Java calling a Basic macro]
+
* [http://www.oooforum.org/forum/viewtopic.phtml?t=59534 Basic macro calling a Python script]
+
* [http://www.oooforum.org/forum/viewtopic.phtml?t=69350 Python script calling a Basic macro]
+
  
 +
{{Note | Do not forget to remove this part before real UNO component packaging. Or comment it out.}}
  
 +
=See also=
 +
* [[Documentation/DevGuide/Extensions/Extensions|Extensions]] chapter of the Developers Guide
 +
* Using C++ with {{AOo}} SDK : [[Constructing_Components| Constructing Component in C++]]
 +
*[[Tutorial_UNO_Library|UNO tutorial]]
 +
*[[Tutorial_UNO_IDL|UNO IDL]]
 +
* [[Uno/Article/Types%26Reflection]]
 +
* Daniel Bölzle's [https://www.openoffice.org/udk/cpp/man/component_tutorial.html tutorial] : Writing a simple UNO component.
 +
* [[UNO_component_packaging|Component with Python]]
  
{{PDL1}}
 
  
[[Category:Documentation/Developer's Guide/Scripting]]
+
[[Category:Uno]]
 +
[[Category:Extensions]]

Latest revision as of 15:07, 3 February 2021

Python UNO component

This page shows you how to pack and deploy already written UNO component. I'm using my Wavelet class as an example of a very simple component.

More detailed info is in the Developers Guide.

Python loader

The Python loader is not able load classes that are not it its own path. Thus it's necessary to copy any classes or modules you will use into the OPenOffice Python lib directory, and it may be necessary to restart OOo before they are picked up.

Sample Python component

Wavelet class

We will use my sample Wavelet class which replaces space with non-breaking space before the Czech prepositions. Put the following code in the Wavelet.py file.

import uno
import unohelper
from com.sun.star.task import XJobExecutor
 
class Wavelet( unohelper.Base, XJobExecutor ):
    def __init__( self, ctx ):
        self.ctx = ctx
 
    def trigger( self, args ):
        desktop = self.ctx.ServiceManager.createInstanceWithContext(
            "com.sun.star.frame.Desktop", self.ctx )
 
        doc = desktop.getCurrentComponent()
 
        try:
            search = doc.createSearchDescriptor()
            search.SearchRegularExpression = True
            search.SearchString = "\\<(k|s|v|z|o|u|i|a) "
 
            found = doc.findFirst( search )
            while found:
                found.String = found.String.replace( " ", u"\xa0" )
                found = doc.findNext( found.End, search)
 
        except:
            pass

Wavelet class registration

You have to tell the Apache OpenOffice what is the main class of your component. Put the following code at the end of your Wavelet.py file.

g_ImplementationHelper = unohelper.ImplementationHelper()
g_ImplementationHelper.addImplementation(
        Wavelet,
        "name.vojta.openoffice.Wavelet",
        ("com.sun.star.task.Job",),)

Component integration

Icons

You can create your own icons for the Menu item or the Toolbar button. The easiest way is to create two true color BMP files - the first one should be 26×26 and the second one should be 16×16.

If you want to use alpha (transparent color), use #FF00FF (RGB) color.

This possibility is optional and you're not forced to create icons.

Addons.xcu

Component integration configuration is in the Addons.xcu file.

Header

The header of this file should contain:

<?xml version="1.0" encoding="UTF-8"?>
<oor:component-data xmlns:oor="http://openoffice.org/2001/registry"
  xmlns:xs="http://www.w3.org/2001/XMLSchema" oor:name="Addons"
  oor:package="org.openoffice.Office">
 
    <node oor:name="AddonUI">

Office Menu Bar

Following part creates new menu item Czech (or Cestina, depends on the UI language) and the Wavelet menu item.

This menu item is associated with the name.vojta.openoffice.Wavelet?execute URL. It means, that when you click on the menuitem, Wavelet.trigger method will be executed with the execute as an argument.

This menu will be visible in the Apache OpenOffice Writer only.

        <node oor:name="OfficeMenuBar">
            <node oor:name="name.vojta.openoffice.Wavelet" oor:op="replace">
                <prop oor:name="Title" oor:type="xs:string">
                    <value/>
                    <value xml:lang="en-US">Czech</value>
                    <value xml:lang="cs">Cestina</value>
                </prop>
                <prop oor:name="Target" oor:type="xs:string">
                    <value>_self</value>
                </prop>
                <prop oor:name="ImageIdentifier" oor:type="xs:string">
                    <value/>
                </prop>
                <node oor:name="Submenu">
                    <node oor:name="m1" oor:op="replace">
                        <prop oor:name="URL" oor:type="xs:string">
                            <value>service:name.vojta.openoffice.Wavelet?execute</value>
                        </prop>
                        <prop oor:name="Title" oor:type="xs:string">
                            <value/>
                            <value xml:lang="en-US">Wavelet</value>
                            <value xml:lang="cs">Vlnka</value>
                        </prop>
                        <prop oor:name="Target" oor:type="xs:string">
                            <value>_self</value>
                        </prop>
                        <prop oor:name="Context" oor:type="xs:string">
                            <value>com.sun.star.text.TextDocument</value>
                        </prop>
                    </node>
                </node>
            </node>
</node>

Office Toolbar

Following part creates new toolbar (with name Add-on 1, Add-on 2, …) and one button. This button is associated with the name.vojta.openoffice.Wavelet?execute URL. It means that the Wavelet.trigger method will be executed when you click on the button on the toolbar. execute will be passed as an argument.

Documentation note.png You can't rename the toolbar. It's due to the backwards compatibility with the 1.1.x version.
        <node oor:name="OfficeToolBar">
            <node oor:name="name.vojta.openoffice.Wavelet" oor:op="replace">
                <node oor:name="m1" oor:op="replace">
                    <prop oor:name="URL" oor:type="xs:string">
                        <value>service:name.vojta.openoffice.Wavelet?execute</value>
                    </prop>
                    <prop oor:name="ImageIdentifier" oor:type="xs:string">
                        <value/>
                    </prop>
                    <prop oor:name="Title" oor:type="xs:string">
                        <value/>
                        <value xml:lang="en-US">Wavelet</value>
                        <value xml:lang="cs">Vlnka</value>
                    </prop>
                    <prop oor:name="Target" oor:type="xs:string">
                        <value>_self</value>
                    </prop>
                    <prop oor:name="Context" oor:type="xs:string">
                        <value>com.sun.star.text.TextDocument</value>
                    </prop>
                </node>
            </node>
</node>

Icons

You can associate your icons with any URL. We will associate our icons with name.vojta.openoffice.Wavelet?execute URL. It means that all controls (menu item, button, …) associated with the same URL will use these icons.

Documentation note.png  %origin% is the UNO component package. We will explain it later.
        <node oor:name="Images">
            <node oor:name="name.vojta.openoffice.Wavelet.image1" oor:op="replace">
                <prop oor:name="URL">
                    <value>service:name.vojta.openoffice.Wavelet?execute</value>
                </prop>
                <node oor:name="UserDefinedImages">
                    <prop oor:name="ImageSmallURL" oor:type="xs:string">
                        <value>%origin%/images/WaveletSmall.bmp</value>
                    </prop>
                    <prop oor:name="ImageBigURL" oor:type="xs:string">
                        <value>%origin%/images/WaveletBig.bmp</value>
                    </prop>
                </node>
            </node>
</node>

Footer

Close all opened sections.

    </node>
 </oor:component-data>

UNO component package

UNO component package is a simple ZIP file. Obviously it contains the Addons.xcu file, images directory with icons and the Python component implementation.

Packing

You can pack our sample package with the following command:

zip -r Wavelet.uno.zip Addons.xcu Wavelet.py images

The final Wavelet.uno.zip package should contain these files:

Wavelet.uno.zip
  Addons.xcu
  Wavelet.py
  images/WaveletBig.bmp
  images/WaveletSmall.bmp
Documentation note.png The %origin% is Wavelet.uno.zip, thus the %origin%/images/WaveletBig.bmp points to the WaveletBig.bmp file in your Wavelet.uno.zip file.

Installation

You can install your UNO component with the Extension manager (somewhere in the Tools menu) or with the unopkg tool.

/opt/openoffice.org1.9.103/program/unopkg add Wavelet.uno.zip

Uninstallation

You can uninstall your UNO component with the Extension manager (somewhere in the Tools menu) or with the unopkg tool.

/opt/openoffice.org1.9.103/program/unopkg remove Wavelet.uno.zip

Python component testing

It's not necessary to install the Python UNO component to test it. You can connect to the running Apache OpenOffice instance and test your UNO component directly.

Code

Append the following code at the end of the Wavelet.py file.

if __name__ == "__main__":
    import os
 
    # Start OpenOffice, listen for connections and open testing document
    os.system( "/etc/openoffice.org-1.9/program/soffice '-accept=socket,host=localhost,port=2002;urp;' -writer ./WaveletTest.odt &" )
 
    # Get local context info
    localContext = uno.getComponentContext()
    resolver = localContext.ServiceManager.createInstanceWithContext(
        "com.sun.star.bridge.UnoUrlResolver", localContext )
 
    ctx = None
 
    # Wait until the OpenOffice starts and connection is established
    while ctx == None:
        try:
            ctx = resolver.resolve(
                "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext" )
        except:
            pass
 
    # Trigger our job
    wavelet = Wavelet( ctx )
    wavelet.trigger( () )


Testing

To test your UNO component, just run your Wavelet.py in the Python interpreter.

/opt/openoffice.org1.9.103/program/python ./Wavelet.py

Resume

This part does this:

starts Apache OpenOffice and opens the WaveletTest.odt file

loop until the connection will be established

start your component in the running Apache OpenOffice context

Documentation note.png Do not forget to remove this part before real UNO component packaging. Or comment it out.

See also

Personal tools