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

前端 未结 2 1534
暖寄归人
暖寄归人 2021-02-06 11:51

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 std

相关标签:
2条回答
  • 2021-02-06 12:24

    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.

    0 讨论(0)
  • 2021-02-06 12:38

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

    0 讨论(0)
提交回复
热议问题