Отказ сегментации при перенаправлении sys.stdout к Tkinter. Текстовый виджет

Я нахожусь в процессе создавания основанного на GUI приложения с Python/Tkinter, который создает сверху существующего Python bdb модуль. В этом приложении я хочу заставить весь stdout/stderr замолчать от консоли и перенаправить его к моему GUI. Для выполнения этой цели записал я специализированный Tkinter. Текстовый объект (кодируют в конце сообщения).

Основная идея состоит в том, что, когда что-то записано в sys.stdout, он обнаруживается как строка в "тексте" с черным цветом. Если что-то записано в sys.stderr, он обнаруживается как строка в "тексте" с красным цветом. Как только что-то записано, текст всегда прокручивает вниз для просмотра новой строки.

Я использую Python 2.6.1 в данный момент. На Mac OS X 10.5, это, кажется, работает отлично. У меня были нулевые проблемы с ним. На RedHat Enterprise Linux 5, однако, я довольно надежно получаю отказ сегментации во время выполнения сценария. Отказ сегментации не всегда происходит в том же месте, но он в значительной степени всегда происходит. Если я комментирую sys.stdout= и sys.stderr= строки из моего кода, отказы сегментации, кажется, уходят.

Я уверен, что существуют другие пути вокруг этого, что я должен буду, вероятно, обратиться к, но кто-либо может видеть что-нибудь, что я делаю очевидно неправильно здесь, который мог вызывать эти отказы сегментации? Это сводит меня с ума.Спасибо!

PS - Я понимаю, что перенаправление sys.stderr к GUI не могло бы быть прекрасной идеей, но я все еще получаю отказы сегментации, даже когда я только перенаправляю 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()
5
задан nbro 20 May 2015 в 00:59
поделиться

2 ответа

Хорошо, мне удалось отследить проблему. Мне так и не удалось воссоздать эту проблему в Mac OS X 10.5.8, где я изначально разработал код. Ошибки сегментации, похоже, возникают только в RedHat Enterprise Linux 5.

Оказывается, виноват этот фрагмент кода:

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. Если я изменю приведенный выше фрагмент кода на следующий:

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 = ...) заключался в том, чтобы сделать так, чтобы пользователь не мог редактировать текстовое поле. Однако, когда текстовое поле находится в состоянии tk.DISABLED , вызовы self.insert (...) также не работают.

Обходное решение, которое я придумал, состоит в том, чтобы оставить поле Text включенным, но заставить поле Text игнорировать весь ввод с клавиатуры (что создает иллюзию поведения только для чтения, если пользователь пытается использовать клавиатуру). Самый простой способ сделать это - изменить метод __ init __ так, чтобы он выглядел следующим образом (измените состояние на tk.NORMAL и измените привязку для 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

Надеюсь, что это поможет любому, кто столкнется с той же проблемой.

4
ответ дан 14 December 2019 в 08:44
поделиться

Я предполагаю, что это часть более крупной многопоточной программы.

Вместо использования блокировки пусть ваш код записывается в потокобезопасный объект очереди. Затем в основном потоке вы опрашиваете очередь и пишете в текстовый виджет. Вы можете выполнить опрос, используя цикл событий (вместо написания собственного цикла), запустив задание опроса, которое перепланировало себя для запуска через несколько мс позже с использованием after (пары сотен мс, вероятно, вполне достаточно).

3
ответ дан 14 December 2019 в 08:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: