3
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Tkinter】Python tkinterはClassで書いたほうが良い

Last updated at Posted at 2024-06-06

はじめに

以前Tkinterに関する記事を書いたときはわからなかったのだが、いろいろツールを作成しているとClassを使ったほうがイケてるし使いやすいと感じるようになった。
自分だけが使うのであればコマンドラインで使用してしまうが、ツールを普及させようと思うとGUI化がマストになってくる。ここらでTkinterの考えをfixし、見やすいコードを書こう(自戒)

以前の記事

Classを使用するメリット

  • root = tk.Tk()が複数ウィンドウでごちゃつかない
  • ウィンドウと関数(メソッド)間で変数を共有できる
    • entryを増やしたり消したりした際の修正が楽
  • 思いついたら追記するよ

目標

こういうGUIを作る。
image.png

ボタンを押すと入力した文字がポップアップされる。
image.png

基本の考え方

以前の記事と重複するが、基本の考え方は
「ヒエラルキーの高いウィジェットから定義していき、ヒエラルキーの低いウィジェットから配置していく」

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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?