Twisted + PyGTK

A few weeks ago I starded interresting about Twisted and asynchronous programming. I realy liked the way that Twisted handles events and callbacks.

I’ve been working in a program with a graphical interface built in PyGTK that uses threads for every single taks and I wondered if I can had this program running in just one thread asynchronously. It’s realy simple implement twisted code in a PyGTK applicacion since both are event-based and Twisted has a native API to do so.

Let’s build a simple PyGTK example:

import gtk
 
class Gui:
    def __init__(self):
        self.window = gtk.Window()
        self.window.set_default_size(200,200)
        self.window.set_title("Simple PyGTK program")
 
        self.vbox = gtk.VBox()
 
        self.button = gtk.Button("I'm a button!")
        self.label = gtk.Label("Nothing here")
 
        self.vbox.pack_start(self.button)
        self.vbox.pack_start(self.label)
 
        self.window.add(self.vbox)
 
        self.button.connect("clicked", self.on_clicked)
        self.window.connect("destroy", lambda x: gtk.main_quit())
        self.window.show_all()
        self.window.show()
 
    def on_clicked(self, widget):
       self.label.set_text("You clicked the button!") 
 
if __name__ == "__main__":
    app = Gui()
    #starts the GTK loop
    gtk.main()

So, this example is using the Gtk loop. Twisted applications use the Twisted reactor, and we need the Twisted reactor to “understend” Gtk signals.
This is what you have to do:

from twisted.internet import gtk2reactor # for gtk-2.0
gtk2reactor.install()
 
#Your code
#...
 
from twisted.internet import reactor
#this starts the reactor
reactor.run()

Now we can re-implement our first example and it would look like this:

import gtk
from twisted.internet import gtk2reactor # for gtk-2.0
gtk2reactor.install() #this installs the gtk reactor
#NOTE: This have to be at top always, before importing the reactor
 
class Gui:
    def __init__(self):
        self.window = gtk.Window()
        self.window.set_default_size(200,200)
        self.window.set_title("Simple PyGTK program")
 
        self.vbox = gtk.VBox()
 
        self.button = gtk.Button("I'm a button!")
        self.label = gtk.Label("Nothing here")
 
        self.vbox.pack_start(self.button)
        self.vbox.pack_start(self.label)
 
        self.window.add(self.vbox)
 
        self.button.connect("clicked", self.on_clicked)
        self.window.connect("destroy", lambda x: gtk.main_quit())
        self.window.show_all()
        self.window.show()
 
    def on_clicked(self, widget):
       self.label.set_text("You clicked the button!") 
 
if __name__ == "__main__":
    app = Gui()
    #No Gtk anymore!
    #gtk.main()
    from twisted.internet import reactor
    #let's start the loop
    reactor.run()

Now you can enjoy the Twisted power in your graphical apps, here is a (very)simple example using Twisted-way callbacks.

import gtk
import time
from twisted.internet import gtk2reactor # for gtk-2.0
gtk2reactor.install() #this installs the gtk reactor
#NOTE: This have to be at top always, before starting the reactor
 
class Gui:
    def __init__(self):
        self.window = gtk.Window()
        self.window.set_default_size(200,200)
        self.window.set_title("Simple PyGTK program")
 
        self.vbox = gtk.VBox()
 
        self.button = gtk.Button("I'm a button!")
 
        self.time = time.time()
 
        self.label = gtk.Label("0")
 
        self.vbox.pack_start(self.button)
        self.vbox.pack_start(self.label)
 
        self.window.add(self.vbox)
 
        self.button.connect("clicked", self.on_clicked)
        self.window.connect("destroy", lambda x: gtk.main_quit())
        self.window.show_all()
        self.window.show()
        self.time = time.time()
 
    def on_clicked(self, widget=None):
        self.label_text = time.time() - self.time
        self.label.set_text("%.2fs" % self.label_text)
        #Twisted will call the sefl.on_clicked function every 100ms
        reactor.callLater(.1, self.on_clicked)
 
 
if __name__ == "__main__":
    app = Gui()
    #No Gtk anymore!
    #gtk.main()
    from twisted.internet import reactor
    #let's start the loop
    reactor.run()

Twisted also has APIS for others toolkits such as Tkinter, wxPython, Win32(Windows) and PyUI.
For more information you can visit the official documentation.