Как я могу сделать тихие исключения громче в tkinter?

Если я запускаю следующий код из терминала, я получаю полезное сообщение об ошибке в терминале:

import Tkinter as tk

master = tk.Tk()

def callback():
    raise UserWarning("Exception!")

b = tk.Button(master, text="This will raise an exception", command=callback)
b.pack()

tk.mainloop()

Однако, если я запускаю его без терминала (скажем, двойным щелчком по значку), сообщение об ошибке подавляется.

В моем реальном, более сложном приложении Tkinter мне нравится, что графический интерфейс немного устойчив к сбоям. Мне не нравится, что моим пользователям трудно дать мне полезную обратную связь, чтобы исправить полученное неожиданное поведение.

Как мне справиться с этим? Есть ли стандартный способ выставить трассировку или stderror или еще что-то в приложении Tkinter? Я ищу что-то более элегантное, чем везде ставить try/except.

РЕДАКТИРОВАТЬ: Йохен Ритцель дал отличный ответ ниже, в котором появляется окно с предупреждением, и упомянул о присоединении его к классу. Просто чтобы сделать это явным:

import Tkinter as tk
import traceback, tkMessageBox

class App:
    def __init__(self, master):
        master.report_callback_exception = self.report_callback_exception
        self.frame = tk.Frame(master)
        self.frame.pack()
        b = tk.Button(
            self.frame, text="This will cause an exception",
            command=self.cause_exception)
        b.pack()

    def cause_exception(self):
        a = []
        a.a = 0 #A traceback makes this easy to catch and fix

    def report_callback_exception(self, *args):
        err = traceback.format_exception(*args)
        tkMessageBox.showerror('Exception', err)

root = tk.Tk()
app = App(root)
root.mainloop()

Моя оставшаяся путаница: Йохен упоминает о возможности использования разных функций отчетности об исключениях в разных фреймах. Я пока не вижу, как это сделать. Это очевидно?


person Andrew    schedule 22.01.2011    source источник
comment
Исключение по-прежнему выводится при двойном щелчке по значку. Просто вы нигде не печатаете стандарт.   -  person Falmarri    schedule 23.01.2011
comment
Согласовано! Я ищу людей, которые порекомендуют элегантный/стандартный способ предоставления пользователю stdout или stderror.   -  person Andrew    schedule 23.01.2011
comment
Класс App — это фрейм, обычно производный от tk.Frame. Если бы в вашей программе было два разных класса фреймов, которые использовались для разных целей, то каждый класс фреймов мог бы иметь свою собственную версию report_callback_exception(), отображающую ошибку по-разному.   -  person Don Kirkby    schedule 24.08.2015
comment
В Python 3 tkMessageBox переместился в tkinter.messagebox, поэтому import tkinter.messagebox затем в теле report_callback tkinter.messagebox.showerror(...).   -  person PeterK    schedule 13.06.2021


Ответы (3)


Для этого есть report_callback_exception:

import traceback
import tkMessageBox

# You would normally put that on the App class
def show_error(self, *args):
    err = traceback.format_exception(*args)
    tkMessageBox.showerror('Exception',err)
# but this works too
tk.Tk.report_callback_exception = show_error

Если вы не импортировали «Tkinter as tk», сделайте

Tkinter.Tk.report_callback_exception = show_error
person Jochen Ritzel    schedule 22.01.2011
comment
Отлично, спасибо! Не могли бы вы расширить свой комментарий о том, чтобы поместить это в класс App? - person Andrew; 23.01.2011
comment
@Andrew: я просто имел в виду, что обычно вы пишете свое приложение в подклассе, который перезаписывает этот метод, вместо того, чтобы изменять сам класс Tk. На тот случай, если вам нужны разные функции отчета в разных фреймах. - person Jochen Ritzel; 23.01.2011
comment
Связано: Обработка исключений в tkinter python - person Stevoisiak; 01.03.2018
comment
Если у вас есть класс приложения для Tk, вы можете переопределить функцию обработки ошибок с помощью def report_callback_exception(self, exc, val, tb): tkMessageBox.showerror("Exception", message=str(val)) - person Stevoisiak; 01.03.2018
comment
Также работает в python3, но вам нужно использовать messagebox из модуля tkinter вместо tkMessageBox. - person Stanislav Ivanov; 11.10.2018
comment
@ Андрей Спасибо за это! - person mika; 03.08.2021

Сначала дополнение: только сегодня патч для трекера CPython для строки документации tkinter.Tk.report_callback_exception прояснил, что < предназначено решение href="https://stackoverflow.com/a/4771200/3357935">Йохена. Патч также (и в первую очередь) предотвратил сбой tk при исключениях обратного вызова при запуске под pythonw в Windows.

Во-вторых: вот простое начало решения для создания функции stderr без консоли (это действительно должен быть отдельный вопрос SO).

import sys, tkinter

root = tkinter.Tk()

class Stderr(tkinter.Toplevel):
    def __init__(self):
        self.txt = tkinter.Text(root)
        self.txt.pack()
    def write(self, s):
        self.txt.insert('insert', s)

sys.stderr = Stderr()

1/0 # traceback appears in window

Требуется больше, чтобы всплывающее окно оставалось скрытым до тех пор, пока оно не понадобится, а затем сделать его видимым.

person Terry Jan Reedy    schedule 14.09.2014

Я нашел эту тему, пытаясь решить ту же проблему. Мне пришлось внести некоторые изменения на основе методологий текущей версии tkinter. Я также добавил настраиваемый обработчик сообщений об ошибках, поскольку полагал, что мой пользователь не будет знать, что означает сообщение об ошибке Tk.

В __init__ вашего класса входят:

parent.report_callback_exception = self.report_callback_exception

затем в вашем классе Tk:

    def report_callback_exception(self, exc, val, tb):
        #Handles tkinter exceptions
        err_msg = {'Index 0 out of range': 'None found'}
        try:
            err = err_msg[str(val)]
        except KeyError:
            err = 'Oops, try again'
        messagebox.showerror('Error Found', err)

Вы можете расширить словарь err_msg, включив в него все, что захотите. В моем случае я создаю доступную для поиска базу данных из объекта Entry, поэтому, если пользователь ввел строку, которой нет в базе данных, это дает пользователю ошибку простого языка.

person Tom    schedule 17.08.2020