Python (pyuno) "Hello World" Addon for OpenOffice

 in

In my last few posts about pyuno (SSConverter, OORunner) we used pyuno to convert spreadsheets to CSV files by running OpenOffice from Python using pyuno as the bridge between the two processes. In this post we're going to get inside OpenOffice and use pyuno as the bridge between OpenOffice and an embedded Python interpreter (embedded inside OpenOffice).

As is the law, our first program when doing something new has to be a "Hello World" program. The idea here is to add an option to the OpenOffice menu which will insert "Hello World" into the first cell of a spreadsheet. As a starting point for this example I used an example from the pyuno udk site (the second "Hello World" example on the page).

The example there does essentially the same thing as the example here except there it inserts "Hello World" into a Writer (Word Processor) document rather than a spreadsheet. One would assume that it would be a fairly easy conversion, and once I knew the answer it was. The hard part was figuring out how to get the "spreadsheet" object and then how to get a "cell" object from it. And it wasn't so much hard as just time consuming trying to find the right information in the documentation.

After a few hours of searching and a few false starts I came across the two needed interfaces: XSpreadsheetDocument and XCell. These gave me what I needed to modify the example to work with a spreadsheet:

 1 import uno
 2 import unohelper
 3 
 4 from com.sun.star.task import XJobExecutor
 5 
 6 # Implement an UNO component by deriving from the standard
 7 # unohelper.Base class and from the interface(s) you want to implement.
 8 class HelloWorldJob(unohelper.Base, XJobExecutor):
 9     def __init__(self, ctx):
10         # store the component context for later use
11         self.ctx = ctx
12 
13     def trigger(self, args):
14         # Retrieve the desktop object
15         desktop = self.ctx.ServiceManager.createInstanceWithContext("com.sun.star.frame.Desktop", self.ctx)
16 
17         # Get the spreadsheet.
18         spreadsheet = desktop.getCurrentComponent()
19 
20         # Get the collection of sheets in the spreadsheet.
21         sheets = spreadsheet.getSheets()
22 
23         # Get the first cell in the first sheet.
24         cell = sheets.getByIndex(0).getCellByPosition(0, 0)
25 
26         # Modify its contents.
27         if cell.getFormula():
28             cell.setFormula("Hello " + cell.getFormula())
29         else:
30             cell.setFormula("Hello world")
31 
32 
33 # pythonloader looks for a static g_ImplementationHelper variable
34 g_ImplementationHelper = unohelper.ImplementationHelper()
35 
36 g_ImplementationHelper.addImplementation( \
37         HelloWorldJob,                                # UNO object class
38         "org.openoffice.comp.pyuno.demo.HelloWorld",  # Implementation name
39         ("com.sun.star.task.Job",),)                  # List of implemented services

The meat of the example is the trigger method of the HelloWorldJob class. This method is called when the add-on is invoked. Trigger performs the following steps:

  • It gets the top-level desktop object.
  • From the desktop object it gets the current component. The current component in this case is a SpreadsheetDocument.
  • From the spreadsheet object it gets the collection of all the sheets in the spreadsheet.
  • From the sheets collection it gets the first sheet and then the first Cell from that sheet.
  • Using the cell, if the cell is empty it inserts "Hello World". If the sheet contains something then it prefixes the old contents with "Hello ".

The rest of the code is essentially a copy of the code from the original example.

Now that we have the code we need to integrate it into OpenOffice. We do that by creating a zip file of the code and a XML file that describes the add-on. The XML file is, again, essentially just a copy from the original example:

<?xml version="1.0" encoding="UTF-8"?>
<oor:node 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">
    <node oor:name="AddonMenu">
      <node oor:name="org.openoffice.comp.pyuno.demo.HelloWorld" oor:op="replace">
        <prop oor:name="URL" oor:type="xs:string">
          <value>service:org.openoffice.comp.pyuno.demo.HelloWorld?insert</value>
        </prop>
        <prop oor:name="ImageIdentifier" oor:type="xs:string">
          <value>private:image/3216</value>
        </prop>
        <prop oor:name="Title" oor:type="xs:string">
          <value xml:lang="en-US">Insert Hello World</value>
        </prop>
      </node>
    </node>
  </node>
</oor:node>

In addition to creating the zip file containing these two files you need to run unopkg to register the add-on with OpenOffice. The following bash script does the zipping and the packaging and then runs OpenOffice so that you can test it:

#!/bin/bash

unopkg_bin=/usr/bin/unopkg
oocalc_bin=/usr/bin/oocalc

addons=Addons.xcu
python_file=hello_world_oocalc.py
zip_file=hello_world.zip

rm $zip_file
zip $zip_file $addons $python_file
$unopkg_bin remove $zip_file
$unopkg_bin add $zip_file

#export PYUNO_LOGLEVEL=CALL
export PYUNO_LOGLEVEL=ARGS
export PYUNO_LOGTARGET=stdout

$oocalc_bin

When you run the script it will start OpenOffice and you should see the following option in the "Tools" menu:

addon-menu.jpg

If you select the option the add-on should modify the spreadsheet and you should now have:

addon-exec1.jpg

If you select the option again you should get:

addon-exec2.jpg
AttachmentSize
addon-menu.jpg55.07 KB
addon-exec1.jpg8.36 KB
addon-exec2.jpg8.45 KB
addon-code.zip3 KB
______________________

Mitch Frazier is an Associate Editor for Linux Journal.

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

This is not really a macro

Alexandro Colorado's picture

the tutorial doesn't really explains Macro's so comparing it to a macro is not a fair statement, this is an extension and most of the code is to declare and package the extensions. So is not a good comparison with the Macro recorder. This explains how to declare the code as an extensions that can be recognized by the extension handlers. Of course there is not a wizard to package the code so all the declarations needs to be done by typing it and that's what adds the complexity. However most of the stuff is really just a defacto code so it would be trivial to generate this code even from a bash script.

$unopkg_bin remove

piR's picture

$unopkg_bin remove $zip_file
doesn't work, because you need to give the id of the add-on, not the file

example
unopkg add ImpressRunner-1.0.oxt
unopkg remove org.openoffice.legacy.ImpressRunner-1.0.oxt

Fun...

Justin Mitchell's picture

I never thought that using Excel's COM interface could be easier than using OO/Python!

Need more articles on using uno from python

John Fabiani's picture

Yes using UNO is like pulling teeth - it hurts. I don't see SUN doing anything about it. So we need more articles using python to simplify the process. Soon we will have better classes that will be easier to use. Keep up the good work.

Is it too complex or just unfamiliar?

Mitch Frazier's picture

Assuming you've never done it before: quick write a pyGTK or pyQt "Hello World" application. Takes a fair bit of study and work to get those going the first time also. And that's true of almost any "useful" development tool these days.

And remember what UNO is: a language independent interface to OpenOffice. Its language independent nature pretty much assures that it won't be super Pythonic. And the size and complexity of OpenOffice pretty much assures it won't be small or simple.

That's not to say that improvements can't be made, that's why there are libraries and other forms of abstraction: to hide the details. Take Python for instance, this is "Hello World" in Python:

  print "Hello world"

pretty simple, right? So, what did it take to get there:

  • Write a Python interpreter
  • Write a C compiler to compile the Python interpreter
  • Write an assembler to assemble the C compiler
  • Write an operating system to run the assembler
  • Create semi-conductors to create the computer
  • Create binary numbers

Not so easy after all.

Mitch Frazier is an Associate Editor for Linux Journal.

Uno is gibberish as well

geoff_f's picture

Sun isn't going to set the programming world on fire if it persists with using gibberish as its language. Uno suffers from the same problems as Java: too much extraneous input that the application should know about. Also, why should I have to declare the variable type with every use? I have a computer; can't it keep track of what types of variables are used?

In this case, the spreadsheet application should know it's a spreadsheet application. It should know what desktop it's running on. Why should the programmer have to specify something that's already known? So, the code returning the 'desktop' and 'spreadsheet' objects is superfluous.

What's wrong with:

sheet=Sheets("Name-of-Sheet")
(The spreadsheet should know the name of the sheets it contains)

sheet.cell(Col, Row).Value = "Hello World"

It should be that simple. Much more work needs to be done by the computer to ease the load of the programmer. Otherwise, 'OOo Basic' is a misnomer. This is why users who rely on macros shun OOo.

oh my goodness that's hard...

Anonymous's picture

It's really great to see an article that pulls it all together.
I had looked at it, and gotten hopelessly confused, so this article is really helpful... On the other hand, this example is so ridiculously complicated, that it is an existence proof of why openoffice has difficulty attracting people to work on it. It really needs to be easier than this.

Re: Comparison

Anonymous's picture

@Xan: you can do the same in OOs with OOBasic. The target is is different, it's not casual users but extension developers. No point comparing oranges and apples.

Comparison

Xan's picture

Compare this to MS Excel. Click record macro, type "hello world" in cell A1. Click stop recording macro. Done.
All that xml gibberish? WTF? How can you possibly port all the Excel VBA cruft that every office has and is reliant on if this is the best that can be done for "Hello World" Seriously. We don't need automagic conversion of macros just something at least half way sane, this nonsense, 5 years after I last looked at it, still just isn't even close. Sun could even build a sane scripting interface to OOo and, gasp, sell it. The market exists...

OpenOffice has macros too

Anonymous's picture

Open Office also has a "one click macro recorder". The point of the article is to introduce what is involved in using python with OOo, not to type "Hello World" in A1.

The problem of OOo

Anonymous's picture

The problem of OOo macro-recorder, that its output is not readily editable.

Average M$O user normally records macro and then adjust it to their needs. Most common use of macros (regardless of what many developers believe) is to access some exotic function/formatting/style. Higher level automation comes much later.

Also note that OOo macro-recorder doesn't record all things. Can't recall precisely what was missing, on most occasions I have tried to do anything to OOo macro-recorder, it missed many things. Since then I have it labeled as "useless" and do not try it anymore.

Webinar
One Click, Universal Protection: Implementing Centralized Security Policies on Linux Systems

As Linux continues to play an ever increasing role in corporate data centers and institutions, ensuring the integrity and protection of these systems must be a priority. With 60% of the world's websites and an increasing share of organization's mission-critical workloads running on Linux, failing to stop malware and other advanced threats on Linux can increasingly impact an organization's reputation and bottom line.

Learn More

Sponsored by Bit9

Webinar
Linux Backup and Recovery Webinar

Most companies incorporate backup procedures for critical data, which can be restored quickly if a loss occurs. However, fewer companies are prepared for catastrophic system failures, in which they lose all data, the entire operating system, applications, settings, patches and more, reducing their system(s) to “bare metal.” After all, before data can be restored to a system, there must be a system to restore it to.

In this one hour webinar, learn how to enhance your existing backup strategies for better disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible bare-metal recovery solution for UNIX and Linux systems.

Learn More

Sponsored by Storix