On Wed, Jun 23, 2004 at 11:05:03AM +0000, Tiago Cogumbreiro wrote:
> I for one would like to see them :) A threading tutorial is always nice.
> Making demos out of the most common concurrent patterns would also be
> interesting.
This is the method I finally settled on for handling threads in
pygtk programs. I have written a class that controls the rate
at which add_idle calls can be executed. The more calls that
are pending, the longer a thread has to sleep before it can
add its own. This way we can assure a reasonable time between
the registration and the execution.
I also defined a new signal that will be used to let the widget
know it should be updated. All subthreads then use the above
class to emit the proper signal.
As far as I have understood things, this should work on linux as well
as on window systems. However I have no window machine available,
so I would be gratefulll if those who have, could test this for
me. I have python 2.3.4 here and haven't thought much about
compatibility with previous versions.
---------------------- demo2.py ---------------------------------------------
import gtk
import gtk.gdk as gdk
import pango
from threading import Thread, Lock
from random import Random, randint, shuffle
from time import sleep
from gobject import signal_new, SIGNAL_RUN_LAST, TYPE_BOOLEAN, TYPE_INT,
SIGNAL_ACTION, \
TYPE_NONE, TYPE_PYOBJECT
signal_new("update", gtk.DrawingArea, SIGNAL_RUN_LAST | SIGNAL_ACTION, TYPE_NONE,
(TYPE_PYOBJECT,TYPE_PYOBJECT,TYPE_PYOBJECT))
class Emittor:
def __init__(self, Expected_Entries):
self.Expect = 1.0 * Expected_Entries
self.Entries = 0
self.sleeptime = 0.01
self.entrylck = Lock()
# __init__
def __call__(self, sig, obj, *args):
self.entrylck.acquire()
self.Entries += 1
self.entrylck.release()
sleeptime = self.sleeptime
Entries = self.Entries
if 2 * Entries >= self.Expect:
sleeptime = sleeptime * (1.2 ** (Entries / self.Expect))
sleep(sleeptime)
# if
gtk.idle_add(self.apply, sig, obj, *args)
# __call__
def apply(self, sig, obj, *args):
obj.emit(sig, *args)
self.entrylck.acquire()
self.Entries -= 1
self.entrylck.release()
# apply
#Emittor
Emit = Emittor(14)
rndfl=file('/dev/random')
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()
self.PRG.seed(rndfl.read(8))
Thread.__init__(self)
# __init__
def run(self):
def limit(x, sub, sup):
if x < sub:
return sub
elif x > sup:
return sup
else:
return x
# if
# limit
if not self.Running:
self.lock.acquire()
# if
while self.ctrl:
OldTime = self.Time
self.Time = OldTime + self.PRG.randint(1,20)
while OldTime < self.Time:
OldTime += 1
# while
#sleep(self.PRG.uniform(0.01, 0.03))
self.Value = limit(self.Value + self.PRG.randint(-5,5) , 0 , 500)
if self.ShowMode != 3:
if self.ShowMode % 2 == 0:
self.ShowValue = self.Value
# if
if self.ShowMode / 2 == 0:
self.ShowTime = self.Time
# if
#gtk.idle_add(canvas.emit , "update" , (self.Id, self.ShowTime,
self.ShowValue))
Emit("update" , canvas , self.Id, self.ShowTime, self.ShowValue)
# if
if not self.Running:
self.lock.acquire()
# if
# while
# run
def Start_Stop(self,ignore):
if self.Running:
self.Running = False
else:
self.Running = True
self.lock.release()
# if
# Start_Stop
def Modus(self,ignore):
if self.Running:
self.ShowMode = (self.ShowMode + 1) % 4
else:
if self.ShowMode == 0:
self.Time = 0
self.Value = 250
else:
self.ShowMode = 0
# if
#gtk.idle_add(canvas.emit , "update" , (self.Id, self.Time, self.Value))
Emit("update" , canvas , self.Id, self.Time, self.Value)
# if
# Modus
def Quit(self):
self.ctrl = False
if not self.Running:
self.Running = True
self.lock.release()
# if
# Quit
# Counting
Worker = [ Counting(i) for i in xrange(7) ]
S = range(7)
shuffle(S)
for i in S:
Worker[i].start()
# for
Rnd = Random()
Rnd.seed()
RowHght = 25
BtnSize = (75, RowHght)
#ColorScale = 65535
ColorScale = 3 * 65535 / 5
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()
for i in xrange(7):
r = (i & 4) >> 2
g = (i & 2) >> 1
b = (i & 1) >> 0
color = cm.alloc_color(r * ColorScale, g * ColorScale, b * ColorScale )
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)
# for
# On_Expose
def On_Update(canvas, ThrdIx, Time, Value):
canvas.Adjust(ThrdIx, Time, Value)
# On_Update
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)
self.connect("expose_event" , On_Expose)
self.connect("update" , On_Update)
self.connect("button_press_event" , self.On_Click)
# __init__
def On_Click(self, evnt, info):
for W in Worker:
W.Start_Stop(None)
# for
# On_Click
def Adjust(self, ThrdIx, Time, Value):
r = (ThrdIx & 4) >> 2
g = (ThrdIx & 2) >> 1
b = (ThrdIx & 1) >> 0
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(r * ColorScale, g * ColorScale, b * ColorScale )
gc.set_foreground(color)
self.window.draw_rectangle(gc, True , 75 + OldValue , (2 * ThrdIx + 1) * RowHght
, Value - OldValue , RowHght)
else:
color = self.style.bg[0]
gc.set_foreground(color)
self.window.draw_rectangle(gc, True , 75 + Value , (2 * ThrdIx + 1) * RowHght ,
OldValue - Value , RowHght)
# if
self.layout.set_text("%8d" % (Time,))
color = self.style.bg[0]
gc.set_foreground(color)
self.window.draw_rectangle(gc, True, 0, (2 * ThrdIx + 1) * RowHght, 75 , RowHght)
self.window.draw_layout(lb, 5 , (2 * ThrdIx + 1) * RowHght + self.TxtPad ,
self.layout)
self.ThrdInfo[ThrdIx] = [Time, Value]
# Adjust
# Canvas
class Frame(gtk.Window):
def __init__(self,canvas):
gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
self.set_title("Thread 2 Demonstration")
self.connect("delete_event" , self.On_Delete)
table = gtk.Table(15, 10, gtk.TRUE)
self.add(table)
for i in xrange(7):
Btn = gtk.Button("Start/Stop")
apply(Btn.set_size_request, BtnSize)
Btn.connect("clicked" , Worker[i].Start_Stop)
table.attach(Btn, 0, 1, 2 * i + 1, 2 * (i + 1) , 0 , 0)
Btn.show()
Btn = gtk.Button("Modus")
apply(Btn.set_size_request, BtnSize)
Btn.connect("clicked" , Worker[i].Modus)
table.attach(Btn, 1, 2, 2 * i + 1, 2 * (i + 1) , 0 , 0)
Btn.show()
# for
table.attach(canvas, 2, 10, 0, 15)
canvas.show()
table.show()
self.show()
# __init__
def On_Delete(self, widget, evt, data=None):
for W in Worker:
W.Quit()
# for
gtk.main_quit()
return gtk.FALSE
# On_Delete
# Frame
gtk.threads_init()
canvas=Canvas()
Win=Frame(canvas)
gtk.main()
_______________________________________________
pygtk mailing list [EMAIL PROTECTED]
http://www.daa.com.au/mailman/listinfo/pygtk
Read the PyGTK FAQ: http://www.async.com.br/faq/pygtk/