Я нахожусь в процессе создания приложения на основе графического интерфейса пользователя с Python / Tkinter, которое строится поверх существующего модуля Python bdb. В этом приложении я хочу отключить весь stdout / stderr с консоли и перенаправить его в свой графический интерфейс. Для этого я написал специальный объект Tkinter.Text (код в конце сообщения).
Основная идея состоит в том, что когда что-то записывается в sys.stdout, это отображается как строка в «Тексте» с черным цветом. Если что-то записано в sys.stderr, это отображается в виде строки в «Тексте» красного цвета. Как только что-то написано, текст всегда прокручивается вниз, чтобы просмотреть самую последнюю строку.
Сейчас я использую Python 2.6.1. В Mac OS X 10.5 это, кажется, отлично работает. У меня не было проблем с этим. Однако в RedHat Enterprise Linux 5 я довольно надежно получаю ошибку сегментации во время выполнения сценария. Ошибка сегментации не всегда возникает в одном и том же месте, но почти всегда. Если я закомментирую строки sys.stdout=
и sys.stderr=
из своего кода, ошибки сегментации, похоже, исчезнут.
Я уверен, что есть другие способы обойти это, к которым мне, вероятно, придется прибегнуть, но может ли кто-нибудь увидеть что-нибудь, что я делаю явно неправильно, что могло бы вызвать эти ошибки сегментации? Это сводит меня с ума. Спасибо!
PS - Я понимаю, что перенаправление sys.stderr в графический интерфейс может быть не очень хорошей идеей, но я все равно получаю ошибки сегментации, даже когда я перенаправляю только sys.stdout, а не sys.stderr. Я также понимаю, что в данный момент я позволяю Тексту расти бесконечно.
class ConsoleText(tk.Text):
'''A Tkinter Text widget that provides a scrolling display of console
stderr and stdout.'''
class IORedirector(object):
'''A general class for redirecting I/O to this Text widget.'''
def __init__(self,text_area):
self.text_area = text_area
class StdoutRedirector(IORedirector):
'''A class for redirecting stdout to this Text widget.'''
def write(self,str):
self.text_area.write(str,False)
class StderrRedirector(IORedirector):
'''A class for redirecting stderr to this Text widget.'''
def write(self,str):
self.text_area.write(str,True)
def __init__(self, master=None, cnf={}, **kw):
'''See the __init__ for Tkinter.Text for most of this stuff.'''
tk.Text.__init__(self, master, cnf, **kw)
self.started = False
self.write_lock = threading.Lock()
self.tag_configure('STDOUT',background='white',foreground='black')
self.tag_configure('STDERR',background='white',foreground='red')
self.config(state=tk.DISABLED)
def start(self):
if self.started:
return
self.started = True
self.original_stdout = sys.stdout
self.original_stderr = sys.stderr
stdout_redirector = ConsoleText.StdoutRedirector(self)
stderr_redirector = ConsoleText.StderrRedirector(self)
sys.stdout = stdout_redirector
sys.stderr = stderr_redirector
def stop(self):
if not self.started:
return
self.started = False
sys.stdout = self.original_stdout
sys.stderr = self.original_stderr
def write(self,val,is_stderr=False):
#Fun Fact: The way Tkinter Text objects work is that if they're disabled,
#you can't write into them AT ALL (via the GUI or programatically). Since we want them
#disabled for the user, we have to set them to NORMAL (a.k.a. ENABLED), write to them,
#then set their state back to DISABLED.
self.write_lock.acquire()
self.config(state=tk.NORMAL)
self.insert('end',val,'STDERR' if is_stderr else 'STDOUT')
self.see('end')
self.config(state=tk.DISABLED)
self.write_lock.release()