diff --git a/IPython/zmq/gui/__init__.py b/IPython/zmq/gui/__init__.py
new file mode 100644
index 0000000..91037ba
--- /dev/null
+++ b/IPython/zmq/gui/__init__.py
@@ -0,0 +1,15 @@
+"""GUI support for the IPython ZeroMQ kernel.
+
+This package contains the various toolkit-dependent utilities we use to enable
+coordination between the IPython kernel and the event loops of the various GUI
+toolkits.
+"""
+
+#-----------------------------------------------------------------------------
+#  Copyright (C) 2010  The IPython Development Team.
+#
+#  Distributed under the terms of the BSD License.
+#
+#  The full license is in the file COPYING.txt, distributed as part of this
+#  software.
+#-----------------------------------------------------------------------------
diff --git a/IPython/zmq/gui/gtkembed.py b/IPython/zmq/gui/gtkembed.py
new file mode 100644
index 0000000..4d33663
--- /dev/null
+++ b/IPython/zmq/gui/gtkembed.py
@@ -0,0 +1,86 @@
+"""GUI support for the IPython ZeroMQ kernel - GTK toolkit support.
+"""
+#-----------------------------------------------------------------------------
+#  Copyright (C) 2010  The IPython Development Team
+#
+#  Distributed under the terms of the BSD License.  The full license is in
+#  the file COPYING.txt, distributed as part of this software.
+#-----------------------------------------------------------------------------
+
+#-----------------------------------------------------------------------------
+# Imports
+#-----------------------------------------------------------------------------
+# stdlib
+import sys
+
+# Third-party
+import gobject
+import gtk
+
+#-----------------------------------------------------------------------------
+# Classes and functions
+#-----------------------------------------------------------------------------
+
+class GTKEmbed(object):
+    """A class to embed a kernel into the GTK main event loop.
+    """
+    def __init__(self, kernel):
+        self.kernel = kernel
+        # These two will later store the real gtk functions when we hijack them
+        self.gtk_main = None
+        self.gtk_main_quit = None
+
+    def start(self):
+        """Starts the GTK main event loop and sets our kernel startup routine.
+        """
+        # Register our function to initiate the kernel and start gtk
+        gobject.idle_add(self._wire_kernel)
+        gtk.main()
+
+    def _wire_kernel(self):
+        """Initializes the kernel inside GTK.
+        
+        This is meant to run only once at startup, so it does its job and
+        returns False to ensure it doesn't get run again by GTK.
+        """
+        self.gtk_main, self.gtk_main_quit = self._hijack_gtk()
+        gobject.timeout_add(int(1000*self.kernel._poll_interval),
+                            self.iterate_kernel)
+        return False
+        
+    def iterate_kernel(self):
+        """Run one iteration of the kernel and return True.
+
+        GTK timer functions must return True to be called again, so we make the
+        call to :meth:`do_one_iteration` and then return True for GTK.
+        """
+        self.kernel.do_one_iteration()
+        return True
+
+    def stop(self):
+        # FIXME: this one isn't getting called because we have no reliable
+        # kernel shutdown.  We need to fix that: once the kernel has a
+        # shutdown mechanism, it can call this.
+        self.gtk_main_quit()
+        sys.exit()
+
+    def _hijack_gtk(self):
+        """Hijack a few key functions in GTK for IPython integration.
+
+        Modifies pyGTK's main and main_quit with a dummy so user code does not
+        block IPython.  This allows us to use %run to run arbitrary pygtk
+        scripts from a long-lived IPython session, and when they attempt to
+        start or stop
+
+        Returns
+        -------
+        The original functions that have been hijacked:
+        - gtk.main
+        - gtk.main_quit
+        """
+        def dummy(*args, **kw):
+            pass
+        # save and trap main and main_quit from gtk
+        orig_main, gtk.main = gtk.main, dummy
+        orig_main_quit, gtk.main_quit = gtk.main_quit, dummy
+        return orig_main, orig_main_quit
diff --git a/IPython/zmq/ipkernel.py b/IPython/zmq/ipkernel.py
index 5f51f78..5105f95 100755
--- a/IPython/zmq/ipkernel.py
+++ b/IPython/zmq/ipkernel.py
@@ -88,6 +88,8 @@ class Kernel(Configurable):
             self.handlers[msg_type] = getattr(self, msg_type)
 
     def do_one_iteration(self):
+        """Do one iteration of the kernel's evaluation loop.
+        """
         try:
             ident = self.reply_socket.recv(zmq.NOBLOCK)
         except zmq.ZMQError, e:
@@ -373,7 +375,8 @@ class WxKernel(Kernel):
         import wx
         from IPython.lib.guisupport import start_event_loop_wx
         doi = self.do_one_iteration
-        _poll_interval = self._poll_interval
+         # Wx uses milliseconds
+        poll_interval = int(1000*self._poll_interval)
 
         # We have to put the wx.Timer in a wx.Frame for it to fire properly.
         # We make the Frame hidden when we create it in the main app below.
@@ -382,9 +385,10 @@ class WxKernel(Kernel):
                 wx.Frame.__init__(self, None, -1)
                 self.timer = wx.Timer(self)
                 # Units for the timer are in milliseconds
-                self.timer.Start(1000*_poll_interval)
+                self.timer.Start(poll_interval)
                 self.Bind(wx.EVT_TIMER, self.on_timer)
                 self.func = func
+
             def on_timer(self, event):
                 self.func()
 
@@ -410,17 +414,19 @@ class TkKernel(Kernel):
 
         import Tkinter
         doi = self.do_one_iteration
-
+        # Tk uses milliseconds
+        poll_interval = int(1000*self._poll_interval)
         # For Tkinter, we create a Tk object and call its withdraw method.
         class Timer(object):
             def __init__(self, func):
                 self.app = Tkinter.Tk()
                 self.app.withdraw()
                 self.func = func
+                
             def on_timer(self):
                 self.func()
-                # Units for the timer are in milliseconds
-                self.app.after(1000*self._poll_interval, self.on_timer)
+                self.app.after(poll_interval, self.on_timer)
+                
             def start(self):
                 self.on_timer()  # Call it once to get things going.
                 self.app.mainloop()
@@ -428,13 +434,25 @@ class TkKernel(Kernel):
         self.timer = Timer(doi)
         self.timer.start()
 
+
+class GTKKernel(Kernel):
+    """A Kernel subclass with GTK support."""
+    
+    def start(self):
+        """Start the kernel, coordinating with the GTK event loop"""
+        from .gui.gtkembed import GTKEmbed
+        
+        gtk_kernel = GTKEmbed(self)
+        gtk_kernel.start()
+
+
 #-----------------------------------------------------------------------------
 # Kernel main and launch functions
 #-----------------------------------------------------------------------------
 
 def launch_kernel(xrep_port=0, pub_port=0, req_port=0, hb_port=0,
                   independent=False, pylab=False):
-    """ Launches a localhost kernel, binding to the specified ports.
+    """Launches a localhost kernel, binding to the specified ports.
 
     Parameters
     ----------
@@ -490,19 +508,20 @@ given, the GUI backend is matplotlib's, otherwise use one of: \
 
     kernel_class = Kernel
 
-    _kernel_classes = {
+    kernel_classes = {
         'qt' : QtKernel,
-        'qt4' : QtKernel,
+        'qt4': QtKernel,
         'payload-svg': Kernel,
         'wx' : WxKernel,
-        'tk' : TkKernel
+        'tk' : TkKernel,
+        'gtk': GTKKernel,
     }
     if namespace.pylab:
         if namespace.pylab == 'auto':
             gui, backend = pylabtools.find_gui_and_backend()
         else:
             gui, backend = pylabtools.find_gui_and_backend(namespace.pylab)
-        kernel_class = _kernel_classes.get(gui)
+        kernel_class = kernel_classes.get(gui)
         if kernel_class is None:
             raise ValueError('GUI is not supported: %r' % gui)
         pylabtools.activate_matplotlib(backend)