はじめに
Tkinterで作成したアプリケーションをexe化して完璧だと思った矢先、ユーザーによって予期せぬエラーを引き起こされる場合がある(知らんけど)
consoleウィンドウがあればtracebackを遡ってエラーを追求できるが、GUIのみの場合は何も起こらずエラーが出たのかすらわからない。
予測できるエラーに対してはtkinterのmessagebox.showerror()
をif文とかで仕込んでおけばよいが、すべてのエラーに対して仕込むのは至難の業。
今回は、こんな風にtracebackの一部を表示し、
consoleがなくても「エラーやで」とわかってもらう方法をメモします。
発想
基本的な考え方は次の通りである。
-
try-except
文を使ってエラーのときはmessagebox.showerror()
を表示する - すべての動作に
try-except
文を仕込むのはタルいので、デコレーターを作成しておく -
traceback
ライブラリを使ってエラー文をstr
でゲットする
コード
import tkinter as tk
from tkinter import messagebox
import traceback
from functools import wraps
# =============================================================================
# デコレータの定義
# =============================================================================
def show_on_error(function):
"関数実行中に例外が発生したら、showerrorするデコレータ"
@wraps(function)
def show_error(*args,**kwargs):
try:
function(*args,**kwargs)
except Exception as e:
print(traceback.format_exc())
title = e.__class__.__name__
message = traceback.format_exc(limit=0)
messagebox.showerror(f"{title}",
f"{message}")
return show_error
# =============================================================================
# GUIから呼び出される関数
# =============================================================================
@show_on_error
def calc(x):
"いかにもエラーが出そうな関数"
y = 1/float(x)
messagebox.showinfo("ans",y)
return y
# =============================================================================
# メイン処理
# =============================================================================
def main():
"GUI作成"
# ウィジェット定義
root = tk.Tk()
frame = tk.Frame(root,
padx=5,
pady=5)
entry = tk.Entry(frame,
width=50)
button = tk.Button(frame,
width=10,
text="button",
command=lambda:calc(entry.get()))
# ウィジェット配置
entry.grid(row=0,column=0)
button.grid(row=0,column=1)
frame.grid(row=0,column=0)
root.mainloop()
return
if __name__ == "__main__":
main()
解説
モジュール
GUI作成に必要なモジュール
import tkinter as tk
from tkinter import messagebox
エラー文を取得するモジュール
import traceback
関数.__name__
が正しく返ってくるようにするおまじない
from functools import wraps
デコレータ
デコレータshow_on_error
を定義します。
-
def
文
show_on_error
関数は引数に関数(function
)取り、
show_error
関数でfunction
を実行する直前にtry-except
文をかませる -
try
文
function
を実行し、エラーがあったらexcept
文に飛ぶ -
except
文
まずはconsoleにprint(traceback.format_exc())
でtracebackメッセージを出力する(print
を使わずにtraceback.print_exc()
でもOK)
title = e.__class__.__name__
で例外クラスの名前を取得
message = traceback.format_exc(limit=0)
でtracebackメッセージの最後の文を取得(limitの値を変えるとどこまで遡るかを変更できる)
messagebox.showerror(f"{title}",f"{message}")
でmessageboxを表示する -
return
文
return show_error
を書かないとcalc()
を呼び出した際に'NoneType' object is not callable
と怒られる -
@wraps(function)
は関数.__name__
が正しく返ってくるようにするおまじない
def show_on_error(function):
"関数実行中に例外が発生したら、showerrorするデコレータ"
@wraps(function)
def show_error(*args,**kwargs):
try:
function(*args,**kwargs)
except Exception as e:
print(traceback.format_exc())
title = e.__class__.__name__
message = traceback.format_exc(limit=0)
messagebox.showerror(f"{title}",
f"{message}")
return show_error
関数をデコレート
@show_on_error
でcalc()
関数をデコレート
x=0
やx="aaa"
を代入するとそれぞれ別のエラーがポップアップする。
エラーが出なければ逆数を表示する
@show_on_error
def calc(x):
"いかにもエラーが出そうな関数"
y = 1/float(x)
messagebox.showinfo("ans",y)
return y
GUIの作成
とりあえず構成だけ
root
└ frame
├ entry
└ button