【Tkinter】Python tkinterはこう考えればイメージしやすい！

# はじめに

この記事には続きがあります

https://qiita.com/yulily/items/5043b342d9445487223f

tkinterってとっつきづらいきがする。でもPySimpleGUIはなんとなくカッコ悪い気がする、、、
tkinterをマスターしないまでも、「こう考えればOK!」と頭の中で整理しやすくするために覚書を残したいと思います。

とりあえずウィジェットの作成、配置、関数の実行ができればOK

# 基本の考え方
個人的にええ感じやんと思うtkinterの書き方の基本方針は
「ヒエラルキーの高いウィジェットから定義していき、ヒエラルキーの低いウィジェットから配置していく」

:::note info
tkinterでGUI作成する際は次のような考え方をする。

親ウィジェットの定義
子ウィジェットの定義
孫ウィジェットの定義
...
孫ウィジェットの配置
子ウィジェットの配置
親ウィジェットの配置
:::

ウィジェットの配置方法はpack,grid,placeがある。個人的にはgridが好み。
| 配置方法 | イメージ |
|:-:|:-|
|pack|詰め込み|
|grid|マトリクス的に配置|
|place|任意の場所に置く|


# 例：定義と配置
次のようなGUIを作成する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2716347/1a6b46ec-e15e-90ca-af7c-ed51f0d1e3f8.png)

## ベタ打ち
画面そのもの（親）を作成
その中にフレームを2つ（子）作成
その中にエントリーボックスとボタン（孫）を作成

```python
import tkinter as tk
# =============================================================================
# 定義セクション
# =============================================================================
# 画面そのもの（親）の定義
root = tk.Tk()
root.title("タイトル")

# フレーム（子）の定義
frame1 = tk.Frame(root)
frame2 = tk.Frame(root)

# エントリーとボタン（孫）の定義
entry = tk.Entry(frame1, width=50)
button = tk.Button(frame2, width=10, text="ボタン")

# =============================================================================
# 配置セクション
# =============================================================================
# エントリーとボタン（孫）の配置
entry.grid(row=0, column=0)
button.grid(row=0, column=0)

# フレーム（子）の配置
frame1.grid(row=0, column=0)
frame2.grid(row=1, column=0)

# 画面そのもの（親）の配置
root.mainloop()
```

# 関数の設定
ウィジェットに操作がされたとき、関数を動かすことができる。
ウィジェットを定義する際、```command```に関数を仕込む。引数を指定したい場合はlambda関数を使用する。
```python
button = tk.Button(frame, width=10, text="ボタン", command=関数名)
# or
button = tk.Button(frame, width=10, text="ボタン", command=lambda:関数(引数))
```

# 値の取得
値を取得したいときは```get()```メソッドを使用する
```python
entry.get()
```

# 例：ボタンを押すと値を取得する

ボタンを押すと関数が動き、入力された値を取得するアプリ
```python
import tkinter as tk
# =============================================================================
# 関数の定義
# =============================================================================
def gettext(entry):
    text = entry.get()
    print(f"{text}")

# =============================================================================
# 定義セクション
# =============================================================================
# 画面そのもの（親）の定義
root = tk.Tk()
root.title("タイトル")

# フレーム（子）の定義
frame = tk.Frame(root)

# エントリーとボタン（孫）の定義
entry = tk.Entry(frame, width=50)
button = tk.Button(frame, width=10, text="ボタン", command=lambda:gettext(entry))
checkbutton = tk.Checkbutton(frame)

# =============================================================================
# 配置セクション
# =============================================================================
# エントリーとボタン（孫）の配置
entry.grid(row=0, column=0)
button.grid(row=1, column=0)
checkbutton.grid(row=2, column=0)

# フレーム（子）の配置
frame.grid(row=0, column=0)

# 画面そのもの（親）の配置
root.mainloop()
```
# メニューバーの定義
メニューバーは画面そのものの子として振る舞うものの、メニューバーのみでヒエラルキーを形成する。
画面そのもの（親）に対する子として作成する。ただし、他のFrameやButtonとの兄弟として作成。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2716347/feca8dd8-9ff3-443c-f083-f6e9f3f8384b.png)

```python
import tkinter as tk
# =============================================================================
# 定義セクション
# =============================================================================
# 画面そのもの（親）の定義
root = tk.Tk()
root.title("タイトル")

# メニューバー(子)の定義
menubar = tk.Menu(root)

# メニュー（孫）の定義
menu = tk.Menu(root, tearoff=0)
menu.add_command(label="A")#command=lambda:関数(引数)を追加すれば処理をさせられる
menu.add_separator()
menu.add_command(label="B")

# メニュー（孫）の定義
helpmenu = tk.Menu(root, tearoff=0)
helpmenu.add_command(label="About us")
helpmenu.add_separator()
helpmenu.add_command(label="Help")

# =============================================================================
# 配置セクション
# =============================================================================
# メニュー（孫）の配置
menubar.add_cascade(label="menu", menu=menu)
menubar.add_cascade(label="help", menu=helpmenu)

# メニューバー（子）配置
root.config(menu=menubar)

# 画面そのもの（親）の配置
root.mainloop()
```
# バインド
ユーザーの動作をきっかけに関数を呼び出す。
引数を入れる方法が案外乗っていないので載せておく。

```.bind()```メソッドを使用し```root.bind("<キー>", lambda event:関数(引数))```のように記述する
キーはsequenceという引数に渡されている。

| バインドしたいキー | sequence |備考|
|:-:|:-:|:-:|
|Ctrl + A|```"<Control-a>"```||
|Shift + A|```"<Shift-A>"```|Aが大文字になることに注意|
|Enter|```"<Return>"```|EnterではなくReturnであることに注意|
|任意キー|```"<Key>"```||


:::note info
キーの名前に困ったときは
```event.keysym```を使うと何のキーが押されたかをstrで返してくれる
:::


```python
import tkinter as tk
# =============================================================================
# 関数の定義
# =============================================================================
def function(text):
    print(text)

# =============================================================================
# 定義セクション
# =============================================================================
# 画面そのもの（親）の定義
root = tk.Tk()
root.title("タイトル")

# =============================================================================
# バインド
# =============================================================================
root.bind("<Key>", lambda event:function(event.keysym))

# =============================================================================
# 配置セクション
# =============================================================================
# 画面そのもの（親）の配置
root.mainloop()
```