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/

Reply via email to