I just dealt with this problem.  I have an application that uses lots of
"C" extensions that don't give the Python interpreter a chance to
"time-slice" different threads.   If I had simply had run the CPU
intensive calculations in another thread, the user-interface thread
would not get enough CPU to be responsive.   

The GUI thread sends requests to a 'dispatch' thread (using the 'Queue'
mechanism).  The 'dispatch' thread in turn sends requests to a "server"
task using 'Pyro' (http://www.xs4all.nl/~irmen/python.html)  Pyro lets
you call methods in a remote Python task just as if the class was part
of your task.   It functions as a Python<->Python remote procedure call
mechanism.

Even though the computations are being done in a separate task, it is
important to be able to see status messages from the server task.

I captured the output of the remote task by spawning it using
Process2.py, and then used 'input_add' to read the "server's" output. 
e.g.:

from Process2 import Process

proc = Process('dq_server', ('->', 1) )
proc.bg()

self.__inputf = input_add( os.fdopen(proc.fd[1],'r'),
                                  GDK.INPUT_READ,
self.__readServerOutput)

def __readServerOutput(self, fp, condition):
        line = fp.readline()
        threads_enter()  # subtle - to avoid deadlock,
                         #threads_enter() can't proceed readline() 
        # cleanup on EOF - avoid infinite stream of callbacks
        if line == "":
            input_remove(self.__inputf)
            fp.close()
            print 'Server closed\n'
        else:
            print ' -> %s' % line[0:-1]
        threads_leave()


 I also redirected standard output to appear in a widget, using code
adapted from 

        pygtk/examples/ide/gtkcons.py
 
This allowed me to view both the server's output and the GUI's task
output in a scrolling log window.  I've attached my 'MessageWidget' that
implements the scrolling log widget with output redirection.




-- 
Joe VanAndel              
National Center for Atmospheric Research
http://www.atd.ucar.edu/~vanandel/
Internet: [EMAIL PROTECTED]
"""
 MessageWidget:
   display a scrolled window of messages in a pygtk GtkText widget in a
   scrolled frame
   
"""
import sys
from gnome.ui  import *
from gtk import * 
from gnome.config import *
import libglade
__id__ = "$Id: $"
__version__ = "$Revision:  $"

# from pygtk/examples/ide/gtkcons.py
class gtkoutfile:
        '''A fake output file object.  It sends output to a gtk text widget,
        and if asked for a file number, returns one set on instance creation'''
        def __init__(self, w, fn, font):
                self.__fn = fn
                self.__w = w
                self.__font = font
        def close(self): pass
        flush = close
        def fileno(self):    return self.__fn
        def isatty(self):    return 0
        def read(self, a):   return ''
        def readline(self):  return ''
        def readlines(self): return []
        def write(self, s):
                #stdout.write(str(self.__w.get_point()) + '\n')
                self.__w.freeze()
                self.__w.insert(self.__font, self.__w.fg,
                                self.__w.bg, s)
                # force a scroll to bottom
                vadj = self.__w.get_vadjustment()
##                sys.stderr.write('lower: %d upper: %d value %d\n' % \
##                                 (vadj.lower, vadj.upper,vadj.value))
                vadj.set_value(vadj.upper)
                self.__w.thaw()
                self.__w.queue_draw()
        def writelines(self, l):
                self.__w.freeze()
                for s in l: self.__w.insert(self.__font,
                                            self.__w.fg, self.__w.bg, s)
                vadj = self.__w.get_vadjustment()
                vadj.set_value(vadj.upper)
                self.__w.thaw()
                self.__w.queue_draw()
        def seek(self, a):   raise IOError, (29, 'Illegal seek')
        def tell(self):      raise IOError, (29, 'Illegal seek')
        truncate = tell

class MessageWidget:
    def __init__(self, text_w,  wordWrap =TRUE):
        self.debug = FALSE
        print 'MessageWidget:__init__'
        self.text = text_w
        self.text.style = self.text.get_style()
        self.text.fg = self.text.style.fg[STATE_NORMAL]
        self.text.bg = self.text.style.white
        #self.text.bg = self.text.style.bg[STATE_NORMAL]

        self.text.set_word_wrap(wordWrap)
        self.text.set_usize(500, 400)
        self.normal = load_font(
                        "-*-helvetica-medium-r-normal-*-*-100-*-*-*-*-*-*")
        self.error = load_font(
                        "-*-helvetica-medium-o-normal-*-12-100-*-*-*-*-*-*")
        # set up hooks for standard output.
        self.stdout = gtkoutfile(self.text, sys.stdout.fileno(),
                                        self.normal)
        self.stderr = gtkoutfile(self.text, sys.stderr.fileno(),
                                        self.error)

    def init(self, debug=0):
        self.debug = debug
        self.text.realize()
#        print 'MessageWidget:text realized '
        if not self.debug:
            sys.stdout, self.stdout = self.stdout, sys.stdout
            #sys.stderr, self.stderr = self.stderr, sys.stderr
        else:
            print 'debug mode : not grabbing standard output'

    def restore(self):
        if not self.debug:
            sys.stdout, self.stdout = self.stdout, sys.stdout
#        sys.stderr, self.stderr = self.stderr, sys.stderr
        return

Reply via email to