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.
Today’s modular x86 servers are compute-centric, designed as a least common denominator to support a wide range of IT workloads. Those generic, virtualized IT workloads have much different resource optimization requirements than hyperscale and cloud applications. They have resulted in a “one size fits all” enterprise IT architecture that is not optimized for a specific set of IT workloads, and especially not emerging hyperscale workloads, such as web applications, big data, and object storage. In this report, you will learn how shifting the focus from traditional compute-centric IT architectures to an innovative disaggregated fabric-based architecture can optimize and scale your data center.
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
| 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 |
| Trying to Tame the Tablet | May 08, 2013 |
| Dart: a New Web Programming Experience | May 07, 2013 |
- New Products
- Making Linux and Android Get Along (It's Not as Hard as It Sounds)
- A Topic for Discussion - Open Source Feature-Richness?
- Drupal Is a Framework: Why Everyone Needs to Understand This
- Home, My Backup Data Center
- What's the tweeting protocol?
- New Products
- One Hand Slapping
- Readers' Choice Awards
- Trying to Tame the Tablet
- Reply to comment | Linux Journal
6 hours 33 min ago - Reply to comment | Linux Journal
9 hours 5 min ago - Reply to comment | Linux Journal
10 hours 22 min ago - great post
10 hours 57 min ago - Google Docs
11 hours 20 min ago - Reply to comment | Linux Journal
16 hours 8 min ago - Reply to comment | Linux Journal
16 hours 55 min ago - Web Hosting IQ
18 hours 29 min ago - Thanks for taking the time to
20 hours 6 min ago - Linux is good
22 hours 3 min ago
Enter to Win an Adafruit Prototyping Pi Plate 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 Prototyping Pi Plate 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
- Next winner announced on 5-21-13!
Free Webinar: Linux Backup and Recovery
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.



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.