diff -urN matplotlib/lib/matplotlib/backends/backend_gdk.py matplotlib-gui/lib/matplotlib/backends/backend_gdk.py
--- matplotlib/lib/matplotlib/backends/backend_gdk.py	2004-12-23 16:48:19.000000000 +0100
+++ matplotlib-gui/lib/matplotlib/backends/backend_gdk.py	2005-01-23 15:11:58.000000000 +0100
@@ -8,6 +8,7 @@
 from matplotlib import verbose
 from matplotlib.numerix import asarray, fromstring, UInt8, zeros, \
      where, transpose, nonzero, indices, ones, nx
+from matplotlib import gtk_protect as protect
 
 import matplotlib.numerix as numerix
 from matplotlib.cbook import is_string_like, enumerate, onetrue
@@ -82,6 +83,7 @@
         self.gtkDA = gtkDA
         self.dpi   = dpi
         self._cmap = gtkDA.get_colormap()
+    __init__=protect(__init__)
         
     def _set_pixmap (self, gdkDrawable):
         self.gdkDrawable = gdkDrawable
@@ -102,6 +104,7 @@
             self.gdkDrawable.draw_arc(gc.gdkGC, True, x, y, w, h, a1, a2)
             gc.gdkGC.foreground = saveColor
         self.gdkDrawable.draw_arc(gc.gdkGC, False, x, y, w, h, a1, a2)
+    draw_arc=protect(draw_arc)
 
 
     def draw_image(self, x, y, im, origin, bbox):
@@ -145,21 +148,25 @@
             pb.render_to_drawable(self.gdkDrawable, gc.gdkGC, 0, 0,
                                   int(x), int(y), cols, rows,
                                   gdk.RGB_DITHER_NONE, 0, 0)
+    draw_image=protect(draw_image)
 
             
     def draw_line(self, gc, x1, y1, x2, y2):
         self.gdkDrawable.draw_line(gc.gdkGC, int(x1), self.height-int(y1),
                                    int(x2), self.height-int(y2))
+    draw_line=protect(draw_line)
 
 
     def draw_lines(self, gc, x, y):
         x = x.astype(nx.Int16)
         y = self.height - y.astype(nx.Int16)  
         self.gdkDrawable.draw_lines(gc.gdkGC, zip(x,y))
+    draw_lines=protect(draw_lines)
         
 
     def draw_point(self, gc, x, y):
         self.gdkDrawable.draw_point(gc.gdkGC, int(x), self.height-int(y))
+    draw_point=protect(draw_point)
 
 
     def draw_polygon(self, gc, rgbFace, points):
@@ -170,6 +177,7 @@
             self.gdkDrawable.draw_polygon(gc.gdkGC, True, points)
             gc.gdkGC.foreground = saveColor
         self.gdkDrawable.draw_polygon(gc.gdkGC, False, points)
+    draw_polygon=protect(draw_polygon)
 
 
     def draw_rectangle(self, gc, rgbFace, x, y, width, height):
@@ -183,6 +191,7 @@
             self.gdkDrawable.draw_rectangle(gc.gdkGC, True, x, y, w, h)
             gc.gdkGC.foreground = saveColor
         self.gdkDrawable.draw_rectangle(gc.gdkGC, False, x, y, w, h)
+    draw_rectangle=protect(draw_rectangle)
 
 
     def draw_text(self, gc, x, y, s, prop, angle, ismath):
@@ -201,6 +210,7 @@
             layout, inkRect, logicalRect = self._get_pango_layout(s, prop)
             l, b, w, h = inkRect
             self.gdkDrawable.draw_layout(gc.gdkGC, x, y-h-b, layout)
+    draw_text=protect(draw_text)
 
         
     def _draw_mathtext(self, gc, x, y, s, prop, angle):
@@ -258,6 +268,7 @@
             pb.render_to_drawable(self.gdkDrawable, gc.gdkGC, 0, 0,
                                   int(x), int(y), imw, imh,
                                   gdk.RGB_DITHER_NONE, 0, 0)
+    _draw_mathtext=protect(_draw_mathtext)
             
         
     def _draw_rotated_text(self, gc, x, y, s, prop, angle):
@@ -307,6 +318,7 @@
         gdrawable.draw_image(ggc, imageBack, 0, 0, x, y, w, h)
         gdrawable.draw_image(ggc, imageVert, 0, 0, x, y, h, w)
         self.rotated[key] = imageVert
+    _draw_rotated_text=protect(_draw_rotated_text)
 
 
     def _get_pango_layout(self, s, prop):
@@ -337,6 +349,7 @@
         
         self.layoutd[key] = layout, inkRect, logicalRect
         return layout, inkRect, logicalRect
+    _get_pango_layout=protect(_get_pango_layout)
 
 
     def flipy(self):
@@ -386,6 +399,7 @@
         self.renderer = renderer
         self.gdkGC    = gtk.gdk.GC(renderer.gdkDrawable)
         self._cmap    = renderer._cmap
+    __init__=protect(__init__)
 
 
     def rgb_to_gdk_color(self, rgb):
@@ -414,7 +428,8 @@
                      int(w), int(h))
         #rectangle = (int(l), self.renderer.height-int(b+h),
         #             int(w+1), int(h+2))
-        self.gdkGC.set_clip_rectangle(rectangle)        
+        self.gdkGC.set_clip_rectangle(rectangle)
+    set_clip_rectangle=protect(set_clip_rectangle)
 
 
     def set_dashes(self, dash_offset, dash_list):
@@ -473,6 +488,7 @@
     def _renderer_init(self):
         #self._renderer = RendererGDK (self, self.figure.dpi) # self is no longer a widget subclass
         self._renderer = RendererGDK (gtk.DrawingArea(), self.figure.dpi)
+    _renderer_init=protect(_renderer_init)
 
         
     def _render_to_pixmap(self, width, height):
@@ -502,6 +518,7 @@
 
         self._renderer._set_width_height (width, height)
         self.figure.draw (self._renderer)
+    _render_to_pixmap=protect(_render_to_pixmap)
 
 
     def print_figure(self, filename, dpi=150, facecolor='w', edgecolor='w',
@@ -566,3 +583,4 @@
             error_msg('Format "%s" is not supported.\nSupported formats are %s.' %
                       (ext, ', '.join(IMAGE_FORMAT)),
                       parent=self)
+    print_figure=protect(print_figure)
diff -urN matplotlib/lib/matplotlib/backends/backend_gtkagg.py matplotlib-gui/lib/matplotlib/backends/backend_gtkagg.py
--- matplotlib/lib/matplotlib/backends/backend_gtkagg.py	2004-12-14 23:27:08.000000000 +0100
+++ matplotlib-gui/lib/matplotlib/backends/backend_gtkagg.py	2005-01-23 15:38:27.000000000 +0100
@@ -7,6 +7,7 @@
 from matplotlib import verbose
 from matplotlib.cbook import enumerate
 from matplotlib.figure import Figure
+from matplotlib import gtk_protect as protect
 
 from backend_agg import FigureCanvasAgg
 from backend_gtk import gtk, FigureManagerGTK, FigureCanvasGTK,\
@@ -48,6 +49,7 @@
     def blit(self):
         if self.window is None: return        
         agg_to_gtk_drawable(self.window, self.renderer._renderer)
+    blit=protect(blit)
 
     def print_figure(self, filename, dpi=150,
                      facecolor='w', edgecolor='w',
@@ -83,6 +85,7 @@
         self.figure.set_figsize_inches(winch, hinch)
         
         return gtk.TRUE
+    configure_event=protect(configure_event)
     
     def expose_event(self, widget, event):
         if DEBUG: print 'FigureCanvasGTKAgg.expose_event'        
@@ -99,3 +102,4 @@
 
 
         return gtk.TRUE
+    expose_event=protect(expose_event)
diff -urN matplotlib/lib/matplotlib/backends/backend_gtk.py matplotlib-gui/lib/matplotlib/backends/backend_gtk.py
--- matplotlib/lib/matplotlib/backends/backend_gtk.py	2005-01-06 12:45:04.000000000 +0100
+++ matplotlib-gui/lib/matplotlib/backends/backend_gtk.py	2005-01-23 17:40:50.000000000 +0100
@@ -8,6 +8,7 @@
 from matplotlib import verbose, MPLError
 from matplotlib.numerix import asarray, fromstring, UInt8, zeros, \
      where, transpose, nonzero, indices, ones, nx
+from matplotlib import gtk_protect as protect
 
 import matplotlib.numerix as numerix
 from matplotlib.cbook import is_string_like, enumerate, onetrue
@@ -53,11 +54,12 @@
 IMAGE_FORMAT_DEFAULT  = 'png'
 
 
+gdkCursor=protect(gtk.gdk.Cursor)
 cursord = {
-    cursors.MOVE          : gtk.gdk.Cursor(gtk.gdk.FLEUR),
-    cursors.HAND          : gtk.gdk.Cursor(gtk.gdk.HAND2),
-    cursors.POINTER       : gtk.gdk.Cursor(gtk.gdk.LEFT_PTR),
-    cursors.SELECT_REGION : gtk.gdk.Cursor(gtk.gdk.TCROSS),
+    cursors.MOVE          : gdkCursor(gtk.gdk.FLEUR),
+    cursors.HAND          : gdkCursor(gtk.gdk.HAND2),
+    cursors.POINTER       : gdkCursor(gtk.gdk.LEFT_PTR),
+    cursors.SELECT_REGION : gdkCursor(gtk.gdk.TCROSS),
     }
 
 # ref gtk+/gtk/gtkwidget.h
@@ -80,10 +82,12 @@
     """
     for manager in Gcf.get_all_fig_managers():
         manager.window.show()
+        manager.canvas.draw()
         
     if mainloop and gtk.main_level() == 0:
         if gtk.pygtk_version >= (2,4,0): gtk.main()
         else:                            gtk.mainloop()
+show=protect(show)
 
 
 def new_figure_manager(num, *args, **kwargs):
@@ -144,6 +148,7 @@
             gdk.POINTER_MOTION_MASK  )
 
         self._renderer_init()
+    __init__=protect(__init__)
 
     def button_press_event(self, widget, event):
         x = event.x
@@ -199,6 +204,7 @@
         
         self._draw_pixmap = True
         return True
+    configure_event=protect(configure_event)
         
 
     def draw(self):
@@ -242,6 +248,7 @@
 
         self._renderer._set_width_height (width, height)
         self.figure.draw (self._renderer)
+    _render_to_pixmap=protect(_render_to_pixmap)
 
 
     def expose_event(self, widget, event):
@@ -256,6 +263,7 @@
             self.window.clear()  # draw pixmap as the gdk.Window's bg
             self._draw_pixmap = False
         return True
+    expose_event=protect(expose_event)
 
 
     def print_figure(self, filename, dpi=150, facecolor='w', edgecolor='w',
@@ -275,7 +283,8 @@
 
         if self.flags() & gtk.REALIZED == 0:
             # for self.window(for pixmap) and has a side effect of altering figure width,height (via configure-event?)
-            gtk.DrawingArea.realize(self) 
+            realize=protect(gtk.DrawingArea.realize)
+            realize(self) 
 
         self.figure.dpi.set(dpi)        
         self.figure.set_facecolor(facecolor)
@@ -385,6 +394,7 @@
             'this will be called whenever the current axes is changed'        
             if self.toolbar != None: self.toolbar.update()
         self.canvas.figure.add_axobserver(notify_axes_change)
+    __init__=protect(__init__)
 
     
     def destroy(self, *args):
@@ -392,6 +402,7 @@
         self.window.destroy()
         if Gcf.get_num_fig_managers()==0 and not matplotlib.is_interactive():
             gtk.main_quit()
+    destroy=protect(destroy)
 
         
 class NavigationToolbar2GTK(NavigationToolbar2, gtk.Toolbar):
@@ -412,6 +423,7 @@
         gtk.Toolbar.__init__(self)
         NavigationToolbar2.__init__(self, canvas)
         self._idleId = 0
+    __init__=protect(__init__)
 
     def set_message(self, s):
         if self._idleId==0: self.message.set_label(s)
@@ -419,6 +431,7 @@
         
     def set_cursor(self, cursor):
         self.canvas.window.set_cursor(cursord[cursor])
+    set_cursor=protect(set_cursor)
 
     def release(self, event):
         try: del self._imageBack
@@ -431,6 +444,7 @@
             return False
         if self._idleId==0:
             self._idleId = gtk.idle_add(idle_draw)
+    dynamic_update=protect(dynamic_update)
         
     def draw_rubberband(self, event, x0, y0, x1, y1):
         'adapted from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/189744'
@@ -468,6 +482,7 @@
                 return False
             if self._idleId==0:
                 self._idleId = gtk.idle_add(idle_draw)
+    draw_rubberband=protect(draw_rubberband)
         
 
     def _init_toolbar(self):
@@ -505,6 +520,7 @@
 
         self.fileselect = FileSelection(title='Save the figure',
                                         parent=self.win,)
+    _init_toolbar2_2=protect(_init_toolbar2_2)
 
         
         
@@ -538,6 +554,7 @@
 
         self.fileselect = FileChooserDialog(title='Save the figure',
                                             parent=self.win,)
+    _init_toolbar2_4=protect(_init_toolbar2_4)
                                             
     
     def save_figure(self, button):
@@ -608,6 +625,7 @@
                                             parent=self.win)
         self.show_all()            
         self.update()
+    __init__=protect(__init__)
 
 
     def _create_toolitems_2_4(self):
@@ -665,6 +683,7 @@
             self.axismenu.popup (None, None, position_menu, 0, gtk.get_current_event_time())
 
         self.menubutton.connect ("clicked", button_clicked)
+    _create_toolitems_2_4=protect(_create_toolitems_2_4)
 
         
     def _update_2_4(self):
@@ -680,6 +699,7 @@
             self.menubutton.hide()
             
         self.set_active(range(len(self._axes)))
+    _update_2_4=protect(_update_2_4)
 
 
     def _create_toolitems_2_2(self):
@@ -703,6 +723,7 @@
             self.omenu,
             'Select axes that controls affect',
             'Private', 0)
+    _create_toolitems_2_2=protect(_create_toolitems_2_2)
 
 
     def _update_2_2(self):
@@ -718,7 +739,8 @@
         else:
             self.omenu.hide()
             
-        self.set_active(range(len(self._axes))) 
+        self.set_active(range(len(self._axes)))
+    _update_2_2=protect(_update_2_2)
 
 
     def _make_axis_menu(self):
@@ -754,6 +776,7 @@
 
         menu.show_all()
         return menu
+    _make_axis_menu=protect(_make_axis_menu)
     
 
     def set_active(self, ind):
@@ -828,6 +851,7 @@
         else:    self.path = os.getcwd() + os.sep
 
         if parent: self.set_transient_for(parent)
+    __init__=protect(__init__)
             
     def get_filename_from_user(self, path=None, title=None):
         if path:  self.path = path
@@ -839,6 +863,7 @@
             self.path = filename = self.get_filename()
         self.hide()
         return filename
+    get_filename_from_user=protect(get_filename_from_user)
     
 
 if gtk.pygtk_version >= (2,4,0):
@@ -893,6 +918,7 @@
 
             hbox.show_all()
             self.set_extra_widget(hbox)
+        __init__=protect(__init__)
             
 
         def get_filename_from_user (self):
@@ -919,12 +945,14 @@
                     
             self.hide()
             return filename
+        get_filename_from_user=protect(get_filename_from_user)
 
 
 # set icon used when windows are minimized, it requires
 # gtk.pygtk_version >= (2,2,0) with a GDK pixbuf loader for SVG installed
 try:
-    gtk.window_set_default_icon_from_file (
+    set_default_icon=protect(gtk.window_set_default_icon_from_file)
+    set_default_icon (
         os.path.join (matplotlib.rcParams['datapath'], 'matplotlib.svg'))
 except:
     verbose.report('Could not load matplotlib icon: %s' % sys.exc_info()[1])
@@ -946,6 +974,7 @@
         message_format = msg)
     dialog.run()
     dialog.destroy()
+error_msg_gtk=protect(error_msg_gtk)
 
 
 def exception_handler(type, value, tb):
diff -urN matplotlib/lib/matplotlib/__init__.py matplotlib-gui/lib/matplotlib/__init__.py
--- matplotlib/lib/matplotlib/__init__.py	2005-01-01 20:28:58.000000000 +0100
+++ matplotlib-gui/lib/matplotlib/__init__.py	2005-01-17 23:02:53.000000000 +0100
@@ -468,6 +468,7 @@
     'toolbar'           : ['toolbar2', validate_toolbar],
     'datapath'          : [get_data_path(), validate_path_exists],
     'interactive'       : [False, validate_bool],
+    'gui_protect'       : ['None', str],
     'timezone'          : ['UTC', str],    
 
     # the verbosity setting
@@ -804,7 +805,36 @@
 def is_interactive():
     'Return true if plot mode is interactive'
     b = rcParams['interactive']
+    import_protect()
     return b
+
+def protect_dummy(fun):
+    return fun
+
+gtk_protect = protect_dummy
+wx_protect = protect_dummy
+
+def import_protect(protect=None):
+    global gtk_protect
+    global wx_protect
+    if protect is None:
+        protect = rcParams['gui_protect'].lower()
+    if protect == 'none':
+        return False
+    try:
+        protect_name='protect_'+protect
+        protect_mod=__import__('matplotlib.'+protect_name, globals(),locals(), [protect_name])
+    except:
+        verbose.report_error('no module '+protect_name)
+        return False
+    if hasattr(protect_mod, 'gtk_protect'):
+        gtk_protect = protect_mod.gtk_protect
+    if hasattr(protect_mod, 'wx_protect'):
+        wx_protect = protect_mod.wx_protect
+    return True
+
+if is_interactive():
+    import_protect()
     
 def tk_window_focus():
     """Return true if focus maintenance under TkAgg on win32 is on.
diff -urN matplotlib/lib/matplotlib/protect_mutex.py matplotlib-gui/lib/matplotlib/protect_mutex.py
--- matplotlib/lib/matplotlib/protect_mutex.py	1970-01-01 01:00:00.000000000 +0100
+++ matplotlib-gui/lib/matplotlib/protect_mutex.py	2005-01-23 19:09:55.000000000 +0100
@@ -0,0 +1,111 @@
+import threading, time, atexit
+import inspect
+
+GTK_THREAD=None
+IN_GTK=[]
+
+GTK_TEMPLATE="""
+def %(name)s(*args, **kwargs):
+    if IN_GTK or threading.currentThread() is GTK_THREAD:
+        return fun(*args, **kwargs)
+    IN_GTK.append(fun)
+    gtk.threads_enter()
+    try:
+      res = fun(*args, **kwargs)
+    finally:
+      gtk.threads_leave()
+      IN_GTK.pop()
+    return res
+"""
+
+def gtk_protect(fun):
+    if GTK_THREAD is None:
+        import_gtk()
+
+    fill={}
+    fill['name']=fun.__name__
+    try:
+      spec=inspect.getargspec(fun)
+    except TypeError:
+      spec=([], 'args', 'kwargs', None)
+    spec=inspect.formatargspec(*spec)
+    globs={}
+    globs.update(globals())
+    globs['fun']=fun
+    locs={}
+    exec(GTK_TEMPLATE % fill, globs, locs)
+    
+    newfun=locs[fun.__name__]
+    newfun.__doc__="proxy for\n"+fun.__name__+spec
+    if fun.__doc__ is not None:
+      newfun.__doc__+="\n\n"+fun.__doc__
+
+    return newfun
+
+def import_gtk():
+    global GTK_THREAD
+    if GTK_THREAD is not None:
+        return True
+
+    global gtk
+    import pygtk
+    pygtk.require('2.0')
+    import gtk
+
+    gtk.threads_init()
+
+    GTK_THREAD = threading.Thread(target=gtk.main, name='GTK_THREAD')
+    assert gtk.main_level() == 0
+    GTK_THREAD.start()
+    atexit.register(gtk.main_quit)
+
+    time.sleep(1)
+
+    return True
+    
+#####################################
+# Wx support should be possible
+# Here is a try for Wx 2.4
+# Various enhencements should be ported
+# from GTK above
+# The backend should be converted
+"""
+WX_THREAD=False
+
+def wx_protect(fun):
+    if not HAS_WX:
+        import_wx()
+
+    def newfun(*args, **kwargs):
+        wx.MutexGuiEnter()
+        res = fun(*args, **kwargs)
+        wx.MutexGuiLeave()
+        return res
+
+    return newfun
+
+def import_wx():
+    if HAS_WX:
+        return True
+
+
+    def init():
+        global HAS_WX
+        global wx
+        import wx
+        wxapp=wx.PySimpleApp()
+        frame=wx.Frame(None,-1,'')
+        if wx.Thread_IsMain():
+            HAS_WX=True
+            wxapp.MainLoop()
+        else:
+            print "import_wx should be started first"
+
+    thread.start_new(init,())
+
+ # For some reason, import wx stalls until the end of the current module import
+ #   while not HAS_WX:
+ #       time.sleep(1)
+
+    return HAS_WX
+"""
diff -urN matplotlib/lib/matplotlib/pylab.py matplotlib-gui/lib/matplotlib/pylab.py
--- matplotlib/lib/matplotlib/pylab.py	2005-01-23 17:31:15.000000000 +0100
+++ matplotlib-gui/lib/matplotlib/pylab.py	2005-01-23 17:36:10.000000000 +0100
@@ -2466,7 +2466,7 @@
     'axes', 'delaxes', 'clim', 'close', 'clf', 'colorbar', 'draw',
     'figtext', 'figimage', 'figlegend', 'figure', 'gca', 'gcf', 'gci',
     'get', 'hold', 'ishold', 'isinteractive', 'imread', 'load', 'rc',
-    'rcdefaults', 'save', 'savefig', 'set', 'subplot', 'title',
+    'rcdefaults', 'save', 'savefig', 'set', 'subplot', 'twin', 'title',
     'xlabel', 'ylabel', 'xlim', 'ylim', 'xticks', 'rgrids',
     'thetagrids', 'yticks', 'polar', 'over', 'ioff', 'ion', 'axhline',
     'axhspan', 'axvline', 'axvspan', 'bar', 'barh', 'cohere',
