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.
精彩评论