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() |
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() |
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() |
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() |
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.