On Mon, 2005-29-08 at 13:18 +1000, Simon Gerber wrote:
> Greetings all,
> 
> I'm, err, having threading problems. I've googled through this list,
> and indeed as much of the Internet as I can reach, looking for answers
> and workaround to no avail. I am still just learning, really, and have
> a feeling I've bitten off more than I can chew.
> 
> The situation is this:
> 
> I am trying to create a gnome applet that checks mail accounts,
> displaying the usual stuff.
> 
> The files involved are:
> 
> gnoptray.py - script that launches the applet
> 
> cApplet.py - Module containing the applet class
> 
> cAccounts.py - Module containing an 'Account' class. It is built using
> (not extending) imaplib and poplib. It stores an internal dictionary
> of message headers, can update itself, fetch and delete messages.
> 
> guiAccount.py - GTK extensions to the cAccounts.Account class. Creates
> a GUI that sets account attributes, such as username, password, etc...
> 
> The problem I'm having is with checking mail. I started by creating a
> timeout that would call the update() method of a guiAccount object.
> Obviously, this freezes the entire UI. I don't really want to update
> the GUI while checking... I just want my menus and such to appear
> while it's churning away in the background.
> 
> So I tried threads... Throwing in gtk.threads_init(), wrapping
> gtk.main() in gtk.threads_enter() and gtk.threads_leave() - doing the
> same with my time-out callback.
> 
> e.g
>  ...
>     if interval != 0:
>         self.timeout_ids[name] = (gobject.timeout_add(interval,
> self.check_mail, name))
> 
> def check_mail(self, name):
>         thread1 = threading.Thread(target=self.accounts[name].update())
>         thread1.start()
>         return 1
> 
> 
> But nothing really worked. The UI still froze until checking was
> complete. Is it worth pursuing this path?
> 
> I have tried generators, but they did not work as expected. I am now
> considering writing a daemon application to do the actual mail
> checking - and just feed it into the GUI through an input_add().
> Either that or toss out my cAccounts.py module and build a gtk version
> of imap/poplib using input_add. Any help would be much appreciated.
> 
> Cheers,

To handle threads and inter thread communication You have a couple
options.  There is a dispatcher.py project out there that can handle all
sorts of inter process communications, etc..  We have come up with a
small simple way of handling threads and gtk callbacks.

I've attached a small dispatcher.py script and an example script that
shows how to use it.  This dispatcher code has fixed a lot of bugs and
intermittent problems with our project.  It does a great job of
separating code and callbacks when dealing with threads.

It allows you to run all gtk/gui calls from one thread while other
threads can trigger callbacks and pass data into or out of a thread.

The dispatcher code can reside in any thread and receive calls from any
other thread.  For our project the dispatcher instances all belong to
the main gtk thread and receive signals from feeder threads.  It is
engineered generic enough to be able to pass any python data between
threads.

-- 
Brian <[EMAIL PROTECTED]>
#! /usr/bin/env python
# Fredrik Arnerup <[EMAIL PROTECTED]>, 2004-12-19
# Brian Dolbec<[EMAIL PROTECTED]>,2005-3-30

import gobject, os, Queue
from select import select

class Dispatcher:
    """Send signals from a thread to another thread through a pipe
    in a thread-safe manner"""
    def __init__(self, callback, args=None):
        self.callback = callback
        self.callback_args = args
        self.continue_io_watch = True
        self.queue = Queue.Queue(0) # thread safe queue
        self.pipe_r, self.pipe_w = os.pipe()
        gobject.io_add_watch(self.pipe_r, gobject.IO_IN, self.on_data)
        
    def __call__(self, *args):
        """Emit signal from thread"""
        self.queue.put(args)
        # write to pipe afterwards
        os.write(self.pipe_w, "X")
    
    def on_data(self, source, cb_condition):
        if select([self.pipe_r],[],[], 0)[0] and os.read(self.pipe_r,1):
            if self.callback_args is not None:
                self.callback(self.callback_args, *self.queue.get())
            else:
                self.callback(*self.queue.get())
        return self.continue_io_watch

#! /usr/bin/env python

# Fredrik Arnerup <[EMAIL PROTECTED]>, 2004-12-19
# Brian Dolbec<[EMAIL PROTECTED]>,2005-3-30


from dispatcher import Dispatcher

# ####################################
# dispatcher
# example code:
#
#
# ####################################

class Thread(threading.Thread):

    def __init__(self, dispatcher, thread_num, length):
        threading.Thread.__init__(self)
        self.setDaemon(1)  # quit even if this thread is still running
        self.dispatcher = dispatcher
        self.thread_num = thread_num
        self.sleep_length = length

    def run(self):
        done = False
        print("thread_num = %s; process id = %d ****************" %(self.thread_num,os.getpid()))
        pid_func(self.thread_num)
        for num in range(250):
            #print self.thread_num, " num = ",num
            #sleep(self.sleep_length)
            data = [ self.thread_num, (": time is slipping away: %d\n" %num), num, done]
            self.dispatcher(data) # signal main thread
        done = True
        data = [ self.thread_num, (": Time slipped away: I'm done"), num, done]
        self.dispatcher(data) # signal main thread


def pid_func(threadnum):
    print("pid_func: called from thread_num = %s; process id = %d ****************" %(threadnum,os.getpid()))

def message_fun(buffer, message):
    #print ("got a message : %s" %(message[0] + str(message[1])))
    if message[3]:
        thread_finished[message[0]] = True
        buffer.insert(buffer.get_end_iter(), message[0] + str(message[1]) + "\n\n")
    else:
        #message2 = ("%d x 3 = %d\n" %(message[2],message[2]*3))
        buffer.insert(buffer.get_end_iter(), message[0] + str(message[1])) # + message2)
    return

def timerfunc():
    if (not thread_finished["thread1"]) or (not thread_finished["thread2"]) \
                or (not thread_finished["thread3"]) or (not thread_finished["thread4"]):
        pbar.pulse()
        #print 'Plusing ProgressBar, since a thread is not finished'
        return True
    else:
        pbar.set_fraction(0)
        pbar.set_text("Done")
        return False

def on_window_map_event(event, param):
    print 'Window mapped'
    thread1 = Thread(Dispatcher(message_fun, buffer), "thread1", 0.1)
    thread2 = Thread(Dispatcher(message_fun, buffer), "thread2", 0.1)
    thread3 = Thread(Dispatcher(message_fun, buffer), "thread3", 0.1)
    thread4 = Thread(Dispatcher(message_fun, buffer), "thread4", 0.1)
    gobject.timeout_add(100, timerfunc)
    thread1.start()
    thread2.start()
    thread3.start()
    thread4.start()


if __name__ == "__main__":
    
    import pygtk; pygtk.require("2.0")
    import gtk
    from time import sleep
    
    gtk.threads_init()
    window = gtk.Window(gtk.WINDOW_TOPLEVEL)
    textview = gtk.TextView()
    buffer = textview.get_buffer()
    sw = gtk.ScrolledWindow()
    sw.add(textview)
    pbar = gtk.ProgressBar()
    vbox = gtk.VBox()
    vbox.pack_start(sw)
    vbox.pack_start(pbar, False)
    window.add(vbox)
    #gui_dispatcher = Dispatcher(message_fun, buffer)
    window.connect('map_event', on_window_map_event)
    window.connect("destroy", gtk.main_quit)
    window.resize(400, 600)
    window.show_all()
    thread_finished = {"thread1":False, "thread2":False, "thread3":False, "thread4":False}
    gtk.threads_enter()
    gtk.main()
    gtk.threads_leave()
_______________________________________________
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