I wanted to try out a new idea about doing threads with pygtk. While experimenting a bit it seems I stumbled upon a bug in pygtk. I have a number of equivallent threads who all produce some kind of result which they then put on a canvas.
The method they use is like this: def Adjust(self, ThrdIx, Time, Value): gdk.threads_enter() Do some calculations and "painting" gdk.flush() gdk.threads_leave() self.ThrdInfo[ThrdIx] = [Time, Value] #end Adjust If I run the program like this, it will crash after some time with an Xlib Error. Most often a Xlib: unexpected async reply but I also received an X Window System error. However if I move the gdk.threads_leave() to the last line the problem disappears. But moving that call to the last line doesn't put any gtk or gdk call within the gdk.threads_enter gdk.threads_leave pair so that shouldn't have an influence. There is also no other thread that is running or any gtk or gdk call somewhere else within the threads that are running. So in my opinion this points to a bug in pygtk. The complete program is here below, start it up and click in the window, normally a crash occurs before the numbers reach 300000. I am working with a Debian testing system with a 2.4.27-2-686 kernel. libgtk 2.6.4-3, python 2.3.5-2, python-gtk2 2.6.2-1 ------------------------------------------------------------- import pygtk pygtk.require('2.0') import gtk import gtk.gdk as gdk import pango from threading import Thread, Lock from random import Random, randint, sample from time import sleep, time import os import sys pname = sys.argv[0].split(os.sep)[-1] demo_id = pname.split('.')[0].replace("demo" , '') def Connect(obj, signal, handler, *args): def wrap(handler): def wrapper(obj, *args): gdk.threads_leave() handler(obj, *args) gdk.threads_enter() #end wrapper return wrapper #end wrap obj.connect(signal, wrap(handler), *args) #end Connect class Counting (Thread): def __init__(self, Id): self.Id = Id self.ctrl = True self.Running = False self.ShowMode = 0 self.Time = 0 self.Value = 250 self.lock = Lock() self.lock.acquire() self.PRG = Random() Thread.__init__(self) #end __init__ def run(self): def limit(x, sub, sup): if x < sub: return sub elif x > sup: return sup else: return x #end if #end limit while 1: if not self.Running: self.lock.acquire() #end if if not self.ctrl: return #end if OldTime = self.Time self.Time = OldTime + self.PRG.randint(1,20) while OldTime < self.Time: OldTime += 1 #end while self.Value = limit(self.Value + self.PRG.randint(-5,5) , 0 , 500) canvas.Adjust(self.Id, self.Time , self.Value) #end while #end run def Start_Stop(self,ignore): if self.Running: self.Running = False else: self.Running = True self.lock.release() #end if #end Start_Stop def Quit(self): self.ctrl = False if not self.Running: self.Running = True self.lock.release() #end if #end Quit #end Counting Worker = [ Counting(i) for i in xrange(7) ] for W in sample(Worker,7): W.start() #end for RowHght = 25 def index2rgb(ix, ColorScale = 3 * 65535 / 5): return (ColorScale * (ix & 4) >> 2, ColorScale * (ix & 2) >> 1, ColorScale * (ix & 1) >> 0) #end index2rgb class Canvas(gtk.DrawingArea): def __init__(self): def On_Expose(canvas, evt): gc = canvas.window.new_gc() lb = canvas.window.new_gc() cm = gc.get_colormap() gdk.threads_enter() for i in xrange(7): color = cm.alloc_color(*index2rgb(i)) gc.set_foreground(color) canvas.window.draw_rectangle(gc, True , 75, (2 * i + 1) * RowHght , canvas.ThrdInfo[i][1] , RowHght ) canvas.layout.set_text("%8d" % (canvas.ThrdInfo[i][0],)) canvas.window.draw_layout(lb, 5 , (2 * i + 1) * RowHght + canvas.TxtPad , canvas.layout) #end for gdk.threads_leave() #end On_Expose gtk.DrawingArea.__init__(self) self.add_events(gdk.BUTTON_PRESS_MASK) self.set_size_request(600, 15 * RowHght) self.layout = self.create_pango_layout("") desc = self.layout.get_context().get_font_description() desc.set_family("Monospace") self.TxtPad = desc.get_size() / (2 * pango.SCALE) self.ThrdInfo = [ [0 , 250 ][:] for x in range(7) ] self.layout.set_font_description(desc) Connect(self, "expose_event" , On_Expose) Connect(self, "button_press_event" , self.On_Click) #end __init__ def On_Click(self, evnt, info): for W in sample(Worker,7): W.Start_Stop(None) #end for #end On_Click def Adjust(self, ThrdIx, Time, Value): gdk.threads_enter() gc = self.window.new_gc() lb = self.window.new_gc() cm = gc.get_colormap() OldValue = self.ThrdInfo[ThrdIx][1] if OldValue < Value: color = cm.alloc_color(*index2rgb(ThrdIx)) base = 75 + OldValue else: color = self.style.bg[0] base = 75 + Value #end if gc.set_foreground(color) self.window.draw_rectangle(gc, True , base , (2 * ThrdIx + 1) * RowHght , abs(Value - OldValue) , RowHght) self.layout.set_text("%8d" % (Time,)) self.window.begin_paint_rect((0, (2 * ThrdIx + 1) * RowHght, 75 , RowHght)) self.window.draw_layout(lb, 5 , (2 * ThrdIx + 1) * RowHght + self.TxtPad , self.layout) self.window.end_paint() gdk.flush() gdk.threads_leave() self.ThrdInfo[ThrdIx] = [Time, Value] #end Adjust #end Canvas class Frame(gtk.Window): def __init__(self,canvas): gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL) self.set_title("Thread %s Demonstration" % demo_id) Connect(self, "delete_event" , self.On_Delete) self.add(canvas) canvas.show() self.show() #end __init__ def On_Delete(self, widget, evt, data=None): for W in sample(Worker,7): W.Quit() #end for for W in sample(Worker,7): W.join() #end for gtk.main_quit() return False #end On_Delete #end Frame gtk.threads_init() canvas=Canvas() Win=Frame(canvas) gtk.threads_enter() gtk.main() gtk.threads_leave() _______________________________________________ pygtk mailing list pygtk@daa.com.au http://www.daa.com.au/mailman/listinfo/pygtk Read the PyGTK FAQ: http://www.async.com.br/faq/pygtk/