PyGTK and Threading

by Jeremy Jones

I've been working on wrapping PyGTK around a podcast grabbing Python script that I wrote. You can take a glance at the code that actually does the work here. The posted code, particularly the configuration management, is really tightly coupled to all other parts of the app. Also, the download manager expects much more information than it should in an odd data type comprised of a dictionary and an elementtree object. I've removed some of those warts in the version I'm currently working on.

Anyway, I've been working on wrapping PyGTK around the above-mentioned script and have finally gotten something that sort of works. One of the minor annoyances, though, is that when I click on a button that tells the app to show me which podcasts are available for download, the app freezes up until it has figured out every last available download. Even though I have code to update the status bar in the method which handles this button press, the only way I can figure out if anything is going on is to look at the terminal window I started the podgrabber app from.

Since I have the app somewhat functional, I decided to fix this blocking annoyance. The reason that this GUI app is behaving like this is that when the application is launched, it is being controlled by a single thread of execution. When you click on any button, that single thread of execution has to run code to perform any actions associated with that button click event. Subsequently, all other activity handling is blocked until the main thread returns from handling the current request.

So, a fix is to allow the main thread of execution to spawn a new thread to handle the button click event. Here is the code that currently executes when the "show downloads" button is clicked:

def OnShowDownloads(self, widget):
status_bar = self.wTree.get_widget("mainStatusbar")
self.downloadList.clear()
for feed_url in self.controller.get_available_feeds():
feed_name = self.config.Feeds[feed_url]["name"]
mode = self.config.Feeds[feed_url]["mode"]
status_bar.push(1, feed_name)
for download in self.controller.get_download_list(feed_url):
dl_url = download["enclosure"].attrib.get("url", "NONE")
try:
length = int(download["enclosure"].attrib.get("length", "0"))
except ValueError:
length = 0
file_type = download["enclosure"].attrib.get("type", "UNKNOWN")
title = download.get("title", "No Title")
self.downloadList.append([feed_name, dl_url, length, file_type, mode, title])
status_bar.push(1, "%s - Done" % feed_name)



Adding threading support here was pretty easy. I simply had to add "gtk.gdk.threads_init()" at the beginning of the program, create a threading decorator, apply the threading decorator to the event handler, and strategically sprinkle "gtk.threads_enter()" and "gtk.threads_leave()" throughout the affected code. Here is the decorator that I created:

def threaded(f):
def wrapper(*args):
t = threading.Thread(target=f, args=args)
t.start()
return wrapper


And here is the slightly modified handler method:
    @threaded
def OnShowDownloads(self, widget):
gtk.threads_enter()
status_bar = self.wTree.get_widget("mainStatusbar")
self.downloadList.clear()
gtk.threads_leave()
for feed_url in self.controller.get_available_feeds():
feed_name = self.config.Feeds[feed_url]["name"]
mode = self.config.Feeds[feed_url]["mode"]
gtk.threads_enter()
status_bar.push(1, feed_name)
gtk.threads_leave()
for download in self.controller.get_download_list(feed_url):
dl_url = download["enclosure"].attrib.get("url", "NONE")
try:
length = int(download["enclosure"].attrib.get("length", "0"))
except ValueError:
length = 0
file_type = download["enclosure"].attrib.get("type", "UNKNOWN")
title = download.get("title", "No Title")
gtk.threads_enter()
self.downloadList.append([feed_name, dl_url, length, file_type, mode, title])
gtk.threads_leave()
gtk.threads_enter()
status_bar.push(1, "%s - Done" % feed_name)
gtk.threads_leave()


4 Comments

Cary
2006-07-16 15:01:18
Twisted integrates with the gtk event loop quite easily.
You wouldn't need to use threads for something like this
since it's completely IO bound.
Jeremy Jones
2006-07-17 05:16:19
Cary,


I had seen that about Twisted and gtk. Currently, I'm using urllib to pull down the podcasts, but had considered switching to Twisted for that, so using it in other places should come pretty easily. I wouldn't include Twisted just to avoid using threads for the GUI, but if I were already using it elsewhere, I'd likely do it.

Keriserg
2007-02-05 02:01:17
Hi all!

Where it is possible to find out about high school diploma?
Max
2008-05-30 19:10:54
You can find out about high school diploma by not flunking out you failure.