开发者

pygtk console output for debugging

I have this problem. I'm creating an editor and I need console output(sys.stderr and sys.stdout) outputted to a textview. The problem is that when I start the console, it waits for it to quit, but I want it to catch anything and output it to the textview, so I thought that you might need different threads, but does it not then make it impossible to catch anything from the other thread? I want this in case the editor was not started from a terminal. It will be used as a module if you are wondering. This is the code so far:

import sys
import gtk
import pygtk
pygtk.require('2.0')

class Console:
    def __init__(self):
        tv = gtk.TextView()
        tv.set_editable(False)
        tv.set_wrap_mode(gtk.WRAP_WORD)
        self.buffer = tv.get_buffer()

        table = gtk.Table(3, 6, gtk.FALSE)
        table.attach(tv, 0, 6, 0, 1)

        #### Main window
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.connect('destroy_event', lambda w, e: gtk.mainquit())
        self.window.connect('delete_event', lambda w, e: gtk.mainquit())
        self.window.set_border_width(10)
        self.window.add(table)
        self.window.set_title('Search')
        self.window.set_default_size(300, 300)
        self.window.show_all()

    def main(self):
        gtk.main()

c = Console()

class ConsoleOutput:
    def __init__(self, source):
        self.source=source
        self.buf = []

    def write(self, data):
        self.buf.append(data)
        if data.endswith('\n'):
          c.buffer.insert(c.buffer.get_end_iter(), ''.join(self.buf))
          self.buf = []

    def __del__(self):
        if self.buf != []:
          c.buffer.i开发者_运维百科nsert(c.buffer.get_end_iter(), ''.join(self.buf))

Thank you.


Since you want to catch sys.stdout and sys.stderr and these are global to the interpreter according to the documentation, you should be able to catch them regardless of the thread where the output was done.

On the other hand, your code wasn't very far from working. It was missing a call to the event loop, which is why it freezed. I added a call to c.main().

I also added a button which prints "hello" and I replaced the default sys.stdout by an instance of ConsoleOutput, so "hello" should appear on the textview.

import sys
import gtk
import pygtk
pygtk.require('2.0')
import gobject

import threading

class MyThread(threading.Thread):
    def __init__(self, name):
        threading.Thread.__init__(self)
        self.name = name
    def run(self):
        for i in range(10):
            print "Hello %d from thread %s" % (i, self.name)

class Console:
    def __init__(self):
        tv = gtk.TextView()
        tv.set_editable(False)
        tv.set_wrap_mode(gtk.WRAP_WORD)
        self.buffer = tv.get_buffer()

        button = gtk.Button("Update")
        button.connect("clicked", self.update, None)

        table = gtk.Table(3, 6, gtk.FALSE)
        table.attach(tv, 0, 6, 0, 1)
        table.attach(button, 0, 6, 1, 2)

        #### Main window
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.connect('destroy_event', lambda w, e: gtk.mainquit())
        self.window.connect('delete_event', lambda w, e: gtk.mainquit())
        self.window.set_border_width(10)
        self.window.add(table)
        self.window.set_title('Search')
        self.window.set_default_size(300, 300)
        self.window.show_all()

    def update(self, widget, data=None):
        print "hello"
        MyThread("A").start()
        MyThread("B").start()

    def main(self):
        gtk.main()

c = Console()

class ConsoleOutput:
    def __init__(self, source):
        self.source=source
        self.buf = []

    def update_buffer(self):
        c.buffer.insert(c.buffer.get_end_iter(), ''.join(self.buf))
        self.buf = []

    def write(self, data):
        self.buf.append(data)
        if data.endswith('\n'):
            gobject.idle_add(self.update_buffer)

    def __del__(self):
        if self.buf != []:
            gobject.idle_add(self.update_buffer)

sys.stdout = ConsoleOutput(None)
c.main()

Edit: I've updated my answer to include an example of threads. Two threads are created when pressing the button. I used python's threading module. I think pygtk has its own threading facilities, it's just that python's module came up first when searching Google.

The important point to be made is in the ConsoleOutput class. Note that I have wrapped the code that updates the console buffer in a method called self.update_buffer, which is called indirectly through gobject.idle_add. What this function does is to call self.update_buffer within the gtk event loop. It must be done this way because all calls that update the GUI must be done within the event loop, otherwise Gtk can't synchronize access to its data structures and you may get strange behaviours and crashes.

There may be some buffering issues that prevent the output from appearing at once in the textview.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜