Scripting the Linux desktop, Part 1: Basics

Put Python and screenlets to work building useful desktop applications

This series of articles explores how to use Python
to create scripts for the GNOME desktop, the screenlets framework,
and Nautilus to deliver a highly productive environment.
Scripts on the desktop enable drag-and-drop functionality and
quick access to the information and services you commonly use.
In this installment, learn how to build a desktop application
using the screenlets widget toolkit.

Paul Ferrill has been writing in the computer trade press for more than
20 years. He got his start writing networking reviews for PC
Magazine on products like LANtastic and early versions of Novell
Netware. Paul holds both BSEE and MSEE degrees and has written software
for more computer platforms and architectures than he can remember.

Developing applications for the Linux desktop typically requires some type of
graphical user interface (GUI) framework to build on. Options include GTK+ for the
GNOME desktop and Qt for the K Desktop Environment (KDE). Both platforms offer
everything a developer needs to build a GUI application, including libraries and layout
tools to create the windows users see. This article shows you how to build desktop
productivity applications based on the screenlets widget toolkit (see
Resources for a link).

A number of existing applications would fit in the desktop productivity category,
including GNOME Do and Tomboy. These applications typically allow users to interact
with them directly from the desktop through either a special key combination or by
dragging and dropping from another application such as Mozilla Firefox. Tomboy
functions as a desktop note-taking tool that supports dropping text from other windows.

Getting started with screenlets

You need to install a few things to get started developing screenlets.
First, install the screenlets package using either the Ubuntu Software Center or
the command line. In the Ubuntu Software Center, type
screenlets in the Search box.
You
should see two options for the main package and a separate installation for the
documentation.

Python and Ubuntu

You program screenlets using Python. The basic installation of Ubuntu 10.04 has
Python version 2.6 installed, as many utilities depend on it. You may need
additional libraries depending on your application's requirements. For the
purpose of this article, I installed and tested everything on Ubuntu
version 10.04.

Next, download the test screenlet's source from the screenlets.org site. The test
screenlet resides in the src/share/screenlets/Test folder and uses Cairo and GTK,
which you also need to install. The entire source code for the test program is in
the TestScreenlet.py file. Open this file in your favorite editor to see the basic
structure of a screenlet.

Python is highly object oriented and as such uses
the class
keyword to define an object. In this example, the class is named
TestScreenlet and has a
number of methods defined.
In TestScreenlet.py, note the following code at line 42:

def __init__(self, **keyword_args):

Python uses the leading and trailing double underscore
(__)
notation to identify system functions with predefined behaviors. In this case,
the __init__ function is for all
intents and purposes
the constructor for the class and contains any number of initialization steps
to be executed on the creation of a new instance of the object. By convention,
the first argument of every class method is a reference to the current instance
of the class and is named self. This behavior makes
it easy to use self to reference methods and properties
of the instance it is in:

self.theme_name = "default"

The screenlets framework defines several naming conventions and standards, as
outlined on screenlets.org's developer's page (see Resources
for a link). There's a link to the source code for the screenlets package along
with the application programming interface (API) documentation. Looking at the
code also gives you insight into what each function does with the calling arguments
and what it returns.

Writing a simple screenlet

The basic components of a screenlet include an icon file, the source code file, and a
themes folder. The themes folder contains additional folders for different themes.
You'll find a sample template at screenlets.org with the required files and folders
to help you get started.

For this first example, use the template provided to create a basic "Hello World"
application. The code for this basic application is shown in Listing 1.

Each application must import the screenlets framework and create a new session.
There are a few other minimal requirements, including any initialization steps
along with a basic draw function to present the widget on screen. The TestScreenlet.py
example has an __init__ method that initializes the
object. In this case, you see a single line with a call to the screenlet's
__init__ method, which sets the initial width and height of
the window to be created for this application.

The only other function you need for this application is the on_draw
method. This routine sets the background color of the box to white and draws
a rectangle with the dimensions defined earlier. It sets the text color to black
and the source text to "Hello World!" and then draws the text. Figure
1 shows what you should see when you run this screenlet. This basic structure
sticks with you for the rest of this article as you build on these simple blocks to
create more useful applications.

Figure 1. Basic screenlet structure

Reusing code in a more complex screenlet

One nice thing about writing screenlets is the ability to reuse code from other
applications. Code reuse opens a world of possibilities with the wide range of
open source projects based on the Python language. Every screenlet has the same
basic structure but with more methods defined to handle different behaviors.
Listing 2 shows a sample application named TimeTrackerScreenlet.

This example introduces a few more concepts that you need to understand before you
start building anything useful. All screenlet applications have the ability to
respond to specific user actions or events such as mouse clicks or drag-and-drop
operations. In this example, the mouse down event is used as a trigger to change
the state of your icon. When the screenlet runs, the start.png image is displayed.
Clicking the image changes it to stop.png and records the time started in
self.started. Clicking the stop image changes the image
back to start.png and displays the amount of time elapsed since the first start
image was clicked.

Responding to events is another key capability that makes it possible to build any
number of different applications. Although this example only uses the
mouse_down event, you can use the same approach
for other events generated either by the screenlets framework or by a system
event such as a timer. The second concept introduced here is persistent state.
Because your application is running continuously, waiting for an event to trigger
some action, it is able to keep track of items in memory, such as the time the
start image was clicked. You could also save information to disk for later retrieval,
if necessary.

Automating tasks with screenlets

Now that you have the general idea behind developing screenlets, let's put all
together. Most users these days use a Really Simple Syndication (RSS) reader
to read blogs and news feeds. For this last example, you're going to build a
configurable screenlet that monitors specific feeds for keywords and displays
any hits in a text box. The results will be clickable links to open the post in
your default Web browser. Listing 3 shows the source code
for the RSS Search screenlet.

Building on the concepts of the first two examples, this screenlet uses a number of
new concepts, including the config page. In the on_init
routine, three options are added for the user to specify: a list of RSS feeds to
track, a topic of interest to search for, and an update interval. The update
routine then uses all of these when it runs.

Python is a great language for this type of task. The standard library includes
everything you need to load the Extensible Markup Language (XML) from an
RSS feed into a searchable list. In Python, this takes just three lines of code:

The libraries used in these three lines include urllib2
and xml. In the first line, the entire contents found
at the feed_url address are read into the string raw.
Next, because you know that this string contains XML, you use the Python XML
library dom.minidom.parseString method to create a
document object made up of node objects.

Finally, you create a list of element objects corresponding to the individual XML
elements named item. You can then iterate over this
list to search for your target topic. Python has a very elegant way of iterating over
a list of items using the for keyword, as in this code
snippet:

for item in items:
# Find the title and make sure it matches the topic.
title = item.getElementsByTagName('title')[0].firstChild.data
if self.topic.lower() not in title.lower(): continue

Each item matching your criteria is added to the currently displayed list, which
is associated with this instance of the screenlet. Using this approach makes it
possible to have multiple instances of the same screenlet running, each configured
to search for different topics. The final part of the update function redraws the
text with the updated list and fires off a new update timer based on the interval
on the config page. By default, the timer fires every 10 seconds, although you
could change that to anything you want. The timer mechanism comes from the
gobject library, which is a part of the GTK framework.

This application expands the on_draw method quite heavily
to accommodate your new functionality. Both the Cairo and Pango libraries make it
possible to create some of the effects used in the text window. Using a gradient
gives the background of the widget a nice look along with rounded angles and
semi-transparency. Using Pango for layout adds a number of functions for saving
and restoring the current context easily. It also provides a way to generate scalable
fonts based on the current size of the screenlet.

The trickiest part in the on_draw method is handling when
a user hovers over an item in the list. Using the for"
keyword, you iterate over the items in the screenlet to see whether the user
is hovering over that particular item. If so, you set the selected property and
change the color to provide visual feedback. You also use a bit of markup to set
the link property to bold—probably not the most elegant or efficient way
to deal with the problem, but it works. When a user clicks one of the links in the
box, a Web browser is launched with the target URL. You can see this functionality
in the on_mouse_down function. Python and its
libraries make it possible to launch the default web browser to display the desired
page with a single line of code. Figure 2 shows an example of
this screenlet.

Figure 2. The example screenlet

Summary

Building useful desktop applications is not a difficult task with Python and screenlets.
The biggest hurdle is getting comfortable with the screenlets API and the mechanics
of passing control between different functions. Although the documentation may
not be an easy read, it does contain the information you need to use the different
functions. An even better way of getting something working quickly is to modify an
existing screenlet that is close to what you want.

Get products and technologies

Evaluate IBM products
in the way that suits you best: Download a product trial, try a product online, use a product in a cloud environment, or spend a few hours in the
SOA Sandbox
learning how to implement Service Oriented Architecture efficiently.

Discuss

Get involved in the My developerWorks community.
Connect with other developerWorks users while exploring the developer-driven blogs, forums, groups, and wikis.

The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.