【執筆中】【Python／GUI】Tkinterのチュートリアル

# はじめに
PythonのGUIライブラリの一種である「Tkinter」について解説しています。
皆さんも一緒にTkinterを使いこなして、パイソニスターに近づいていきましょう。

# 環境
私が動かした環境は次の通り。

* Windows11 Home
* Python 3.9.9

# Tkinterって何ぞや？
Pythonで扱うことのできるGUIライブラリの一種です。最大の特徴は、Pythonさえインストールすればデフォルトで入っており、環境構築をする必要が無いところです。

そういう意味では、最も手軽に使用できるGUIライブラリとも言えますね。

ただ弱点もありまして

* デザインツールがほとんどない（「[Page](https://page.sourceforge.net/)」や「[Tkinter Designer](https://github.com/ParthJadhav/Tkinter-Designer)」くらいしか無い）
* 近代的なデザインに対応していない（最近は[CustomTkinter](https://customtkinter.tomschimansky.com/)というオシャレなGUIがある）

という点が挙げられます。ですが、ゆるっゆるなライセンスで、色々なUIが作れるという点で私個人としては最強のGUIライブラリだと思っています。

# ウィンドウ
とにかくココから。
GUIのすべての基礎となるウィンドウの解説になります。

## ウィンドウの表示
ただウィンドウだけを表示してみましょう。

このプログラムはフォーマットのようなものです。
すぐに書けるようにメモなり、ブックマークなりしておくと良いです。

```py
# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成と、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウのループ処理
window.mainloop()
```

このプログラムを実行すると、以下のようにウィンドウだけが表示されます。

![000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/80ec6051-04c2-c33b-778a-d598fc29a320.png)

## タイトルを設定する
ウィンドウにタイトルを設定してみます。
ウィンドウにタイトルを設定するには以下のメソッドを使用します。

```py:ウィンドウにタイトルを設定する
<Tkinterオブジェクト>.title("<タイトル名>")
```

それでは、ウィンドウタイトルを設定してみましょう。

```py
# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成と、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウにタイトルを設定
window.title("Tkinterを使ってみましょう！！")

# ウィンドウのループ処理
window.mainloop()
```

プログラムを実行すると、ウィンドウ最上部に設定したタイトルが表示されます。

![001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/09481019-f7ff-8557-8dff-25d563202a0f.png)

## ウィンドウのサイズと位置を設定する
ウィンドウのサイズと位置を設定してみます。
ウィンドウにサイズと位置を設定するには、次のメソッドを使用します。

```py:ウィンドウのサイズと位置を設定する
<Tkinterオブジェクト>.geometry("<横幅>x<高さ>+<X座標>+<Y座標>")
```

それでは、サイズと位置を設定してみましょう。

```py
# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成と、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウのサイズと位置を設定
window.geometry("500x400+500+300")

# ウィンドウのループ処理
window.mainloop()
```

画像では分かりにくいですが、指定した座標とサイズでウィンドウが出てきます。

![002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/71af1ebd-615e-d2fd-9134-6eff709bc620.png)

変数を使って `window.geometry(f"{width}x{height}+{xPos}+{yPos}")` のように設定することもできます。

変数を使った方が見やすいと思います。

```py
# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成と、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウのサイズと位置を設定
width   = 500   # 横幅
height  = 400   # 高さ
xPos    = 500   # X座標
yPos    = 300   # Y座標
window.geometry(f"{width}x{height}+{xPos}+{yPos}")

# ウィンドウのループ処理
window.mainloop()
```

### ウィンドウのサイズだけ設定する
ウィンドウのサイズだけを設定することもできます。
サイズだけを設定するのにも `geometry` メソッドを使用し、サイズだけのところを設定すればOKです。

```py:ウィンドウのサイズだけを設定する
<Tkinterオブジェクト>.geometry("<横幅>x<高さ>)
```

それでは、ウィンドウのサイズだけを設定してみましょう。

```py
# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウのサイズだけを設定
width   = 500   # 横幅
height  = 100   # 高さ
window.geometry(f"{width}x{height}")

# ウィンドウのループ処理
window.mainloop()
```

これを実行するとプログラム通り、横500ｘ縦100の大きさでウィンドウを表示できましたね。

![003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/67f51ccc-6e69-9761-fb7f-2d393b0168a3.png)

### ウィンドウの位置だけ設定する
あまり需要は無いと思いますが、ウィンドウの位置だけを設定することもできます。
位置だけを設定するのにも `geometry` メソッドを使用し、位置だけのところを設定すればOKです。

ただ、<font color="red"><b>『＋』から始まる</b></font>ことに注意してください。

```py:ウィンドウの位置だけを設定する
<Tkinterオブジェクト>.geometry("+<X座標>+<Y座標>")
```

それでは、ウィンドウの位置だけを設定してみましょう。

```py
# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウの座標だけを設定
xPos = 1000 # X座標
yPos = 700  # Y座標
window.geometry(f"+{xPos}+{yPos}")

# ウィンドウのループ処理
window.mainloop()
```

画像だけでは分かりにくいですが、しっかりと設定した位置でウィンドウが起動されていますね。

![004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/01a80506-523a-0100-3c70-f2b9bdf22eac.png)

### ウィンドウのサイズ変更を制限する
ウィンドウのサイズ変更に制限を加えることもできます。
`resizable` メソッドを使うことで、サイズ変更をできなくしたり、特定の方向のみサイズ変更できるようになります。

```py:ウィンドウのサイズの変更制限
<Tkinterオブジェクト>.resizable(<横幅の変更制限>, <高さの変更制限>)
```

`resizable` の引数には `True` または `False` を指定します。

* `True` ならサイズ変更ができる
* `False` ならサイズ変更ができない

試しに、横幅と高さの両方をサイズ変更できないようにしてみます。

```py
# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウのサイズ変更を制限する
window.resizable(False, False)

# ウィンドウのループ処理
window.mainloop()
```

`resizable` メソッドによってサイズ変更に制限をかけると、最大化の操作が出来なくなっています。
もちろん、ウィンドウの端をドラッグしてサイズ変更することもできなくなります。

![005.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/2ba303ef-fa5a-5175-6bc4-d18c02dbab1c.png)

### ウィンドウの最大・最小サイズを制限する
ウィンドウの変更できるサイズの最大値と最小値を設定することができます。
最大サイズを設定するには `maxsize` メソッド、最小サイズを設定するには `minsize` メソッドを使用します。

```py:ウィンドウの最大・最小サイズを設定する
# ウィンドウの最大サイズを設定
<Tkinterオブジェクト>.maxsize(<横幅>, <高さ>)

# ウィンドウの最小サイズを設定
<Tkinterオブジェクト>.minsize(<横幅>, <高さ>)
```

画面サイズを最大500x500px、最小300x300pxに制限してみたいと思います。

```py
# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウのサイズ変更を制限する
window.maxsize(500, 500)    # 最大サイズ
window.minsize(300, 300)    # 最小サイズ

# ウィンドウのループ処理
window.mainloop()
```

このプログラムを実行してみると画面サイズが設定したところで変更できなくなるようになります。
画像では分かりにくいですが、以下のようにマウスで変更できるサイズに制限を与えることができました。

![006.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/76934623-cea2-485a-6556-d6b24459338d.png)

## ウィンドウの色を設定する
ウィンドウの背景色を設定してみます。
背景色を設定するには `config` メソッドを使用します。

```py:背景色の設定
<Tkinterオブジェクト>.config(bg="<カラーコード>")
```

この `config` メソッドは多彩な機能があります。
ここでは背景色の設定だけに焦点を当てて説明します。

背景色を設定するには `config` メソッドに背景色（backgroundのbg）を指定する引数 `bg` があります。その `bg` にカラーコードを指定すればウィンドウの背景色を設定できます。

```py
# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウの背景色を設定
window.config(bg="#FFDDDD")

# ウィンドウのループ処理
window.mainloop()
```

ちゃんと指定したカラーコードで背景色が設定できたかと思います。

![007_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/78d32276-28f3-a1e5-e3d5-5786f9f2ad84.png)

### ウィンドウを透明度を設定する
需要は無いかな・・・と思いますが、一応載せときます。
ウィンドウの透明度を変更するには `attributes` メソッドを使用します。

```py:ウィンドウを透明度の設定
<tkinterオブジェクト>.attributes("-alpha", <透明度>)
```

`<透明度>` は 0 ～ 1 の範囲で指定します。
それでは、ウィンドウの透明度を設定してみたいと思います。

```py
# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウの透明度を設定
window.attributes("-alpha", 0.7)

# ウィンドウのループ処理
window.mainloop()
```

以下のようにウィンドウが透過して背景が見えるようになります。

![007_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/43a1b195-1705-6b25-bc69-96dde9773eb2.png)

## アイコンを設定する
ウィンドウ左上にあるアイコンを設定してみたいと思います。
デフォルトでは以下のように羽のアイコンとなっております。

![008.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/ca0354fa-f54c-efb8-e9b2-da76ae26dc19.png)


このアイコンを「[フリーアイコンSVG、PNG、ICO、ICNS](https://icon-icons.com/ja/)」というフリー画像サイトの「[App Essentials による Design Shots (16 アイコン)](https://icon-icons.com/ja/pack/App-Essentials/4038)」ページにある以下のアイコンに変更してみます。

![multimedia_player_sound_audio_music_note_musical_note_icon_256744.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/fb2f0b7e-45bf-c8af-5bc1-9f144f6d8e67.png)

アイコンを変更するには `iconbitmap` メソッドを使用します。

```py:ウィンドウのアイコンを設定する
<Tkinterオブジェクト>.iconbitmap("<アイコンのパス>")
```

:::note warn
アイコンに指定できるのは、拡張子が「ico」になっているもののみになります。
PNGやJPEGは指定できません。
:::

それでは、アイコンを変更してみます。

```py
# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウのアイコンを設定
window.iconbitmap(r"note_icon.ico")

# ウィンドウのループ処理
window.mainloop()
```

左上のアイコンが変更されているのが分かりますね。

![009.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/3dfd2202-c1ea-8aaa-005d-49c069831697.png)

## ウィンドウのサイズと座標を取得する
現在のウィンドウのサイズと座標を取得することができます。
ウィンドウのサイズと座標を取得するには `winfo_geometry` メソッドを使用します。

```py
<Tkinterオブジェクト>.winfo_geometry()
```

現時点ではウィンドウのサイズと座標を取得するメリットを感じにくいと思います。ですが、2つ目のウィンドウを起動するときにウィンドウ同士を重ねたくない場合や、ウィンドウに表示させる画像などのサイズに応じてウィンドウサイズを変更させるような場合に力を発揮してくれます。

それでは、ウィンドウのサイズと座標を取得してみましょう。

```py
# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウサイズの設定
width  = 300
height = 100
xPos = 400
yPos = 400
window.geometry(f"{width}x{height}+{xPos}+{yPos}")

# ウィンドウのサイズと座標を出力
window.update_idletasks() # mainloopの前に設定値の更新
print("ウィンドウのサイズと座標:", window.winfo_geometry())

# ウィンドウのループ処理
window.mainloop()
```

![010.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/7c8f7323-6905-9f16-98b6-dbf9e95bd0b8.png)

ここで `update_idletasks` について軽く説明します。
まず、`update_idletasks` を入れないとどうなるのか見てみましょう。

```py:update_idletasksが無いとどうなる？
# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウサイズの設定
width  = 300
height = 100
xPos = 400
yPos = 400
window.geometry(f"{width}x{height}+{xPos}+{yPos}")

# ウィンドウのサイズと座標を出力
print("ウィンドウのサイズと座標:", window.winfo_geometry())

# ウィンドウのループ処理
window.mainloop()
```

すると、期待したウィンドウサイズを正しく出力されなくなってしまいます。

![011.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/cbc03610-23d8-979f-9be5-2c7012c72c77.png)

Tkinter では `mainloop` メソッドにて設定値を更新するため、このような現象が起こってしまいます。
そこで、`mainloop` メソッドに入る前に設定値を更新すれば期待した値を取得することができます。

### ウィンドウのサイズだけ取得する
現在のウィンドウのサイズだけを取得することもできます。
ウィンドウのサイズを取得するには `winfo_width` メソッドや `winfo_height` メソッドを使用します。

```py
# ウィンドウの横幅を取得する
<Tkinterオブジェクト>.winfo_width()

# ウィンドウの高さを取得する
<Tkinterオブジェクト>.winfo_height()
```

では、ウィンドウサイズを取得してみたいと思います。

```py
# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウサイズの設定
width  = 300
height = 100
window.geometry(f"{width}x{height}")

# ウィンドウサイズを出力
window.update_idletasks() # mainloopの前に設定値の更新
print("ウィンドウの横幅:", window.winfo_width())
print("ウィンドウの高さ:", window.winfo_height())

# ウィンドウのループ処理
window.mainloop()
```

このプログラムを実行するとプロンプトにウィンドウサイズが出力されます。

![012.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/5203048e-ba75-618d-e123-e1a435def052.png)

### ウィンドウの座標だけを取得する
現在のウィンドウの座標を取得することもできます。
ウィンドウの座標はどこを基準にするのかによって取得できる値が変わってきますが、座標を取得するメソッドは4つだけです。

```py:ウィンドウの座標だけを取得
# ウィンドウを基準にしたX座標を取得する
<Tkinterオブジェクト>.winfo_x()

# ウィンドウを基準にしたY座標を取得する
<Tkinterオブジェクト>.winfo_y()

# ディスプレイを基準にしたX座標を取得する
<Tkinterオブジェクト>.winfo_rootx()

# ディスプレイを基準にしたY座標を取得する
<Tkinterオブジェクト>.winfo_rooty()

```

「ウィンドウを基準」「ディスプレイを基準」がいまいちピンとこないと思いますが、画像で違いを比較すると分かりやすいです。

![013.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/586da616-4fe2-8485-d1fb-289970364496.png)

文字通り、ウィンドウの左上を基準にするのか、ディスプレイの左上を基準にするのか、その違いだけです。

それでは、ウィンドウの座標を取得してみたいと思います。

```py
# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウサイズの設定
width  = 500
height = 300
xPos = 400
yPos = 400
window.geometry(f"{width}x{height}+{xPos}+{yPos}")

# ウィンドウのサイズと座標を出力
window.update_idletasks()
print("親ウィンドウのx座標:", window.winfo_x())
print("親ウィンドウのy座標:", window.winfo_y())
print("ディスプレイ上のx座標:", window.winfo_rootx())
print("ディスプレイ上のy座標:", window.winfo_rooty())

# ウィンドウのループ処理
window.mainloop()
```

実行するとウィンドウの座標を取得することができます。

![014.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/bc9fe9c2-ab72-5843-c3d5-5c46f88aab44.png)

# ウィジェットの配置
ボタンやラベルなどのウィジェットの説明に入る前に、先にウィジェットの配置方法について説明します。

ウィジェットの説明をするために、以下にベースとなるプログラムを用意しました。
このプログラムを用いてウィジェットの配置を説明していきます。

今はボタンについては分からなくても全然問題ありません。

```py
# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ボタンオブジェクトの生成と取得（今は分からなくてもOK）
btn1 = tkinter.Button(window, text="【　１　】")
btn2 = tkinter.Button(window, text="【　２　】")
btn3 = tkinter.Button(window, text="【　３　】")

# ウィンドウのループ処理
window.mainloop()
```

ちなみに、上記のプログラムを動かしてもウィンドウが表示されるだけで、ボタンは表示されません。
ボタンオブジェクトを生成しただけで、ウィンドウに配置していないからです。

![100.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/a33f8410-d501-a403-8177-562e3835fefc.png)

> **ウィジェットって何ですか？**
> 
> ウィジェットとはボタンやスクロールバー、画像といったウィンドウ上に表示される機能を持ったものや情報を伝えるものです。
> 説明するより見た方が速いと思いますので、図解をご覧ください。
> 
> ![101.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/fe90a8f4-d893-5b27-da9a-f81e9f594972.png)

## pack メソッド
`pack` メソッドでウィジェットを配置することができます。

```py:
<widgetオブジェクト>.pack()
```

`pack` メソッドでは、ウィジェット同士を重ねることなく順番に配置してくれます。
レイアウトの自由度は低いですが、簡単にウィジェットを配置することができます。

`pack` メソッドを使ってボタンを配置してみたいと思います。

```py
# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ボタンオブジェクトの生成と取得（今は分からなくてもOK）
btn1 = tkinter.Button(window, text="【　１　】")
btn2 = tkinter.Button(window, text="【　２　】")
btn3 = tkinter.Button(window, text="【　３　】")

# packメソッドでウィンドウにボタンを配置
btn1.pack()
btn2.pack()
btn3.pack()

# ウィンドウのループ処理
window.mainloop()
```

実行すると上から順にボタンが配置されます。

![102.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/d39dca58-1748-2f48-9bac-dea57af4fe2b.png)

### side オプション
`pack` メソッドでのウィジェットの配置をある程度制御することもできます。
その1つの方法が `side` オプションになります。

```py:
<widgetオブジェクト>.pack(anchor=tkinter.<配置場所>)

<配置場所>に指定できるオプション一覧：
    tkinter.LEFT   : 左から配置
    tkinter.TOP    : 上から配置
    tkinter.RIGHT  : 右から配置
    tkinter.BOTTOM : 下から配置
```

`side` オプションをすれば配置する方向性を制御できるようになります。
以下に全方位に2個ずつボタンを配置するプログラムを用意しました。

```py
# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ボタンオブジェクトの生成と取得（今は分からなくてもOK）
btn1_left = tkinter.Button(window, text="【１：左】")
btn2_left = tkinter.Button(window, text="【２：左】")

btn1_right = tkinter.Button(window, text="【１：右】")
btn2_right = tkinter.Button(window, text="【２：右】")

btn1_top = tkinter.Button(window, text="【１：上】")
btn2_top = tkinter.Button(window, text="【２：上】")

btn1_bottom = tkinter.Button(window, text="【１：下】")
btn2_bottom = tkinter.Button(window, text="【２：下】")

# packメソッドでウィンドウにボタンを配置
btn1_left.pack(side=tkinter.LEFT)
btn2_left.pack(side=tkinter.LEFT)

btn1_right.pack(side=tkinter.RIGHT)
btn2_right.pack(side=tkinter.RIGHT)

btn1_top.pack(side=tkinter.TOP)
btn2_top.pack(side=tkinter.TOP)

btn1_bottom.pack(side=tkinter.BOTTOM)
btn2_bottom.pack(side=tkinter.BOTTOM)

# ウィンドウのループ処理
window.mainloop()
```

このプログラムを実行すれば上下左右にボタンが配置されました。

![103.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/17be6fb4-0cbe-b8c5-24f4-281c65abd0a1.png)

ただし `pack` メソッドでウィジェットを配置する注意点が１つあり、

:::note warn
`pack` メソッドは配置する順番を考えないとぐちゃぐちゃなウィジェットの配置になってしまう。
:::

と言うことです。

どういうことか？　なのですが、先ほどのプログラムの `pack` メソッドによるウィジェットの配置する順番だけ変えてみました。

```py
# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ボタンオブジェクトの生成と取得（今は分からなくてもOK）
btn1_left = tkinter.Button(window, text="【１：左】")
btn2_left = tkinter.Button(window, text="【２：左】")

btn1_right = tkinter.Button(window, text="【１：右】")
btn2_right = tkinter.Button(window, text="【２：右】")

btn1_top = tkinter.Button(window, text="【１：上】")
btn2_top = tkinter.Button(window, text="【２：上】")

btn1_bottom = tkinter.Button(window, text="【１：下】")
btn2_bottom = tkinter.Button(window, text="【２：下】")

# packメソッドでウィンドウにボタンを配置（ここの順番をぐちゃぐちゃにしてみた）
btn1_top.pack(side=tkinter.TOP)
btn1_left.pack(side=tkinter.LEFT)
btn2_top.pack(side=tkinter.TOP)
btn1_right.pack(side=tkinter.RIGHT)
btn2_left.pack(side=tkinter.LEFT)
btn2_bottom.pack(side=tkinter.BOTTOM)
btn2_right.pack(side=tkinter.RIGHT)
btn1_bottom.pack(side=tkinter.BOTTOM)

# ウィンドウのループ処理
window.mainloop()
```

これを実行すると・・・

![104.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/fd86ac12-fc78-5362-a94b-d2fc8b0f6b9f.png)

かなり見た目が汚くなりましたね。

どうしてこんな配置になってしまうのか？

実は `pack` メソッドでウィジェットを配置すると、そのウィジェットの横幅または縦幅が独占されてしまうためです。

文字で説明しても分かりにくいですが、先ほどのプログラムの `pack` で配置している処理を見ながら図で確認と分かりやすいです。

まず、`pack` で配置している部分のみのプログラムを再度載せます。

```py
# packメソッドでウィンドウにボタンを配置（ここの順番をぐちゃぐちゃにしてみた）
btn1_top.pack(side=tkinter.TOP)
btn1_left.pack(side=tkinter.LEFT)
btn2_top.pack(side=tkinter.TOP)
btn1_right.pack(side=tkinter.RIGHT)
btn2_left.pack(side=tkinter.LEFT)
btn2_bottom.pack(side=tkinter.BOTTOM)
btn2_right.pack(side=tkinter.RIGHT)
btn1_bottom.pack(side=tkinter.BOTTOM)
```

最初の `btn1_top.pack(side=tkinter.TOP)` によって、ウィンドウの最上部にボタンが配置されます。この時、ボタンの横幅いっぱいを独占してしまいます。

![105_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/530f2a44-6240-c02e-fb47-e67243516540.png)

続いて `btn1_left.pack(side=tkinter.LEFT)` で、ウィンドウの最左部にボタンが配置され、同じようにボタンの縦幅いっぱいを独占してしまいます。

ですが、既に `pack` メソッドで独占された領域を、後から独占することができなくなります。

![105_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/4e9df2c5-026f-bd2e-380e-fbf4d1386c07.png)

同じ理屈で `btn2_top.pack(side=tkinter.TOP)` で、ウィンドウの最上部にボタンを配置しますが、独占済みの領域は独占できません。

![105_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/7ef5ecf4-27ad-8f71-f550-fad40418277d.png)

同様に `btn1_right.pack(side=tkinter.RIGHT)` で最右部にボタン配置しますが、独占済みの領域は独占できません。

![105_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/f64515a2-e8ac-19d4-94db-a930f3fe762c.png)

お気づきの方もいるかもしれませんが、`pack` によって独占される領域には以下の法則があります。

* `tkinter.TOP` または `tkinter.BOTTOM` で縦方向で配置したウィジェットは、横幅いっぱいの領域を独占
* `tkinter.LEFT` または `tkinter.RIGHT` で横方向で配置したウィジェットは、縦幅いっぱいの領域を独占

そこで `pack` メソッドでウィジェットを配置する際には、

:::note info
 配置する方向性に規則を持たせる
:::

を意識するとキレイに配置できるようになります。

* 横方向から配置する際には「左から右に」または「右から左に」
* 縦方向から配置する際には「上から下に」または「下から上に」
* 縦横組み合わせて配置するには「上下を先に、左右を後に」または「左右を先に、上下を後に」

と言うことですね。
### anchor オプション
執筆中

# ラベル
執筆中