处理python tkinter中的异常

sar*_*jit 4 python tkinter

我在Python Tkinter中编写了一个应用程序.我最近注意到,对于其中一个操作,如果该操作失败,它有时会关闭(不会给出任何错误).我写了一个小程序来说明问题: -

import os
from Tkinter import *

def copydir():
    src = "D:\\a\\x\\y"
    dest = "D:\\a\\x\\z"
    os.rename(src,dest)

master = Tk()

def callback():
    global master
    master.after(1, callback)
    copydir()
    print "click!"

b = Button(master, text="OK", command=copydir)
b.pack()

master.after(100, callback)

mainloop()
Run Code Online (Sandbox Code Playgroud)

要重现该问题,请在"ms命令提示符"中打开它将重命名的文件夹,以便重命名它将从Tkinter代码中引发异常.

我的原始代码是使用线程并且还在执行其他任务,因此我尝试使此测试脚本中的操作尽可能相似.

现在,如果我通过双击它来运行此代码,那么程序只需关闭而不会抛出任何错误.但是,如果我从控制台运行此脚本,那么异常消息将被转储到控制台上,至少我知道,出了点问题.

我可以通过在尝试重命名的代码中使用try/catch来修复此代码,但我也想告知用户这个失败.所以我只想知道在编写Tkinter App时应该遵循哪些编码方法,我想知道: -

1)每当用户双击它时,我可以让我的脚本在文件中转储一些堆栈跟踪.通过这个至少,我会知道一些错误并修复它.

2)我可以阻止tkinter应用程序退出此类错误并在某些TK对话框中抛出任何异常.

感谢帮助!!

小智 7

我看到你有一个非面向对象的例子,所以我将展示两个变种来解决异常捕获的问题.

密钥位于tkinter\__init__.py文件中.可以看出,有一种记录report_callback_exceptionTk类方法.这是它的描述:

report_callback_exception()

报告sys.stderr上的回调异常.

应用程序可能希望覆盖此内部函数,并且当sys.stderr为None时应该.

所以我们看到它应该覆盖这个方法,让我们做!

非面向对象的解决方案

import tkinter as tk
from tkinter.messagebox import showerror


if __name__ == '__main__':

    def bad():
        raise Exception("I'm Bad!")

    # any name as accepted but not signature
    def report_callback_exception(self, exc, val, tb):
        showerror("Error", message=str(val))

    tk.Tk.report_callback_exception = report_callback_exception
    # now method is overridden

    app = tk.Tk()
    tk.Button(master=app, text="bad", command=bad).pack()
    app.mainloop()
Run Code Online (Sandbox Code Playgroud)

面向对象解决方案

import tkinter as tk
from tkinter.messagebox import showerror


class Bad(tk.Tk):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # or tk.Tk.__init__(*args, **kwargs)

        def bad():
            raise Exception("I'm Bad!")
        tk.Button(self, text="bad", command=bad).pack()

    def report_callback_exception(self, exc, val, tb):
        showerror("Error", message=str(val))

if __name__ == '__main__':

    app = Bad()
    app.mainloop()
Run Code Online (Sandbox Code Playgroud)

结果

我的环境:

Python 3.5.1 |Anaconda 2.4.1 (64-bit)| (default, Dec  7 2015, 15:00:12) [MSC  
v.1900 64 bit (AMD64)] on win32

tkinter.TkVersion
8.6

tkinter.TclVersion
8.6
Run Code Online (Sandbox Code Playgroud)


A. *_*das 5

您可以覆盖Tkinter的CallWrapper。为此,必须使用Tkinter的命名导入而不是通配符导入:

import Tkinter as tk
import traceback

class Catcher: 
    def __init__(self, func, subst, widget):
        self.func = func 
        self.subst = subst
        self.widget = widget
    def __call__(self, *args):
        try:
            if self.subst:
                args = apply(self.subst, args)
            return apply(self.func, args)
        except SystemExit, msg:
            raise SystemExit, msg
        except:
            traceback.print_exc(file=open('test.log', 'a'))

# ...
tk.CallWrapper = Catcher
b = tk.Button(master, text="OK", command=copydir)
b.pack()
master.mainloop()
Run Code Online (Sandbox Code Playgroud)