用于调试的pygtk控制台输出

时间:2011-10-11 13:40:28

标签: python windows linux gtk pygtk

我有这个问题。我正在创建一个编辑器,我需要输出到textview的控制台输出(sys.stderr和sys.stdout)。问题是,当我启动控制台时,它等待它退出,但是我希望它能抓住任何东西并将其输出到textview,所以我认为你可能需要不同的线程,但它不会让它变得不可能从另一个线程中捕获任何东西?如果编辑器没有从终端启动,我想要这个。如果您想知道它将用作模块。这是到目前为止的代码:

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.insert(c.buffer.get_end_iter(), ''.join(self.buf))

谢谢。

1 个答案:

答案 0 :(得分:2)

由于您希望捕获sys.stdoutsys.stderr并且这些对于解释器来说是全局的,因此无论输出完成的线程如何,您都应该能够捕获它们。

另一方面,你的代码离工作还不远。它缺少对事件循环的调用,这就是它冻结的原因。我添加了对c.main()的调用。

我还添加了一个打印“hello”的按钮,我用sys.stdout的实例替换了默认的ConsoleOutput,因此“hello”应该出现在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()

编辑:我已经更新了我的答案以包含一个线程示例。按下按钮时会创建两个线程。我使用了python的线程模块。我认为pygtk有自己的线程设施,只是在搜索Google时首先出现了python的模块。

重要的一点是ConsoleOutput课程。请注意,我已经在名为self.update_buffer的方法中包装了更新控制台缓冲区的代码,该方法通过gobject.idle_add间接调用。此函数的作用是在gtk事件循环中调用self.update_buffer 。必须以这种方式完成,因为所有更新GUI的调用必须在事件循环中完成,否则Gtk无法同步对其数据结构的访问,您可能会遇到奇怪的行为和崩溃。

可能存在一些阻止输出在文本视图中一次出现的缓冲问题。