After my needs finally exceeded the limited capacity of GNOME's default bittorrent client, I tried a few alternatives but didn't have much luck. Azureus is almost definitely the single worst program I have ever used, and most of the interesting native-Linux alternatives are still too experimental for day-to-day use. A friend had repeatedly suggested that I consider uTorrent, so I eventually decided to try running it through Wine. It is now my bittorrent client of choice, and one that I highly recommend to others. Miraculously, uTorrent works almost perfectly on Linux. Even though I am running it through Wine, it is more stable, responsive, and robust than Azureus and signficantly less resource intensive.
The latest public beta release of uTorrent includes support for a remotely-accessible web interface. The web UI itself is relatively unremarkable and of little interest to me, but the underlying API is enormously useful. In order to facilitate the construction of a web interface, the uTorrent developers have exposed much of the application's underlying infrastructure through JSON. A little bit of simple reverse engineering has made it possible for me to construct my own interface, and adapt the program in unique and innovative ways.
My first experiment was a simple Python script that connects to uTorrent, logs in using proper authentication, extracts information about active torrents, and displays that information on stdout:
import httplib, base64
def parse_data(data):
data = dict((name, data[column]) for column, name in enumerate([
"hash", "state", "name", "total", "unknown", "received", "sent", "ratio",
"upload_speed", "speed", "eta", "label", "connected_peers", "peers",
"connected_seeds", "seeds", "available", "queue", "remaining"]))
data.update({
"hours_remaining": round(float(data["eta"] / 60 / 60), 2),
"percent": round(float(data["received"]) / data["total"] * 100,2),
"speed_kb": round(float(data["speed"]) / 1024, 2),
})
return data
h = {"Authorization": "Basic " + base64.encodestring("admin:").strip()}
con = httplib.HTTPConnection("localhost", 8080)
con.request("GET", "/gui/?list=1", headers = h)
response = con.getresponse()
if response.status == 200:
torrents = [parse_data(x) for x in eval(response.read())["torrents"]]
for t in torrents:
print "%-15.15s | %5.2f kb/s | %3.0f hours | %5.2f%%" % (
t["name"], t["speed_kb"], t["hours_remaining"], t["percent"])
print "\nTotal download speed: %s kb/s" % sum(t["speed_kb"] for t in torrents)
This is a relatively simple task, particularly since the syntax used to describe JSON data structures is virtually identical to Python's dict syntax. I cheat and use eval to eliminate the need for a third party JSON library. In general, using eval like that is something I would avoid and discourage since it could potentially create security vulnerabilities, but for a basic experiment it isn't really a big deal. In this example, the parse_data function is used to generate a python dict that associates key names with various torrent attribute values. Data from each torrent is parsed, and then displayed on the screen. At the very end, I use the sum function to compute the total download speed.

After completing my first experiment, I decided to take it to the next level. My Logitech G15 keyboard has a nifty programmable LCD that can be used to display various kinds of data. I haven't really done much with the LCD yet, so it seemed like the perfect opportunity. The G15 LCD is supported on the Linux platform by the open source G15 Tools collection, which includes a low-level hardware access library, a userspace daemon for managing the keyboard's macro buttons and LCD, a rendering library designed specifically for generating content suitable for the G15 screen, and a composer utility that enables users to paint the LCD with simple commands.
Unfortunately, there are no Python or Ruby bindings for the rendering library, so I had to either communicate directly with the daemon or use the composer as an intermediary. The Python bindings for the daemon only provide raw pixel access with no abstraction for text or anything like that. The composer provides such abstractions, but it has some weird limitations of its own. The composer binds to a named pipe on the file system, and then one communicates with it by writing commands into the named pipe. It's counterintuitive and messy, but it does work.
The composer accepts numerous commands, and allows the user to write text in fonts at several different sizes. It also supports basic drawing and graphics functionality. I used several of the composer's advanced features in my uTorrent display for the sake of experimentation. I added the following code to my initial script, and wrapped the entire thing in a while loop with a one second time.sleep invocation to make it repeat every second:
total_speed = sum(t["speed_kb"] for t in torrents)
text = "%(name)-20.20s %(speed_kb)3d %(hours_remaining)3d %(percent)3d"
headings = "%-20.20s %3s %3s %3s" % ("Name", "k/s", "eta", "%")
sys.stdout = f = open(sys.argv[1], "a")
print "PC 0\nTM \"%s\"" % headings
print "TO 0 10 1 0 \"%s\"" % '" "'.join(text % t for t in torrents)
print "TO 0 35 2 0 \"uTorrent %0.2f Kb/s\"" % total_speed
for x in [101, 121, 141]: print "DL %d 0 %d 32 1" % (x, x)
for x in [8, 32]: print "DL 0 %d 500 %d 1" % (x, x)
It's not exactly simple, but it works. My G15 LCD uTorrent display looks relatively nice. The grid layout in this particular examples uses up a lot of space, so only three active torrents will be visible. If you need to see more, you can reduce the font size of the total download speed value or remove the grid and labels. If you use the smallest font size and you don't include anything but the torrent data, you could probably fit a lot more on the screen. I usually only have two or three torrents actively downloading at any given time, so I prefer using the bigger fonts.
Since I plan on using this on a regular basis, I decided to clean up the code a bit. In the final version, I put the uTorrent stuff into its own file and threw together a few classes to make it easier to work with. You can see the complete version here. Those of you who want to use it, please remember that this implementation was designed specifically for the Linux platform and almost definitely wont work on Windows in its current state. I've already started to think about other ways to integrate uTorrent and the G15. For my next project, I want to see if I can find a way to control uTorrent's maximum download speed with the keyboard's volume dial.
Tags: programming, python, G15, bittorrent
Posted on 2007-01-18
I recently purchased a Logitech G15 gaming keyboard, which features a nifty integrated LCD and an assortment of macro keys. Unfortunately, Beryl only allows users to bind arbitrary commands to 11 keys. Since the G15 has 18 separate macro keys in addition to multimedia buttons, I had to find an alternate solution. I finally decided to implement my own binding manager so that I can associate keys with commands and Python expressions.

Obviously, the first step was to figure out how to set up global key bindings with Python. I started by poking around the GTK/GDK documentation in search of relevant functionality, but I didn't really find anything there. I asked in the #linux channel on the Ars Technica IRC server and got some useful suggestions, but nothing that seemed to do exactly what I wanted. It finally occurred to me to investigate the source code of a Python application that has global key bindings.
Deskbar, a really useful panel applet that I use on a daily basis, uses a global key binding for activation. The deskbar/Keybinder.py file in the Python site-packages directory reveals that Deskbar is actually using a file called _keybinder.so, which appears to have been created for the Tomboy note-taking program. The tomboy_keybinder_bind function in _keybinder.so makes it possible to associate a binding with a Python function. The binding is described as strings consisting of modifiers and single letters. The following is a simple example that shows how to associate a key binding with a function:
import gtk
from _keybinder import tomboy_keybinder_bind as bindkey
def onBindingPress(arg):
print "Binding initiated and parameter received: %s" % arg
bindkey("<Ctrl><Alt>b", onBindingPress, "Test")
bindkey("<Ctrl><Alt>q", gtk.main_quit)
gtk.main()
I find it a bit odd that this functionality had to be borrowed from Tomboy, and it makes me wonder how it is implemented in _keybinder.so. I'm not entirely sure why global key binding support isn't provided by the Python and Ruby GTK bindings, but I'm pleased that I found a way to do it with minimal hassle.
Tags: programming, python, gnome, G15
Posted on 2007-01-10
In the dynamic world of technology journalism, there are very few universal truisms. One of the most important things I have learned is that screenshots almost always improve the quality of an article. In written works about software, particularly in reviews or tutorials, screenshots fulfill a number of crucial functions and imbue content with elegance and authority that would otherwise be absent. Failure to include a screenshot in a news article about a new program, for instance, elicits criticism and rebuke from irritated readers. They don't just appreciate screenshots, they expect screenshots.
The screenshot rule isn't entirely applicable to documentation. Although screenshots are still important in documentation, it is a context in which they should be used sparingly. There are many reasons for this, but I think the most important one is that screenshots are more likely to be superfluous in task-oriented reference material that is meant to be used alongside the actual program. In many cases, screenshots in the GNOME documentation are used to help the user confirm that they are reading about the right program.
Although I routinely use screenshots to break up large blocks of text in articles in order to increase readability, screenshots in documentation actually have the opposite effect. As the GNOME documentation style guide points out, breaking up documentation text with screenshots inconveniences readers by making them do additional scrolling. There are also some pragmatic reasons. Each screenshot included in the documentation has to be replicated by translators in each of the 42 languages supported by GNOME (Update: the GDP is evaluating ways to automate this process). Screenshots also have to be updated constantly as continued development alters the appearance of applications.
One of the points mentioned in the style guide has been of particular concern to the GDP lately. Usability studies have shown that users confuse screenshots in documentation with real windows. Although this may seem far fetched, it is apparently a very real concern, and one that we are actively trying to address. Joachim suggests fading the edges of screenshots to make the distinction between windows and images more apparent to users. I think this is a great idea, but as Joachim points out in a comment on bug #348495, there is no way to ensure that the fading effect will be consistent in all screenshots. Additionally, opening up the GIMP and doing that sort of operation for every image could become very time consuming.
For the sake of simplicity and consistency, I think we need to automate the process. In order to facilitate discussion on the possibility of leveraging automated image effects, I have developed a simple proof-of-concept utility that automatically blurs the edges of PNG images and applies a drop shadow.
The New Utility
Written in Python, my new utility leverages Cairo, PIL, and GTK. I initially wanted to do all of the image processing with ImageMagick, but the ImageMagick bindings for Python don't appear to be maintained or widely distributed. When I discovered the binding deficiency, I thought about using Ruby instead of Python, but the utility really needs to be built with a language that is used and understood by other GNOME developers. I also want to avoid foisting Ruby/GNOME dependency headaches on other documentation writers. It would have been nice to be able to avoid using PIL, but Cairo doesn't seem to have a working guassian blur filter yet, and I need that in order to produce a drop shadow. There is actually a value in Cairo::Filter called FILTER_GAUSSIAN, but apparently it doesn't do anything yet.
Using PIL to render the drop shadow is problematic because there is no simple way to convert Cairo surfaces into PIL image instances with version 1.0.2 of Cairo. Right now, the only way to do it is to save the Cairo surface to disk and load it back into a PIL image instance. Since the Python Cairo bindings don't use Python's file interface conventions, I can't just save to a StringIO instance like I do when I want to get a PIL image into a GDK pixbuf. There are some nasty hacks that involve wrapping a Cairo surface around a binary array, but that particular trick causes some peculiar problems and distorts the images in some cases. Fortunately, a new surface method called to_rgba() is already in CVS, and possibly even in the next major version of Cairo. For now, I have the script automatically save the image and load it back in again, but hopefully I'll be able to eliminate that step in the future.
My working prototype isn't particularly elegant, but it is effective. I spent an extra hour adding a user interface that allows the user to customize the parameters of the operation. Users can drag sliders to change several values:
- border - specifies the size of the faded regions
- filled - specifies how far from the edges the fades should end
- offset - specifies the drop shadow offset
The UI is currently very kludgy, but it will make it easy to compare various configurations and determine exactly which values should be used by default for documentation screenshots. If there is sufficient interest, I plan to eventually produce a complete, streamlined screenshot utility specifically for use by documentation writers. It will include support for screen region selection and it will make it possible to capture multiple images and then choose which ones to save. (Update: It looks like other members of the GDP team are also interested in automation and have some very cool ideas of their own. I particularly like the idea of using DogTail for automatically grabbing the images.)
The current implementation of my utility can be run as a stand-alone script or loaded into other Python scripts as a module. To execute it in stand-alone mode, simply run it from the command line and provide the path to a png file as a parameter:
python edge_fader.py screenshot.png
The following example shows how to use the edge fader as a module:
import edge_fader
img = edge_fader.fade_edges("original.png",
border = 30, filled = 2, offset = 6, show_shadow = True)
edge_fader.save_to_disk(img, "output.png")
View the source code here, and download it here. In order to run it, you will need PIL, and Python bindings for Cairo and GTK. An example of the program's output can be found here, and this is a screenshot of the program itself. Comments and criticism are greatly appreciated.
Tags: programming, screenshots, python, gnome, cairo, gdp
Posted on 2006-08-02
Ainalda and I did a software upgrade on the server today. This is our first upgrade since the Sarge roll-over. I am pleased to report that, to my knowledge, nothing is broken. If you notice any abnormalities, please let me know via e-mail. While I was tweaking the server, I took the opportunity to fix my Moot interface script so that my
I'm starting to get frustrated with the structural fallabilities of MoinMoin. Ainalda and I are both eager to develop a new Wiki system that will use an SVN back-end. Unfortunately, there are a number of issues that complicate CGI SVN access. We have discovered that local repository access from any user other than www-data will alter permissions and make the repository inaccessable.
We have looked at several systems that externally interface with a local SVN repository, and most of them are pretty convoluted. WebSVN is a PHP CGI system that provides a quality HTML interface for repository browsing. It uses a wrapper that sits on top of the svnlook command line utility, which is a really nasty hack. I started looking at other solutions, namely Trac, a Wiki, repository browser, and bug tracking system that integrates with SVN via Python. They use the official SVN Python bindings, which are the grotesque result of a failed Swig experiment.
Although no documentation is available for the SVN Python bindings, I did manage to find some semi-useful examples at Koders. Using those examples and some interactive Python pounding, I managed to produce a Python script that, when run as www-data, will display the requested file from a particular revision of the repository.
Tags: python, programming, subversion
Posted on 2005-07-11
I am currently working on designing an article management system to help me keep track of things. Unfortunately, progress has been slow. It took me a long time to pick a suitable back-end. At first, I wanted to use an XML serialization mechanism to store the data, but my experiments indicated that an XML solution would place severe limitations on scalability. After that, I began to look at databases.
SQLite was the obvious solution, but it has a lot of limitations that bother me. I also took a look at Metakit, an object oriented database library made by Equi4. Metakit is a good library, but I strongly dislike it's Python API, and it's lack of a true query system conflicts with the user interface paradigm that I want to implement. My final conclusion is that SQLite is best suited for the task.
I recently discovered that MozStorage, the Mozilla framework's next generation data storage system, will utilize SQLite. MozStorage will be used for Firefox's much needed new bookmark system. The current bookmark system has a vast number of deficiencies, and I ardently welcome improvements that will make it easier for me to manage and search my 900+ bookmarks.
Tags: python, programming
Posted on 2005-07-08