Gnome applets with Python

Revision History
Revision 0.507 February 2004
Revision 0.423 January 2004 (added "change-orient" event to callbacks section)Revised by: arturogf
Revision 0.320 January 2004 (merge 0.2 with the new version. Some updates on bonobo and .server files. Autotools installation).Revised by: arturogf
Revision 0.215 January 2004 (some grammar and structure corrections)Revised by: lgs
Revision 0.107 January 2004 (first release)Revised by: arturogf

Table of Contents
1. Introduction
1.1. The applet code skeleton
1.2. The bonobo .server file
2. Running the applet in its own window
3. Moving on: Object Oriented Applets
3.1. The class definition
4. The import section
5. The class constructor: __init__
5.1. Some instance variable definitions
5.2. The GTK widgets definition
5.3. Optional timeout callback method
5.4. Connecting the "destroy" signal and show the applet
5.5. Connecting signals and events to handlers
5.5.1. Loading the interface with glade.XML()
5.5.2. Using the gtkWidget.connect() method
6. The callback methods
6.1. The timeout callback
6.2. The panel applet "change-orient" signal
7. Autotools : a brief introduction to the installation process
7.1. The project structure: directories and files needed
7.2. Creating these files
7.3. Editing the files Makefile.am and configure.in
8. Example applet
Bibliography

This article will cover how to make an applet for the GNOME 2.x desktop with the Python programming language. Usually, GNOME applets are made in C, which has the big advantage of generating compiled executables (this means less memory footprint and faster programs) but in the other hand it's more difficult to do it and it takes more time to write the code. Python programs are developed faster, they have fewer lines of code, and it's an object oriented programming language. But it's an interpreted language, so the execution speed can be worse. It's up to the reader to decide if it is worthy writing a GNOME applet in Python. One good aproach is to prototype the applet in Python and then write the final version in C. Here we'll explain the necessary steps to write and deploy a simple applet from scratch using Glade 2 and libglade for the dialogs. Look at the bibliography for an in-deep explanation. Another good source of information is the pyGTK mailing list archives.

However, with regard to the code shown at the tutorial, we advise you to better look at the example section, where you will find some releases that do like it has been explained here, so you can see it growing from a prototype to a really functional applet.

1. Introduction

GNOME 2.x applets need at least two files to get them working:

1.1. The applet code skeleton

The simplest applet we can write can be found in the gnome-python package documentation (/usr/share/doc/python2.x-gnome2/examples/applet/applet.py in DEBIAN-based distributions) or simply in the gnome-python tarball from the original GNOME ftp.

Warning
place this at /usr/bin/pysample.py


        
        #!/usr/bin/env python
        import pygtk
        pygtk.require('2.0')

        import gtk
        import gnome.applet

        def sample_factory(applet, iid):
            label = gtk.Label("Success!")
            applet.add(label)

            applet.show_all()
            return gtk.TRUE

        gnome.applet.bonobo_factory("OAFIID:GNOME_PysampleApplet_Factory", 
                                     gnome.applet.Applet.__gtype__, 
                                     "hello", "0", sample_factory)

First, we need to import some Python modules that we will need. The pyGTK module, needed to specify the GTK version used (we'll be using 2.x in this article), the gnome module, that contains all the useful classes and methods about the gnome desktop, i.e. the applet class, and the gtk module, Python bindings for the GTK toolkit.

Then, we define the sample factory function to generate applet objects, with a text label "Sucess!". This function receives the object to be initializated (the applet) and the bonobo activation ID that the new factory will implement. It's all that simple. It returns gtk.TRUE if no errors were reported (see the panelapplet reference manual for details). When we call to gnome.applet.bonobo_factory().

When we call the bonobo_factory method, we need to pass it the following arguments:

  1. iid: The bonobo-activation iid of the factory.

  2. type : the type of the created object.

  3. description.

  4. version.

  5. factory callback: the name of the factory method.

1.2. The bonobo .server file

The second thing we need to get the applet running is to construct the bonobo server file. bonobo-activation-server, the GNOME application that tracks information about installed components and brokers components, reads and mantains the component descriptions from /usr/lib/bonobo/servers/*.server. These files provide an XML description of a component's capabilities which can be queried and manipulated by clients using the activation client library. Bonobo-activation-server, also ensures that the minimum neccessary number of servers for your display setup are running.

Bonobo activation server is nothing but a daemon implementing a set of CORBA interfaces. These CORBA interfaces implement a name service for the set of CORBA servers installed on your system. GNOME Object Activation Framework daemon (OAFD) knows about all the CORBA servers in your system, running or not. The OAF daemon will activate those servers if you ask for them. Each server is described by its .server file which contains among other things the IDL interfaces it implements, some specific properties and an IID (Implementation ID). Each IID has to be globally unique, and its format is pretty simple:

               OAFIID:program_name:UUID

The bonobo factories are CORBA objects that allow the creation of other new CORBA objects. This is a common practice in GNOME. The use of factories will allow us to use only an executable to get several instances of the component.

So we need to define the .server file and place it at the location mentioned before:


        
        <oaf_info>
        <oaf_server iid="OAFIID:GNOME_PysampleApplet_Factory"
                    type="exe" location="/usr/bin/pysample.py">

                <oaf_attribute name="repo_ids" type="stringv">
                        <item value="IDL:Bonobo/GenericFactory:1.0"/>
                        <item value="IDL:Bonobo/Unknown:1.0"/>
                </oaf_attribute>
                <oaf_attribute name="name" type="string" value="Python applet example"/>
                <oaf_attribute name="description" type="string" value="Python applet example"/>
        </oaf_server>

        <oaf_server iid="OAFIID:GNOME_PysampleApplet"
                    type="factory" location="OAFIID:GNOME_PysampleApplet_Factory">

                <oaf_attribute name="repo_ids" type="stringv">
                        <item value="IDL:GNOME/Vertigo/PanelAppletShell:1.0"/>
                        <item value="IDL:Bonobo/Control:1.0"/>
                        <item value="IDL:Bonobo/Unknown:1.0"/>
                </oaf_attribute>
                <oaf_attribute name="name" type="string" value="Python applet example"/>
                <oaf_attribute name="description" type="string" value="Python applet example"/>
                <oaf_attribute name="panel:category" type="string" value="Utility"/>
                <oaf_attribute name="panel:icon" type="string" value="bug-buddy.png"/>
        </oaf_server>
        </oaf_info>
        

The .server file registers two unique OAF identifiers, and give the activation-server the description of how the objects must be created. The first one is the Factory, that is created when the .py script is executed. The second is the applet itself, and is created asking the Factory how to do it. When we do the "add to panel" action in GNOME, it takes the OAF identifiers that are supposed to implement the "IDL:GNOME/Vertigo/PanelAppletShell:1.0" in its "repo_ids" attribute. The submenu is determined by the "panel:category" attribute, the icon from "panel:icon", the text displayed at the menu comes from "name" and the tooltip text from "description".

The sample_factory() function is called when a new applet is created. It receives a container that should be populated with the applet contents.