вывод консоли Pygtk для отладки - PullRequest
1 голос
/ 11 октября 2011

У меня есть эта проблема.Я создаю редактор, и мне нужен вывод консоли (sys.stderr и sys.stdout) в текстовое представление.Проблема в том, что когда я запускаю консоль, она ждет, когда она закроется, но я хочу, чтобы она что-то перехватила и вывела в текстовое представление, поэтому я подумала, что вам могут понадобиться другие потоки, но не делает ли это невозможнымпоймать что-нибудь из другого потока?Я хочу это в случае, если редактор не был запущен из терминала.Он будет использоваться в качестве модуля, если вам интересно.Это код до сих пор:

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 Ответ

2 голосов
/ 12 октября 2011

Поскольку вы хотите перехватить sys.stdout и sys.stderr, и они являются глобальными для интерпретатора в соответствии с документацией, вы должны быть в состоянии перехватить их независимо от потока, в котором был сделан вывод.

С другой стороны, ваш код не очень далек от работы. Отсутствовал вызов в цикле событий, поэтому он завис. Я добавил звонок на c.main().

Я также добавил кнопку, которая печатает «привет», и заменил значение по умолчанию sys.stdout на экземпляр ConsoleOutput, поэтому «приветствие» должно появиться в текстовом представлении.

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 есть свои средства для работы с потоками, просто модуль python появился первым при поиске в Google.

Важный момент, который необходимо сделать, относится к классу ConsoleOutput. Обратите внимание, что я обернул код, который обновляет буфер консоли, в метод с именем self.update_buffer, который вызывается косвенно через gobject.idle_add. Эта функция вызывает self.update_buffer в цикле событий gtk . Это должно быть сделано так, потому что все вызовы, которые обновляют GUI, должны выполняться в цикле событий , в противном случае Gtk не может синхронизировать доступ к своим структурам данных, и вы можете получить странное поведение и сбои.

Могут быть некоторые проблемы с буферизацией, которые не позволяют выводу сразу появиться в текстовом представлении.

...