はじめに
以前Tkinterに関する記事を書いたときはわからなかったのだが、いろいろツールを作成しているとClassを使ったほうがイケてるし使いやすいと感じるようになった。
自分だけが使うのであればコマンドラインで使用してしまうが、ツールを普及させようと思うとGUI化がマストになってくる。ここらでTkinterの考えをfixし、見やすいコードを書こう(自戒)
以前の記事
Classを使用するメリット
-
root = tk.Tk()
が複数ウィンドウでごちゃつかない - ウィンドウと関数(メソッド)間で変数を共有できる
-
entry
を増やしたり消したりした際の修正が楽
-
- 思いついたら追記するよ
目標
基本の考え方
以前の記事と重複するが、基本の考え方は
「ヒエラルキーの高いウィジェットから定義していき、ヒエラルキーの低いウィジェットから配置していく」
tkinterでGUI作成する際は次のような考え方をする。
親ウィジェットの定義
子ウィジェットの定義
孫ウィジェットの定義
...
孫ウィジェットの配置
子ウィジェットの配置
親ウィジェットの配置
コード
変数にself.
をつけておくことでclass
内どこからでも共通して呼び出したり書き換えたりすることができる。
うまくやればdef
文の引数がself
のみで済む。
import tkinter as tk
from tkinter import messagebox
class MainGui():
def __init__(self):
# =====================================================================
# 定義セクション
# =====================================================================
# 画面そのもの(親)の定義
self.root = tk.Tk()
self.root.title("タイトル")
# フレーム(子)の定義
self.frame1 = tk.Frame(self.root)
self.frame2 = tk.Frame(self.root)
# エントリーとボタン(孫)の定義
self.entry = tk.Entry(self.frame1, width=50)
self.button = tk.Button(self.frame2, width=10, text="ボタン",
command=lambda: self.popup_text())
# =====================================================================
# レイアウトセクション
# =====================================================================
# エントリーとボタン(孫)の配置
self.entry.grid(row=0, column=0)
self.button.grid(row=0, column=0)
# フレーム(子)の配置
self.frame1.grid(row=0, column=0)
self.frame2.grid(row=1, column=0)
# 画面そのもの(親)の配置
self.root.mainloop()
def popup_text(self):
messagebox.showinfo("title", self.entry.get())
MainGui()
class MainGui():
の部分でメインのウィンドウを構成するパーツと、内部で使用する関数(メソッド)を定義する。
def __init__(self):
の部分で階層の高いウィジェットから順に定義し、階層の低いウィジェットから順にレイアウトする。最後にself.root.mainloop()
でウィンドウを表示してコンストラクタを終了する。
def popup_text(self):
の部分は内部で呼び出す関数(メソッド)を定義している。クラス化しentry
をジェットをself.entry
と定義したことでself.entry.get()
を引数なしで使用することができる。
MainGui()
でクラスをインスタンス化し、実際にウィンドウを起動する。
テンプレ化
ここからは自己満足。
過去記事で作成したデコレータを追加している。
- 1つのウィンドウに1つのクラスを定義する
- 別ウィンドウに遷移する場合はもとのウィンドウを
self.root.destroy()
で消す -
class
内にdef
文を追加する際は@show_on_error
デコレータをつける -
def close(self):
で終了処理を記入、何も書かない消せないウィンドウになってしまう
import tkinter as tk
from tkinter.messagebox import showerror
from functools import wraps
import traceback
def show_on_error(func):
# エラー時にポップアップするデコレータ
@wraps(func)
def popup(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
print(traceback.format_exc())
title = e.__class__.__name__
msg = traceback.format_exc(limit=0)
showerror(title, msg)
return popup
class GUI():
@show_on_error
def __init__(self):
# top
self.root = tk.Tk()
self.root.title("title")
# 2nd
self.frame = tk.Frame(self.root)
# 3rd
# ウィジェットの定義
# 3rd
# ウィジェットの配置
# 2nd
self.frame.grid(row=0, column=0)
# top
self.root.protocol("WM_DELETE_WINDOW", lambda: self.close())
self.root.mainloop()
@show_on_error
def close(self):
self.root.destroy()
return
def main():
GUI()
return
if __name__ == "__main__":
main()