2011-10-11 90 views
1

我有這個問題。我正在創建一個編輯器,我需要輸出到一個textview的控制檯輸出(sys.stderr和sys.stdout)。問題是,當我啓動控制檯時,它等待它退出,但我希望它捕捉任何內容並將其輸出到textview,所以我認爲你可能需要不同的線程,但是它不會使它無法從其他線程捕捉任何東西?如果編輯器沒有從終端啓動,我希望這樣做。如果你想知道它將被用作模塊。這是目前爲止的代碼:pygtk控制檯輸出用於調試

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)) 

謝謝。

回答

2

既然你想趕上sys.stdoutsys.stderr,並且這些對於解釋器來說是全局性的,你應該能夠捕獲它們,而不管輸出是在哪個線程完成的。

另一方面,你的代碼並不是很不起作用。它錯過了對事件循環的調用,這就是它凍結的原因。我加了一個電話給c.main()

我還添加了一個打印「hello」的按鈕,我用ConsoleOutput的實例替換了默認的sys.stdout,所以「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無法同步對其數據結構的訪問,並且可能會出現奇怪的行爲和崩潰。

可能會有一些緩衝問題阻止輸出在文本視圖中一次出現。

+0

我意外地省略了一些文字,因爲我將它合併到其他地方。問題是我不知道如何在這種情況下使用線程。我已經看過一些例子,但是我無法用頭圍住它。如果你將這個包含在你的例子中,我將不勝感激:)。 – thabubble

+0

@thabubble我已經更新了答案。 –