#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. Я случайно пропустил некоторый текст, так как я вставил его в другом месте. Дело в том, что я не знаю, как использовать потоки в этом случае. Я просмотрел несколько примеров, но не могу разобраться в этом. Я был бы признателен, если бы вы включили это в свой пример :).