I decided to write a program similar to pygtk-test for pygtk2.
It's based upon old work:
http://www.daa.com.au/pipermail/pygtk/2001-August/001561.html
but it integrates much better with the standard python console, actually
it subclass the appropriate classes from code.py so it should more or
less be identical. 
There is a difference though, the old pygtk-test used threads, but this
doesn't. Instead i'm using two processes, one for input and one for
gtk's mainloop. Communication between them is handled by a pipe and two
signals.

I figured that this might be a good candidate for inclusion in the
official tree if everybody likes it and if it's found that it has no
known bugs :)

What do you think James?

-- 
Johan Dahlin <[EMAIL PROTECTED]>
Async Open Source
#!/usr/bin/python
# -*- Mode: python; c-basic-offset: 4 -*-
#
# Interactive PyGtk Console, Johan Dahlin 2002
#

import os
import signal
import sys
from code import InteractiveInterpreter, InteractiveConsole

import gtk
from gtk import TRUE, FALSE

# For compatibility, instead of using GDK.INPUT_READ or
# gtk.gdk.INPUT_READ depending on the PyGtk version
GDK_INPUT_READ = 1

class Mainloop(InteractiveInterpreter):
    def __init__(self, read_fd, pid):
        InteractiveInterpreter.__init__(self)
        self._rfd = os.fdopen(read_fd, 'r')
        self.pid = pid
        
        gtk.input_add(read_fd, GDK_INPUT_READ, self.input_func)

    def read_message(self):
        length = ord(self._rfd.read(1))
        return self._rfd.read(length)

    def input_func(self, fd, cond):
        data = self.read_message()
        more = self.runsource(data)
        if more:
            signal_id = signal.SIGUSR1
        else:
            signal_id = signal.SIGUSR2
            
        # Now when we're done, notify the console (parent process)
        os.kill(self.pid, signal_id)
        return TRUE

    def run(self):
        gtk.mainloop()
        
class Console(InteractiveConsole):
    def __init__(self, write_fd, pid):
        InteractiveConsole.__init__(self)
        self._wfd = os.fdopen(write_fd, 'w')
        self.pid = pid
        
        # Listen on SIGUSR1, SIGUSR2
        signal.signal(signal.SIGUSR1, self.signal_handler)
        signal.signal(signal.SIGUSR2, self.signal_handler)

    def signal_handler(self, sig_id, frame):
        # We receive different signals depend on if
        # we have more data in the buffer to interpret
        if sig_id == signal.SIGUSR1:
            self.more = 1
        elif sig_id == signal.SIGUSR2:
            self.more = 0
    
    def send_message(self, message):
        self._wfd.write('%c%s' % (len(message), message))
        self._wfd.flush()

    def interact(self, banner=None):
        InteractiveConsole.interact(self, banner)
        # Die parent die
        os.kill(self.pid, 9)
        
    def runsource(self, source, filename):
        self.send_message(source)
        # wait for notification from parent
        signal.pause()

        return self.more
        
class GtkInterpreter(Console):
    def __init__(self):
        rfd, wfd = os.pipe()

        parent_pid = os.getpid()
        child_pid = os.fork()
        if not child_pid:
            g = Mainloop(rfd, parent_pid)
            g.run()
        else:
            Console.__init__(self, wfd, child_pid)

def interact():
    try:
        import readline
        import rlcompleter
        readline.parse_and_bind('tab: complete')
    except ImportError:
        pass
    
    gi = GtkInterpreter()
    gi.push("from gtk import *")

    python_version = sys.version.split(' ')[0]
    pygtk_version = '.'.join(map(str, gtk.pygtk_version))
    gtk_version = '.'.join(map(str, gtk.gtk_version))
    
    banner = """Python %s, PyGTK %s (Gtk+ %s)
Interactive console that from where you can manipulate GTK+ widgets
interactively, without blocking on the mainloop.""" % (python_version,
       pygtk_version,
       gtk_version)
    gi.interact(banner)
    
if __name__ == '__main__':
    interact()

Reply via email to