Python (pyuno) "Hello World" Addon for OpenOffice
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:
If you select the option the add-on should modify the spreadsheet and you should now have:
If you select the option again you should get:
| Attachment | Size |
|---|---|
| addon-menu.jpg | 55.07 KB |
| addon-exec1.jpg | 8.36 KB |
| addon-exec2.jpg | 8.45 KB |
| addon-code.zip | 3 KB |
Mitch Frazier is an Associate Editor for Linux Journal.
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Sponsored by AMD
Built-in forensics, incident response, and security with Red Hat Enterprise Linux 6
Every security policy provides guidance and requirements for ensuring adequate protection of information and data, as well as high-level technical and administrative security requirements for a system in a given environment. Traditionally, providing security for a system focuses on the confidentiality of the information on it. However, protecting the data integrity and system and data availability is just as important. For example, when processing United States intelligence information, there are three attributes that require protection: confidentiality, integrity, and availability.
Learn more about catching the bad guy in this free white paper.
Sponsored by DLT Solutions
| Dynamic DNS—an Object Lesson in Problem Solving | May 21, 2013 |
| Using Salt Stack and Vagrant for Drupal Development | May 20, 2013 |
| Making Linux and Android Get Along (It's Not as Hard as It Sounds) | May 16, 2013 |
| Drupal Is a Framework: Why Everyone Needs to Understand This | May 15, 2013 |
| Home, My Backup Data Center | May 13, 2013 |
| Non-Linux FOSS: Seashore | May 10, 2013 |
- RSS Feeds
- Making Linux and Android Get Along (It's Not as Hard as It Sounds)
- Using Salt Stack and Vagrant for Drupal Development
- Dynamic DNS—an Object Lesson in Problem Solving
- New Products
- Validate an E-Mail Address with PHP, the Right Way
- Drupal Is a Framework: Why Everyone Needs to Understand This
- A Topic for Discussion - Open Source Feature-Richness?
- Download the Free Red Hat White Paper "Using an Open Source Framework to Catch the Bad Guy"
- Tech Tip: Really Simple HTTP Server with Python
- Roll your own dynamic dns
2 hours 28 min ago - Please correct the URL for Salt Stack's web site
5 hours 40 min ago - Android is Linux -- why no better inter-operation
7 hours 55 min ago - Connecting Android device to desktop Linux via USB
8 hours 24 min ago - Find new cell phone and tablet pc
9 hours 22 min ago - Epistle
10 hours 51 min ago - Automatically updating Guest Additions
11 hours 59 min ago - I like your topic on android
12 hours 46 min ago - This is the easiest tutorial
19 hours 21 min ago - Ahh, the Koolaid.
1 day 1 hour ago
Enter to Win an Adafruit Pi Cobbler Breakout Kit for Raspberry Pi

It's Raspberry Pi month at Linux Journal. Each week in May, Adafruit will be giving away a Pi-related prize to a lucky, randomly drawn LJ reader. Winners will be announced weekly.
Fill out the fields below to enter to win this week's prize-- a Pi Cobbler Breakout Kit for Raspberry Pi.
Congratulations to our winners so far:
- 5-8-13, Pi Starter Pack: Jack Davis
- 5-15-13, Pi Model B 512MB RAM: Patrick Dunn
- 5-21-13, Prototyping Pi Plate Kit: Philip Kirby
- Next winner announced on 5-27-13!
Free Webinar: Hadoop
How to Build an Optimal Hadoop Cluster to Store and Maintain Unlimited Amounts of Data Using Microservers
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Some of key questions to be discussed are:
- What is the “typical” Hadoop cluster and what should be installed on the different machine types?
- Why should you consider the typical workload patterns when making your hardware decisions?
- Are all microservers created equal for Hadoop deployments?
- How do I plan for expansion if I require more compute, memory, storage or networking?



Comments
This is not really a macro
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
$unopkg_bin remove $zip_filedoesn't work, because you need to give the id of the add-on, not the file
example
unopkg add ImpressRunner-1.0.oxtunopkg remove org.openoffice.legacy.ImpressRunner-1.0.oxt
Fun...
I never thought that using Excel's COM interface could be easier than using OO/Python!
Need more articles on using uno from python
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?
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:
pretty simple, right? So, what did it take to get there:
Not so easy after all.
Mitch Frazier is an Associate Editor for Linux Journal.
Uno is gibberish as well
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...
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
@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
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
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
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.