Skip navigation.

Programmer's notes

Beginners guide to creating a GNOME applet with Python (Part I)

, , ,

Programming is an art.
To be more specific, it's everlasting art. There is no program that could completely satisfy the users. And I doubt that there is a program that could completely satisfy a programmer.

I spend hours in front of my computer. Of course that is very unhealthy for my eyes. I wanted to create a simple reminder applet - an eyes icon that changes to "bloody" eyes - indicating that it's time to relax.

This was my first GNOME applet, my first GTK and more or less serious Python experience and I'd like too share it with everyone, who deals with his or her first GNOME applet.

I hope, this tutorial will help you.

P.S.
Dear Friends, the up-to-date version of this article and it's second part are located here.

Part I: GNOME applet basics

Before we begin...
Some technical information about my working environment. I use Ubuntu 8.10 and Python 2.5.2. So, when I'm saying that "this library loads files from here", I'm speaking of what I've found in my working environment. It could be different in yours.

All numbers in square brackets (like [1]) are references to web-pages that may be very helpful. or were used as source for this tutorial. The references section is located at the end of the tutorial.

1. Project's directory
First, lets create a directory for the applet. /home/user/applet (~/applet) will do, of course you can create it as a symbolic link to your designated path. Here, /home/user is a path to your home directory.
The source code of the applet will be located in /home/user/applet/src (~/applet/src) directory.

2. How GNOME interacts with applets
In GNOME, an applet is a small application, designed to sit in the GNOME panel, providing quick and easy access to a control, such as a volume control, a network status display, or even a weather gauge. [1]

Technically, applets are Bonobo controls embedded in the Gnome panel. This means that there are a few slight differences from stand-alone GNOME programs. The first difference is that each applet requires a 'server' file, which contains a description of the Bonobo capabilities. [1]
Gnome searches for applet server files in /usr/lib/bonobo/servers directory.

The basic part of a simple server file contains server's id and location of a executable file (our file is a python script):

<oaf_server iid="OAFIID:SampleApplet_Factory" 
    type="exe" 
    location="home/user/applet/src/applet.py">

We call our 'factory' SampleApplet_Factory and the home/user/applet/src/applet.py is the absolute path to the executable Python script.

So, your fist step is to create a server file in /usr/lib/bonobo/servers. Call it SampleApplet.server.

Note: You'll need root privileges to work with the SampleApplet.server file. And remember to correct the /home/user/ path

After that, you can restart GNOME session (restarting X-server via Ctrl+Alt+Backspace will do) and try to add the new applet to a panel. You should get a dialog like this:


The applet's image is absent, because it doesn't exist in /usr/share/pixmaps. The applet engine searches for an image according to our .server file:

<oaf_attribute name="panel:icon" type="string" value="no-picture-yet.png"/>

Change the value="no-picture-yet.png" string to value="gnome-laptop.png" and restart GNOME session. Now, the "Add to panel" dialog should look like this:


If by accident the gnome-laptop.png is absent, you can copy it to /usr/share/pixmaps right now:


It's time to write a few lines of code.

3.1 Launching the applet
We start with creating a applet.py script file in ~/applet/src

Next, we need to define the interpreter that'll handle our script (this is where programming starts):

#!/usr/bin/env python

The /usr/bin is a standard directory on Unix-like operating systems that contains most of the executable files (i.e., ready-to-run programs) that are not needed for booting (i.e., starting) or repairing the system. [2]


We're going to import some modules: The pyGTK module is needed to specify the GTK version used (we're 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. [3]

import sys

import gtk
import pygtk
import gnomeapplet

pygtk.require('2.0')

And continuation:

def applet_factory(gnome_applet, iid):   
   label = gtk.Label("It works!")
   gnome_applet.add(label)
   gnome_applet.show_all()
   print('Factory started')
   return True
            
if __name__ == '__main__':   # testing for execution
   print('Starting factory')
   gnomeapplet.bonobo_factory('OAFIID:SampleApplet_Factory', 
                              gnomeapplet.Applet.__gtype__, 
                              'Sample Applet', '0.1', 
                              applet_factory)

The applet_factory function receives the object to be initialized (the applet) and the bonobo activation ID that the new factory will implement. It returns True if no errors were reported. Then the bonobo_factory function is called. [3]

Note: Remember to change the mode of the applet.py file to +x (execute/search), e.g. by running chmod 755 ~/applet/src/applet.py

Here is the description of bonobo_factory(IID,Type,Description,Version,Callback) arguments:
  1. IID: The bonobo-activation id of the factory.
  2. Type: the type of the created object.
  3. Description
  4. Version
  5. Factory callback: the name of the factory function

You should see the "It works!" label on a panel:

3.2 Debugging routine
I'm sure you've noticed the print( ) function calls. But where is the output? A common issue when developing an applet is the debug process which means, knowing what it's failing and why. As an applet is nothing but a GTK+ application we can use an additional command line argument like "run-in-window" to put it in window-mode by creating a GTK+ window and inserting the applet in it. [3]
You can make it by making some changes in the code:

if __name__ == '__main__':   # testing for execution
   print('Starting factory')

   if len(sys.argv) > 1 and sys.argv[1] == '-d': # debugging
      mainWindow = gtk.Window()
      mainWindow.set_title('Applet window')
      mainWindow.connect('destroy', gtk.main_quit)
      gnome_applet = gnomeapplet.Applet()
      applet_factory(gnome_applet, None)
      gnome_applet.reparent(mainWindow)
      mainWindow.show_all()
      gtk.main()
      sys.exit()
   else:
      gnomeapplet.bonobo_factory('OAFIID:SampleApplet_Factory', 
                                 gnomeapplet.Applet.__gtype__, 
                                 'Sample applet', '0.1', 
                                 applet_factory)

Let me explain: we create a GTK window, set it's title and connect the 'destroy' signal to the gkt.main_quit function. It mean's that the gtk.main_quit(...) will be automatically called, when the window is destroyed (closed). We create GNOME applet's instance and make it a child control (widget) of the window we've just created.
The gtk.main() function runs the main loop until the gtk.main_quit() function is called. [4] The GTK+ main loop's primary role is to listen for events on a file descriptor connected to the X server, and forward them to widgets. [5]

Now you should launch the applet from a console with the -d key, e.g. ./applet.py -d. The applet should be launched in a window. It's a usual GNOME window, you can resize it to see the the 'Applet window' title.


That's it! This is the end of the first part of the tutorial. I hope you've learned a couple of new trics. I'll try to catch up with 2nd part in March.

References
[1]http://projects.gnome.org/ORBit2/appletstutorial.html
[2]http://www.linfo.org/usr_bin.html
[3]http://www.pygtk.org/articles/applets_arturogf/
[4]http://www.pygtk.org/docs/pygtk/gtk-functions.html
[5]http://developer.gnome.org/doc/GGAD/sec-mainloop.html

The legacy of the initramfs optimizationOpera::User JS

Comments

Anonymous 31. March 2009, 13:34

Patrick writes:

Thank you for this great tutorial!
It's hard to find good resources on writing gnome-applets for beginners but this ist definitely a good one.
I'm looking forward to part 2.

Excellent work!

Anonymous 1. April 2009, 19:36

Anonymous writes:

This is good stuff, thank you very much! Looking forward to part 2 also...!

BasicWolf 2. April 2009, 18:36

Thank you guys for responding. Your comments keep the blog running :smile:
I'm very busy right now with my studies and I hope to complete the second part by the end of April.

Anonymous 28. April 2009, 20:14

tdi writes:

Great !! Looking forward to part II and where to proceed next ?

Anonymous 26. May 2009, 19:41

Gebstadter writes:

I found this tutorial quite useful. Thanks!

Anonymous 27. May 2009, 16:08

Cristian N writes:

That was great!

Anonymous 26. July 2009, 09:11

Peter C writes:

Very well written, easy to understand and very useful. Thanks !!

Anonymous 19. October 2009, 21:50

Anonymous writes:

Nice. However how could one make the child of the applet take all the available space? I mean a sort of autoresize according to the actual size of the panel. Thanks anyways!

How to use Quote function:

  1. Select some text
  2. Click on the Quote link

Write a comment

Comment
(BBcode and HTML is turned off for anonymous user comments.)

If you can't read the words, press the small reload icon.


Smilies

Download Opera, the fastest and most secure browser
November 2009
M T W T F S S
October 2009December 2009
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30