what is the best practice to separate Pygtk and long running thread code

seb sebastien.thur at laposte.net
Fri Sep 22 04:07:41 EDT 2006


Hi,

I am using pygtk for the first times.

I am wondering what would be the best "pattern" to interface pygtk with
a thread.

The thread is collecting informations (over the network for example) or
is doing some long calculations.

I would like also to separate the gui part to the action part so that I
should be easier to maintain.

************************************************************
What should be the best practice in order to achieve this ?
***********************************************************

What I use now is :



1)
the gui is launching the "action" evrey 1 sec and is checking for
message using a queue every second also.

gui.py

....etc ...
class window1(SimpleGladeApp):
#@-- class window1 }
    #@-- init window1.__init__ {
    def __init__(self, path='gui.glade',
                 root='window1',
                 domain=app_name, kwargs={}):
        path = os.path.join(glade_dir, path)
        SimpleGladeApp.__init__(self, path, root, domain, **kwargs)
	self.q=Queue.Queue()
	self.action=act.action(self.q)
        gobject.timeout_add (1000,self.action.go) # this is the action
asked
	gobject.timeout_add (1000,self.process) # check if a new message is
available in the queue

    def process (self):
	dir (self.q)
        if self.q.empty() == False :
        	print "from main ",self.q.get()
	return True

 ...etc ....

2)
The action part is making somehow the interface between the running
thread and the gui :
It puts the new information in the queue for the gui to process it
later.


action.py

import os
import gui
import time
import Queue
import Spethread # thread that always run in the background

class Singleton(object):
    _instance = None
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(Singleton, cls).__new__(cls, *args,
**kwargs)
        return cls._instance


class action(Singleton):
    def __init__(self, queue):
        self.q=queue
	self.thread_seb=Spethread.worker()
	self.thread_seb.start()
	self.go()

    def go(self):
        if self.thread_seb:
            reponse=self.thread_seb.status()
	    self.q.put(reponse)
	else :
            self.q.put("le thread n'existe plus ")
        return True

    def stop(self):
        self.thread_seb.die()


3)
The thread part is "just" a thread that should from time to time
time.Sleep in order for action to query if it has some new messages.

Spethread.py

import threading
import time

class worker(threading.Thread):

	def __init__(self):
		threading.Thread.__init__(self)
		self.value=0
		self.go_on=1

	def run(self):
		print "self.go_on=",self.go_on
		while self.go_on == 1:
			self.value=int(time.time())
			res=2
			for i in range(0, 100):
				res=res^i
				print res
			time.Sleep(0.01)
	def status(self):
		return self.value


	def die(self):
		print "die request"
		self.go_on=0


Thanks in advance for sharing this informations.
Sebastien.

PS :
the entire gui.py

gH#@-- python gui.py {
#@-- header gui.py {
#!/usr/bin/env python
# -*- coding: UTF8 -*-

# Python module gui.py
# Autogenerated from gui.glade
# Generated on Wed Sep 20 22:03:00 2006

# Warning: Do not modify any context comment beginning with # @--
# They are required to keep user's code
# Doing so will make it unable for kefir to help you further manage
your project

#@-- header gui.py }
#@-- app gui {
import os
import gobject
import gtk
import action as act
import Queue

from SimpleGladeApp import SimpleGladeApp, bindtextdomain

app_name = 'gui'
app_version = '0.0.1'

glade_dir = ''
locale_dir = ''

bindtextdomain(app_name, locale_dir)

#@-- app gui }
#@-- window window1 {
#@-- class window1 {
class window1(SimpleGladeApp):
#@-- class window1 }
    #@-- init window1.__init__ {
    def __init__(self, path='gui.glade',
                 root='window1',
                 domain=app_name, kwargs={}):
        path = os.path.join(glade_dir, path)
        SimpleGladeApp.__init__(self, path, root, domain, **kwargs)
	self.q=Queue.Queue()
	self.action=act.action(self.q)
        gobject.timeout_add (1000,self.action.go)
	gobject.timeout_add (1000,self.process)
    #@-- init window1.__init__ }
    #@-- new window1.new {
    def new(self):
        print 'A new %s has been created' % self.__class__.__name__
    #@-- new window1.new }
    #@-- custom window1 {
    #   Write your own methods here
    #@-- custom window1 }
    #@-- callback window1.on_button1_clicked {
    def on_button1_clicked(self, widget, args=[]):
        print 'on_button1_clicked called with self.%s' %
widget.get_name()
        self.action.stop()
        print "arrete de thread"
    #@-- callback window1.on_button1_clicked }
    def process (self):
	dir (self.q)
        if self.q.empty() == False :
        	print "from main ",self.q.get()
	return True

#@-- window window1 }
#@-- main gui.py {
#@-- init main {
def main():
#@-- init main }
#@-- body main {
    Window1 = window1()
    Window1.run()
#@-- body main }
#@-- main gui.py }
#@-- run gui.py {
if __name__ == '__main__':
    main()
#@-- run gui.py }
#@-- python gui.py }



the glade file:


<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM
"http://glade.gnome.org/glade-2.0.dtd">

<glade-interface>

<widget class="GtkWindow" id="window1">
  <property name="visible">True</property>
  <property name="title" translatable="yes">window1</property>
  <property name="type">GTK_WINDOW_TOPLEVEL</property>
  <property name="window_position">GTK_WIN_POS_NONE</property>
  <property name="modal">False</property>
  <property name="resizable">True</property>
  <property name="destroy_with_parent">False</property>
  <property name="decorated">True</property>
  <property name="skip_taskbar_hint">False</property>
  <property name="skip_pager_hint">False</property>
  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
  <property name="focus_on_map">True</property>
  <property name="urgency_hint">False</property>

  <child>
    <widget class="GtkVBox" id="vbox1">
      <property name="visible">True</property>
      <property name="homogeneous">False</property>
      <property name="spacing">0</property>

      <child>
	<widget class="GtkEntry" id="entry1">
	  <property name="visible">True</property>
	  <property name="can_focus">True</property>
	  <property name="editable">True</property>
	  <property name="visibility">True</property>
	  <property name="max_length">0</property>
	  <property name="text" translatable="yes">this is it</property>
	  <property name="has_frame">True</property>
	  <property name="invisible_char">*</property>
	  <property name="activates_default">False</property>
	</widget>
	<packing>
	  <property name="padding">0</property>
	  <property name="expand">True</property>
	  <property name="fill">True</property>
	</packing>
      </child>

      <child>
	<widget class="GtkButton" id="button1">
	  <property name="visible">True</property>
	  <property name="can_focus">True</property>
	  <property name="label" translatable="yes">button1</property>
	  <property name="use_underline">True</property>
	  <property name="relief">GTK_RELIEF_NORMAL</property>
	  <property name="focus_on_click">True</property>
	  <signal name="clicked" handler="on_button1_clicked"
last_modification_time="Wed, 20 Sep 2006 19:59:06 GMT"/>
	</widget>
	<packing>
	  <property name="padding">0</property>
	  <property name="expand">False</property>
	  <property name="fill">False</property>
	  <property name="pack_type">GTK_PACK_END</property>
	</packing>
      </child>
    </widget>
  </child>
</widget>

</glade-interface>




More information about the Python-list mailing list