GUIとは
GUIはGraphical User Interface の略で、多くのPCやスマホで利用されている視覚的に操作できるアプリ/ソフトのことをいいます。
これに対立する概念としてCUI(Character User Interface)があり、コマンドプロンプトやpowershell(windows)、Mac/Linuxのterminal上で動くアプリケーションがこれにあたります。
PythonでGUI
pythonにもGUIアプリケーションを作るためのライブラリがいくつか存在します。その中で今回はTkinterをとりあげます。
Tkinterはそれなりに古く、資料が豊富にあり、そもそもpythonを入れると一緒に入っているので別途インストールする必要がなく手軽に始めやすいという利点があります(日本語を使うのはあまり向いてないっぽいけど)。
Tkinter
Tkinterとは端的に言うとTcl/Tkのpython用ラッパーです。
つまり何なんだと言われると、Tclとは80年代に使われていたスクリプト言語で、TkとはそのGUItoolkitです。今ではあまり聞かない言語ですが、windows・Mac・Linuxで現役で動作する上、Tkはtclごとpythonに組み込まれるなど陰で生き続けています。
このtcl/tkをpythonの文法で使えるようにしたのがTkinterとなります。
TkinterでのGUIアプリケーション開発
環境
python3.7を使用しています。
MacOS catalina 10.15.3で実行しています。
前提知識
Tkinterではボタンやウィンドウは全てwidgetと呼ばれ、これらを組み合わせることでGUIを構成します。
1. 最も簡単なTkinter app
from tkinter import *
root = Tk()
root.mainloop()
上記を実行してみましょう。以下のような真っ白なウィンドウが表示されたと思います。
コードの解説
巷のチュートリアルではfrom tkinter import *
のようにwildcard importでtkinterのライブラリを全てimportすることが多いようです。
ただしこのようなimportはあまり推奨はされていないので、きっちりやりたい人は以下の記事を参考に必要なものを個別にimportすることをおすすめします。
参考: How To Avoid Wildcard Imports
この記事ではめんどくさくなってwildcard importを使ってしまいました。気が向いたら直します。
tkinterでアプリを作る際に絶対に必要なのがTk root widgetで、これを作るのがroot = Tk()
です。先ほどみた真っ白なウィンドウで、この中にボタンやテキストを配置することになります。
tkinterは歴史が長く多くのチュートリアルがありますが、それぞれ様々な流儀で書かれています。中でも大きく2つの派閥に分かれているのがこのwidgetをクラスにするか否かです。個人的にはAn Introduction To Tkinterに倣い、初めはクラスにせず、後半コードが長くなってからクラスにしたいと思います。
Tkinterのアプリケーションとマウスのクリックなどのイベントを関連づけるのがroot.mainloop()
です。
「イベントがくるのを待ち、イベントが起こる度に何かをする」を繰り返すのでloopとついています。
2.widgetを配置する
tkinerのwidgetにはテキストエリア、ラベル、ラジオボタン、プルダウン等色々あります。また、配置する方法にもpack(), grid(), place()の3種類があり、それぞれ違いがあります。
pack() | grid() | place() |
---|---|---|
一次元的に並べる | 2次元的に並べる | 前者2つでうまくいかない時に使う |
・pack() で配置する
先ほどのコードに4行(とエンコード情報を1行)追加しました。
# -*- coding: utf-8 -*-
from tkinter import *
root = Tk()
w = Label(text="Enter your name!")
w.pack()
t = Entry()
t.pack()
root.mainloop()
一次元的(縦に一列に並んだ形)にLabel widgetとEntry widgetを配置しています。オラオラアプリならいいですが、わかりにくい気はします。# -*- coding: utf-8 -*-
を追加したのは、これを書いておかないと日本語での入力ができないからです。なお、これを書いていてもエンターを押すまで入力した文字が表示されないので、マルチバイトの文字が考慮されていないライブラリだと言えます。textとして日本語を使う場合はu"名前"
のようにuを付けます。
・grid() で配置する
pack()で配置する場合、全てのwidgetは縦並びになります。とはいえわかりづらい。例えば、以下のような入力欄の左にラベルが表示されているのに慣れている方がほとんどかと思います。
こうした二次元的な配置をする場合、grid()を使用します。
gridはroot widgetを縦横の列に分け、何列目の何行目に置くと言う形でGUIを構成します。
「pack()で配置する」の項で書いたコードを変更して、以下のようなGUIを作成します。
# -*- coding: utf-8 -*-
from tkinter import *
root = Tk()
w = Label(text="Name")
w.grid(row=0, column=0)
t = Entry()
t.grid(row=0, column=1)
w1 = Label(text="adress")
w1.grid(row=1, column=0)
t1 = Entry()
t1.grid(row=1, column=1)
root.mainloop()
・placeで配置する
基本的にはgrid()で配置している例が多いように思いますが、凝ったことをしたい場合place()を使う必要があります。これはまた今度書きます。
3.データをあれやこれやする
もちろんアプリケーションなわけですから、インプットに対して計算結果を返したり、何かをする必要があります。
例えば、「入力されたテキストから何か生成する」とか、「選択されたボタンに応じてアウトプットを変える」とかです。
今回は名前とアドレスを入力し確認ボタンを押すと、下に入力した文字が表示されるGUIアプリケーションを作ってみます。
Confirmボタンを押すと...
ボタンの下に入力情報が表示されました。
これのコードは以下です。
# -*- coding: utf-8 -*-
from tkinter import *
root = Tk()
def Confirm_click():
input_name = t.get()
input_adress = t1.get()
w2.configure(text=input_name)
w3.configure(text=input_adress)
w = Label(text=u"名前")
w.grid(row=0, column=0)
t = Entry()
t.grid(row=0, column=1)
w1 = Label(text="adress")
w1.grid(row=1, column=0)
t1 = Entry()
t1.grid(row=1, column=1)
conf_button = Button(text="Confirm", command=Confirm_click)
conf_button.grid(row=2, column=1)
w2 = Label(text=" ")
w2.grid(row=3, column=1)
w3 = Label(text=" ")
w3.grid(row=4, column=1)
root.mainloop()
少しコードが醜くなってしまいましたが、後でクラスにしてきれいにしますので、ひとまずこれで行きます。
まず目に入るのはいきなり現れたdef Confbutton_click()
だと思いますが、一旦無視してください。ちゃんと説明するので。その下の部分、
w = Label(text=u"名前")
w.grid(row=0, column=0)
t = Entry()
t.grid(row=0, column=1)
w1 = Label(text="adress")
w1.grid(row=1, column=0)
t1 = Entry()
t1.grid(row=1, column=1)
は上のgrid()で表示するの項のと同じです。
その下に今回はconfirmボタンを追加しました。
conf_button = Button(text="Confirm", command=Confirm_click)
conf_button.grid(row=2, column=1)
confirm ボタンにcommand=Confirm_click
と先ほど無視してくださいと書いた関数が出てきました。このボタンが押されると先のConfirm_click()
が実行されるのです。この関数を先に定義したのは、定義しておかなければnot defined
と言われてしまうからです。(自分の時は少なくともそうだった)
それではconfirm_click関数に飛んで見ましょう。
def Confirm_click():
input_name = t.get()
input_adress = t1.get()
w2.configure(text=input_name)
w3.configure(text=input_adress)
ボタンが押されたあとの動作は、入力された文字列を受け取り、ボタンの下に表示することです。
まず、get()
メソッドで入力された文字を受け取り、表示するwidgetのtextプロパティをconfigure()
メソッドで書き換えます。
conf_button.grid()
の下にある
w2 = Label(text=" ")
w2.grid(row=3, column=1)
w3 = Label(text=" ")
w3.grid(row=4, column=1)
が入力された文字を表示する場所です。
4.コードをクラスで書く
上のコードはごちゃごちゃしてきたので、見やすくするためにメインの部分はクラスにしましょう。
すると以下のようになります。
selfについての解説はUnderstanding self in Pythonが詳しいです。
# -*- coding: utf-8 -*-
from tkinter import *
class window(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
w = Label(text=u"名前")
w.grid(row=0, column=0)
self.t = Entry()
self.t.grid(row=0, column=1)
w1 = Label(text="adress")
w1.grid(row=1, column=0)
self.t1 = Entry()
self.t1.grid(row=1, column=1)
self.conf_button = Button(text="Confirm", command=self.Confirm_click)
self.conf_button.grid(row=2, column=1)
self.w2 = Label(text="")
self.w2.grid(row=3, column=1)
self.w3 = Label(text="")
self.w3.grid(row=4, column=1)
def Confirm_click(self):
input_name = self.t.get()
input_adress = self.t1.get()
self.w2.configure(text=input_name)
self.w3.configure(text=input_adress)
if __name__ == "__main__":
root = Tk()
window(root)
root.mainloop()
少なくとも先のコードよりは表示されるwidgetがまとめられ、動作も目でおいやすくなったと思います。