вывод на консоль pygtk для отладки

#python #windows #linux #gtk #pygtk

#python #Windows #linux #gtk ( гтк ) #pygtk #gtk

Вопрос:

У меня есть эта проблема. Я создаю редактор, и мне нужен консольный вывод (sys.stderr и sys.stdout), выводимый в textview. Проблема в том, что когда я запускаю консоль, она ожидает завершения работы, но я хочу, чтобы она что-нибудь перехватывала и выводила в 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:

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

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

Я также добавил кнопку, которая выводит «привет», и я заменил значение по умолчанию sys.stdout экземпляром ConsoleOutput , поэтому «привет» должен появиться в 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 имеет свои собственные средства обработки потоков, просто модуль python появился первым при поиске в Google.

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

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

Комментарии:

1. Я случайно пропустил некоторый текст, так как я вставил его в другом месте. Дело в том, что я не знаю, как использовать потоки в этом случае. Я просмотрел несколько примеров, но не могу разобраться в этом. Я был бы признателен, если бы вы включили это в свой пример :).