Beginners guide to creating a GNOME applet with Python (Part I)
Wednesday, 25. February 2009, 20:22:54
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.
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:
-
IID: The bonobo-activation id of the factory.
-
Type: the type of the created object.
-
Description
-
Version
-
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




Anonymous # 31. March 2009, 13:34
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
This is good stuff, thank you very much! Looking forward to part 2 also...!
BasicWolf # 2. April 2009, 18:36
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
Great !! Looking forward to part II and where to proceed next ?
Anonymous # 26. May 2009, 19:41
I found this tutorial quite useful. Thanks!
Anonymous # 27. May 2009, 16:08
That was great!
Anonymous # 26. July 2009, 09:11
Very well written, easy to understand and very useful. Thanks !!
Anonymous # 19. October 2009, 21:50
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!