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.

All rigt here below is my first version of a multi-threaded pygtk
program. In this version I use gtk.threads_enter()/gtk.threads_leave
pairs when I want to draw something to the screen from within
a subthread.

Personnaly I would recommed against this method except if
your subthreads are very intensive in updating the screen,
the logic of your subthread is not to complcated and you
really need the speed.

The reason is the pygtk really can get in your way with
how it handles things. Look for instance to the Counting
class. There are two methods that will adjust the screen,
run and Modus. Now in run we use the threads_enter/threads_leave
pair but we don't in Modus. Why? Well because although Modus
belongs to the Thread object it really gets called by the
main thread through a signal handler and callback that are
connected are implicitly squeezed between a threads_enter/threads_leave
pair when called.

That is also the reason why the On_Delete method of Frame starts
with a threads_leave() call. Your sunthreads are likely to be
blocked by the threads_enter() call in run. So just Quitting
and joining the subthreads in On_Delete will very likely cause
a deadlock if you didn't call threads_leave() first.

So lots of things to look out for if you try it this way.

--------------------- demo1.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


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.threads_enter()
        canvas.Adjust (self.Id, self.ShowTime , self.ShowValue)
        gdk.flush()
        gtk.threads_leave()
      # 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
      canvas.Adjust(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, Results):
      pass
    # 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 1 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):

    gtk.threads_leave()
    for W in Worker:
      W.Quit()
      W.join()
    # for
    gtk.threads_enter()
    gtk.main_quit()
    return gtk.FALSE


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