Segmentation fault while redirecting sys.stdout to Tkinter.Text widget

£可爱£侵袭症+ 提交于 2019-12-03 09:06:12

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

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

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.

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.

I'm assuming this is part of a larger, threaded program.

Instead of using a lock, have your code write to a thread-safe queue object. Then, in your main thread you poll the queue and write to the text widget. You can do the polling using the event loop (versus writing your own loop) by running the polling job which reschedules itself to run a few ms later using after (a couple hundred ms is probably quite sufficient).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!