2011-01-22 3 views
14

Jeśli uruchomić następujący kod z terminala, pojawia się jako komunikat o błędzie w terminalu:W jaki sposób uczynić cichsze wyjątki głośniejszymi w 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() 

Jednakże, jeśli go uruchomić bez terminala (powiedzmy, przez dwukrotne kliknięcie ikony), przy czym komunikat o błędzie jest wyłączony.

W mojej prawdziwej, bardziej skomplikowanej aplikacji Tkinter, podoba mi się, że GUI jest trochę odporny na zderzenia. Nie podoba mi się to, że moi użytkownicy mają trudności z udzieleniem mi przydatnej informacji zwrotnej w celu naprawienia powstałego nieoczekiwanego zachowania.

Jak mam sobie z tym poradzić? Czy istnieje standardowy sposób na ujawnienie śladów zwrotnych lub strakotypu lub czegoś innego w aplikacji Tkinter? Szukam czegoś bardziej eleganckiego niż próba wprowadzenia/z wyjątkiem wszędzie.

EDYCJA: Jochen Ritzel podał poniżej doskonałą odpowiedź, która wyświetli okno z ostrzeżeniem i wspomniał o dołączeniu go do klasy. Wystarczy, aby to jednoznacznie:

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

Moja pozostała zamieszanie: Jochen wspomina o możliwości mające różne funkcje raportowania wyjątek w różnych ramkach. Nie widzę jeszcze, jak to zrobić. Czy to oczywiste?

+0

Wyjątek jest nadal wyprowadzany po dwukrotnym kliknięciu ikony. Po prostu nie drukujesz nigdzie standardowo. – Falmarri

+0

Zgoda! Szukam osób, które polecą elegancki/standardowy sposób na ujawnienie użytkownikowi stdout lub stderror. – Andrew

+1

Klasa 'App' to ramka, zwykle wyprowadzona z' tk.Frame'. Jeśli twój program ma dwie różne klasy klatek, które były używane dla różnych rzeczy, to każda klasa klatek może mieć własną wersję 'report_callback_exception()', która wyświetla błąd w inny sposób. –

Odpowiedz

21

Jest report_callback_exception to zrobić:

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 

Jeśli nie importować 'Tkinter jako tk', a następnie zrobić

Tkinter.Tk.report_callback_exception = show_error 
+0

Doskonale, dziękuję! Czy mógłbyś rozszerzyć komentarz na temat umieszczania go na klasie App? – Andrew

+1

@Andrew: Chodziło mi o to, że zazwyczaj piszesz swoją aplikację w podklasie, która nadpisuje tę metodę, zamiast zmieniać samą klasę Tk. Na wszelki wypadek potrzebujesz różnych funkcji raportów w różnych ramkach. –

+0

Powiązane: [Obsługa wyjątków w python tkinter] (https://stackoverflow.com/a/35073005/3357935) –

1

pierwszy Followup: Właśnie dzisiaj, łatę na CPython tracker dla docstring wyjaśniono, że jest przeznaczony do Jochen's solution. Ta poprawka również (i przede wszystkim) zatrzymała tk przed awarią wyjątków wywołania zwrotnego, gdy uruchomiono pod pythonw w systemie Windows.

Po drugie: oto początek procesu tworzenia funkcji stderr bez konsoli (to powinno być oddzielne pytanie 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 

Potrzebne jest więcej, aby ukryć okno wyskakujące, dopóki nie będzie potrzebne, a następnie uczynić je widocznym.