Pythonで簡単なGUIを作れる「Tkinter」を使おう

今IoTで注目されている、手のひらサイズのコンピューター「Raspberry Pi(通称RPi、ラズパイ）」が人気を浴びています。
ラズパイでプログラムを動かしたりするとき、そのほとんどは**Python**でやっていることが多いようです。
そのとき、プログラムを作っていて「GUIが使いたい」と思うときはありませんか？
過去にも紹介されてきている、簡単なGUIをわずかなコードで作れるライブラリをご紹介します。
# 軽量ライブラリ「Tkinter」
簡単に扱えるものの中に「Tkinter」というライブラリがあります。
現在はQtなどが主流ですが、Tkinterはコードを難しくすることなくできるので、日経Linuxなどでも取り上げられています。
![tkinter-window.png](https://qiita-image-store.s3.amazonaws.com/0/196404/22c722b8-1024-7eaa-5d72-2bbff5bdb481.png)
###### Tkinterが動作している様子。
さっそく導入してみましょう。

導入においては、以下の環境が必要です。

|Name|Value|
|:--|----------------------:|
|OS|Windows, macOS, Linux|
|Pythonバージョン|3.8以降|


# Tkinterの導入
まず、TkinterはPythonで動くので、まだインストールしていない場合はインストールします。

## Windows の場合
WSLを使う手もありますが、TkinterはGUIを扱うものですのでネイティブ環境で実行します。
ここでは安定版の3.8.5をインストールします。

64ビット版: https://www.python.org/ftp/python/3.8.5/python-3.8.5-amd64.exe
32ビット版: https://www.python.org/ftp/python/3.8.5/python-3.8.5.exe

起動して最初の画面には「**Add Python 3.8 to PATH**」のチェックボックスがありますのでチェックを入れます。
これを設定すると、どこでもPythonが使えます。
まあ、本当はチェックボックス設けないで自動的にパス通してほしいのですが...

途中インストールを進めているとオプション機能をインストールするか尋ねられます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/196404/55441347-78ce-7dfe-91d9-c4a27d84a1d0.png)
Tkinterを使いますので、**td/tk and IDLE** にチェックを入れて進んでください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/196404/ed6118b0-f8c7-9e9a-7791-62e36f5a6760.png)
最後にこの画面が出ますが、「**Disable path length limit**」というものがあります。
これは、環境変数に登録できるパスの長さを無制限にするものです。設定したからと言ってWindowsが起動しなくなったり不安定になることはありません。
特に事情がなければクリックして制限を外しておきます。

## macOS の場合
こちらも公式サイトよりダウンロードできます。
前者同様、3.8.5をインストールします。

64ビット版: https://www.python.org/ftp/python/3.8.5/python-3.8.5-macosx10.9.pkg
Mac OS Xでは32ビット版はありません。

macOSのインストーラでは自動的にTkinterがインストールされますので特段操作は必要ありません。
画面の表示に従ってください。

## Linux の場合
Ubuntu系ディストリビューションや、一部のLinuxOSでは、はじめからPythonがインストールされているものがあります。
事前に ```$ python3 --version``` を実行して存在を確かめ、ないようであれば必要に応じてインストールしてください。

```bash
$ sudo apt-get install python3.8 python3-pip
```
※2020年7月30日追記 Python2.7のサポートが終了しましたので、インストールするパッケージを修正しました。

これでPythonは用意できました。次にTkinterをインストールします。

```bash
$ sudo apt-get install python3-tk
```

完了すればTkinterの準備は完了です。
# Pythonで使えるようにするには
Pythonでは、ライブラリのインポートに`import`というのを使います。
例えば、timeというライブラリをインポートするには`import time`とします。
カンマ区切りで一括で指定することもできます。

```python
import os, sys, time
```

ここではTkinterをインポートするので、`import tkinter`と入力します。
でも、毎回 tkinter.・・・とするのは面倒なので、tkという名前で使えるようにしたいなら

```python
import tkinter as tk
```
と記述します。ここからは、tkというエイリアス名でプログラムを作成していきます。

では、GUIを作成していきます。
# GUIを試しに作ってみよう
Tkinterでは、ボタンなどを**ウィジェット**と呼んでいます。
でもいまいちピンと来ない方もいるでしょうから、ここでは**部品**と呼ぶことにします。
## rootウィンドウを作る
Tkinterでは、まずrootウィンドウを作らないと、ウィンドウが表示されません。
また、色々な部品を表示するときにも、すべてrootウィンドウにぶら下がります。
そのため、Tk()メソッドで新しくウィンドウを作ります。

```python:tk.py
# メインウィンドウ作成
root = tk.Tk()
```
とりあえずこれを書いた状態で実行してみましょう。これはtk.pyとして保存した場合です。

```bash
$ python3 tk.py
```

すると、プログラムが正しければ、何もないウィンドウが表示されるはずです。
![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/196404/a4f5e9d0-da5a-6f2d-0c99-06b761fba68e.png)

ちなみに、ウィンドウのタイトルを変えることもできます。

```python
#メインウィンドウのタイトルを変更
root.title("Tkinter test")
```
![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/196404/bdeb824f-f486-96ce-4abb-41350968384a.png)

root変数で、Tkメソッドで作ったウィンドウが操作できるようになります。
root変数に対して、titleメソッドを呼び出すと、タイトルが変えられるというわけです。
日本語も入れられます。(※場合によっては文字化けするかもしれません)

今の状態では、ちょっとウィンドウサイズが小さいですね。大きくしてみましょう。
ウィンドウサイズを変えるには、geometryメソッドで変更できます。
例えば、640x480のウィンドウにするには

```python
#メインウィンドウを640x480にする
root.geometry("640x480")
```
とします。実行してみると、大きくなっているのがわかると思います。
![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/196404/faf5fbec-ad43-db4d-6f62-ddc4f1c7ad10.png)

## 最後は必ず、mainloop()
ウィンドウの表示ができるようになりましたが、最後にもう１つ。
Tkinterでは、各部品を表示した後は、最後に追加した部品でmainloopメソッドを実行することが定められています。
これがないと、**ボタンなどが正しく表示されない、描画されない、イベントが処理されない**といったバグの原因になります。
例えば、今rootウィンドウを作りましたが、とりあえずrootウィンドウだけなので、そちらでmainloopメソッドを呼び出します。

```python
#rootを表示し無限ループ
root.mainloop()
```
このmainloopメソッドはどの部品でも必須です。覚えておきましょう。

## テキストを表示する
Tkinterでは**Label**という部品を使うと、文字を表示できます。
試しに「Hello,World」と表示してみましょう。

```python
#ラベルを追加
label = tk.Label(root, text="Hello,World")
#表示
label.grid()

root.mainloop()
```
最後に`root.mainloop()`と書いてある場合は消してから追加してください。
実行すると、左上に「Hello,World」と表示されるはずです。
![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/196404/1dfdc729-3f32-845e-12c1-73bb08034b17.png)


Labelメソッドの書式はこんな感じです。

```python
label = tk.Label(window, param1=value, param2=value...)
```

*window*にはラベルを表示させたいウィンドウの変数を、*param1=value*の部分には色々なパラメーターを指定します。
「abc」と表示させたいなら

```python
label = tk.Label(window, text="abc")
```

となります。他のパラメーターを指定することもできます。
## 部品を表示するにはgrid()
label.grid()ってなんだろうと思ったと思いますが、gridメソッドはすべての部品に備わっているものです。
**pack、gridメソッドを呼び出さないと部品は表示されません。**
※Buttonはgridメソッドで表示するのが主流です
## ボタンを表示する
ボタンを表示するには、**Button**という部品を使えば表示できます。

```python
button = tk.Button(root, text="ボタン", command=pushed)
button.grid()
```

![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/196404/e01b22d5-61b2-2eb1-5149-376f527fab45.png)


tk.Buttonでは、commandに関数を指定すると、クリックした時に関数を呼び出すことができます。
例えば、pushedという関数を作り、クリックするとprint関数で「clicked」と表示するものとしましょう。

```python
def pushed():
 print("clicked")
```
defというのは、自作関数を作るものです。

それで、ボタン作成時は

```python
button = tk.Button(root, text="クリックしてね", command=pushed)
button.grid()
```
とします。実行して、ボタンをクリックしてみてください。

端末に「clicked」が出力されるはずです。
![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/196404/d8fb5c22-fbbb-8ab3-2cc0-c4fab6beed43.png)

さらに、ボタン作成後に表示するテキストを変更することもできます。

```python
button["text"] = "変更したぜ"
```
![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/196404/6a25da9d-d3f2-bf9f-d2fc-f3bd0980491c.png)


では、クリックしたときにメッセージを変えてみます。これをpushed関数に取り込んでみましょう。
pushed関数の定義の部分を

```python
def pushed(b):
```
に変えます。
その下に

```python:tk.py
[空白]b["text"] = "押されたよ"
[改行]
```
を入力します。
Pythonでは、**インデント(空白やタブ)**でまとまりを認識しているので、空白を開けないと

```
IndentationError: expected an indented block
```
というエラーになります。そのため、*def*や*if*などの複数行記述するものは、必ず**インデント**をつけるようにしましょう。
また、まとまりの終わりは**改行を1回余計に加える**ことが重要です。

さて、ボタンを生成する側は

```python
button = tk.Button(root, text="ボタン", command=lambda: pushed(button))
button.grid()
```
にします。
ここで、**lambda**というのが出ますが、これはPythonでコールバックなどを指定するときに役立つ**無名関数**というものです。
詳しくは「python lambda」「python 無名関数」で調べてみてください。

それで、`pushed(button)`の部分ですが、button変数をpushed関数から操作できるように、pushed関数に渡しています。
pushed関数ではそれをbという変数として受け取っています。

## これまでの部品を生成するプログラム
```python:tk.py
#!/usr/bin/python3
# -*- coding: utf8 -*-
import tkinter as tk

def pushed(b):
 b["text"] = "押されたよ"

#rootウィンドウを作成
root = tk.Tk()
#rootウィンドウのタイトルを変える
root.title("Tkinterテスト")
#rootウィンドウの大きさを320x240に
root.geometry("320x240")

#Label部品を作る
label = tk.Label(root, text="Tkinterのテストです")
#表示する
label.grid()

#ボタンを作る
button = tk.Button(root, text="ボタン", command= lambda : pushed(button))
#表示
button.grid()

#メインループ
root.mainloop()
```
大体、PythonとTkinterに慣れたでしょうか？
Tkinterは他にも使えることがたくさんあるので、調べてみてください。

# まとめ
まずはじめに、**tk.Tk()でメインウィンドウをつくる**
部品を生成したら、**grid、packメソッドを呼び出す**
プログラムの最後には、最後に作った部品に対して**mainloopメソッドを呼び出すようにする**