Как мога да направя тихите изключения по-силни в 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 приложение ми харесва, че GUI е малко устойчив на сривове. Не ми харесва, че на моите потребители им е трудно да ми дадат полезна обратна връзка, за да коригирам полученото неочаквано поведение.

Как трябва да се справя с това? Има ли стандартен начин за излагане на tracebacks или stderror или какво ли още не в приложение на Tkinter? Търся нещо по-елегантно от това да пробвам/освен навсякъде.

РЕДАКТИРАНЕ: Йохен Рицел даде отличен отговор по-долу, който изскача предупредителна кутия и спомена за прикачването му към клас. Само за да направя това изрично:

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 като tk“, направете го

Tkinter.Tk.report_callback_exception = show_error
person Jochen Ritzel    schedule 22.01.2011
comment
Отлично, благодаря! Бихте ли разширили коментара си относно поставянето на това в класа на приложението? - person Andrew; 23.01.2011
comment
@Andrew: Просто имах предвид, че обикновено бихте написали приложението си в подклас, който презаписва този метод, вместо да променяте самия клас Tk. Само в случай, че искате различни функции за отчет в различни рамки. - person Jochen Ritzel; 23.01.2011
comment
Свързано: Обработка на изключение в python tkinter - 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
@Andrew Благодаря за тази точка! - person mika; 03.08.2021

Първо последващи действия: Точно днес корекция на CPython тракер за tkinter.Tk.report_callback_exception документен низ изясни, че решението на Jochen е предвидено. Корекцията също така (и основно) спря 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