将 sys.stdout 重定向到 Tkinter.Text 小部件时出现分段错误 [英] Segmentation fault while redirecting sys.stdout to Tkinter.Text widget

查看:52
本文介绍了将 sys.stdout 重定向到 Tkinter.Text 小部件时出现分段错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 Python/Tkinter 构建基于 GUI 的应用程序,该应用程序构建在现有 Python bdb 模块之上.在此应用程序中,我想将控制台中的所有 stdout/stderr 静音并将其重定向到我的 GUI.为了实现这个目的,我编写了一个专门的 Tkinter.Text 对象(文章末尾的代码).

I'm in the process of building a GUI-based application with Python/Tkinter that builds on top of the existing Python bdb module. In this application, I want to silence all stdout/stderr from the console and redirect it to my GUI. To accomplish this purpose, I've written a specialized Tkinter.Text object (code at the end of the post).

基本思想是,当某些内容写入 sys.stdout 时,它会在文本"中显示为黑色的一行.如果将某些内容写入 sys.stderr,它会在文本"中显示为红色的一行.写入内容后,文本始终向下滚动以查看最近的一行.

The basic idea is that when something is written to sys.stdout, it shows up as a line in the "Text" with the color black. If something is written to sys.stderr, it shows up as a line in the "Text" with the color red. As soon as something is written, the Text always scrolls down to view the most recent line.

我目前使用的是 Python 2.6.1.在 Mac OS X 10.5 上,这似乎很好用.我的问题为零.但是,在 RedHat Enterprise Linux 5 上,我非常可靠地在脚本运行期间遇到了分段错误.分段错误并不总是发生在同一个地方,但它几乎总是发生.如果我从代码中注释掉 sys.stdout=sys.stderr= 行,分段错误似乎消失了.

I'm using Python 2.6.1 at the moment. On Mac OS X 10.5, this seems to work great. I have had zero problems with it. On RedHat Enterprise Linux 5, however, I pretty reliably get a segmentation fault during the run of a script. The segmentation fault doesn't always occur in the same place, but it pretty much always occurs. If I comment out the sys.stdout= and sys.stderr= lines from my code, the segmentation faults seem to go away.

我确信还有其他方法可以解决这个问题,我可能不得不求助于它,但是有人能看到我在这里做的任何可能导致这些分段错误的明显错误吗?它让我发疯.谢谢!

I'm sure there are other ways around this that I will probably have to resort to, but can anyone see anything I'm doing blatantly wrong here that could be causing these segmentation faults? It's driving me nuts. Thanks!

PS - 我意识到将 sys.stderr 重定向到 GUI 可能不是一个好主意,但即使我只重定向 sys.stdout 而不是 sys.stderr,我仍然会遇到分段错误.我也意识到我现在允许 Text 无限增长.

PS - I realize redirecting sys.stderr to the GUI might not be a great idea, but I still get segmentation faults even when I only redirect sys.stdout and not sys.stderr. I also realize that I'm allowing the Text to grow indefinitely at the moment.

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()

推荐答案

好的,我已经设法找到了问题所在.我无法在最初开发代码的 Mac OS X 10.5.8 上重现这个问题.分段错误似乎只发生在 RedHat Enterprise Linux 5 上.

Alright, so I've managed to track down the problem. I was never able to recreate this problem on Mac OS X 10.5.8 where I originally developed the code. The segmentation faults only seem to occur on RedHat Enterprise Linux 5.

事实证明,这段代码是罪魁祸首:

It turns out that this piece of code is the culprit:

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()

我希望我能解释一下为什么会发生分段错误,但我发现不断启用和禁用 Text 对象是罪魁祸首.如果我把上面的代码改成这样:

I wish I had an explanation for why the segmentation faults are occurring, but I've found that constantly enabling and disabling the Text object is the culprit. If I change the above piece of code to this:

def write(self,val,is_stderr=False):

        self.write_lock.acquire()

        self.insert('end',val,'STDERR' if is_stderr else 'STDOUT')
        self.see('end')

        self.write_lock.release()

当我删除 self.config(state=...) 调用时,我的分段错误消失了.self.config(state=...) 调用的全部目的是使用户无法编辑文本字段.当 Text 字段处于 tk.DISABLED 状态时,对 self.insert(...) 的调用也不起作用.

My segmentation faults go away when I remove the self.config(state=...) calls. The whole point of the self.config(state=...) calls was to make it so the user could not edit the Text field. When the Text field is in tk.DISABLED state though, calls to self.insert(...) do not work either.

我想出的解决方法是让 Text 字段保持启用状态,但导致 Text 字段忽略所有键盘输入(因此如果用户尝试使用键盘,则会产生只读行为的错觉).最简单的方法是将 __init__ 方法更改为如下所示(将状态更改为 tk.NORMAL 并更改 的绑定; 事件):

The workaround solution I've come up with is to leave the Text field enabled, but cause the Text field to ignore all keyboard input (thus giving the illusion of read-only behavior if the user attempts to use the keyboard). The easiest way to do this is to change the __init__ method to look like this (change the state to tk.NORMAL and change the binding for <Key> events):

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.NORMAL)
        self.bind('<Key>',lambda e: 'break') #ignore all key presses

希望对遇到同样问题的人有所帮助.

Hope that helps anyone who runs into the same problem.

这篇关于将 sys.stdout 重定向到 Tkinter.Text 小部件时出现分段错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆