2011-01-22 11 views
14

Si funciono el siguiente código desde un terminal, aparece un mensaje de error útiles en el terminal:¿Cómo puedo hacer que las excepciones silenciosas sean más fuertes en 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() 

Sin embargo, si me quedo sin un terminal (por ejemplo, haciendo doble clic en un icono), la mensaje de error es suprimido

En mi aplicación Tkinter real y más complicada, me gusta que la GUI sea un poco resistente a los golpes. No me gusta que a mis usuarios les cueste mucho hacer comentarios útiles para corregir el comportamiento inesperado resultante.

¿Cómo debo manejar esto? ¿Existe una forma estándar de exponer tracebacks o stderror o whatnot en una aplicación Tkinter? Estoy buscando algo más elegante que ponerlo a prueba/excepto en todas partes.

EDIT: Jochen Ritzel dio una excelente respuesta a continuación que muestra un cuadro de advertencia, y mencionó adjuntarlo a una clase. Sólo para hacer esto explícito:

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

Mi confusión restante: Jochen menciona la posibilidad de tener diferentes funciones de informe de excepción en diferentes marcos. Todavía no veo cómo hacer eso. ¿Es esto obvio?

+0

La excepción sigue apareciendo cuando hace doble clic en el icono. Es solo que no estás imprimiendo en cualquier lugar. – Falmarri

+0

de acuerdo! Estoy buscando gente que recomiende una forma elegante/estándar para exponer stdout o stderror al usuario. – Andrew

+1

La clase 'App' es un marco, generalmente derivado de' tk.Frame'. Si su programa tenía dos clases de marcos diferentes que se usaron para cosas diferentes, entonces cada clase de marco podría tener su propia versión de 'report_callback_exception()' que muestra el error de una manera diferente. –

Respuesta

21

Hay report_callback_exception para hacer esto:

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 

Si no importa 'Tkinter como tk', y luego hacer

Tkinter.Tk.report_callback_exception = show_error 
+0

Excelente, ¡gracias! ¿Te importaría expandir tu comentario sobre poner eso en la clase de la aplicación? – Andrew

+1

@Andrew: Solo quería decir que generalmente escribirías tu aplicación en una subclase que sobrescribe este método, en lugar de cambiar la clase Tk. En caso de que desee funciones de informe diferentes en marcos diferentes. –

+0

Relacionados: [Manejo de la excepción en python tkinter] (https://stackoverflow.com/a/35073005/3357935) –

1

Primero un seguimiento: Hoy mismo, un parche en el CPython tracker para el docstring tkinter.Tk.report_callback_exception dejó en claro que Jochen's solution es destinado. El parche también (y principalmente) impidió que tk se bloquee en las excepciones de devolución de llamada cuando se ejecuta bajo pythonw en Windows.

Segundo: aquí es un comienzo escueto de una solución para hacer que stderr funcione sin consola (esto realmente debería ser una pregunta 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 

más se necesita para mantener la ventana emergente oculto hasta que sea necesario y luego hacerla visible.

Cuestiones relacionadas