PythonにはGUI作成ライブラリがいくつか用意されています。
その中でも標準的かつ歴史の長いライブラリが「tkinter」です。
他のライブラリでもGUIを作成することはできますし、モダンなウィジェットを使いたい方には「tkinter」では物足りないかもしれません。
ですが、PythonにおけるGUI作成の基本的な操作を学ぶ・理解する、ということに関しては「tkinter」がおすすめです。
「tkinter」で操作の基本を抑えてから他のライブラリへ移行することで、スムーズに自分の考えたGUIを作成することができるでしょう。
この記事では、簡単なツール・アプリを作るために必要な「tkinter」の機能を実行結果を交えながら説明します。
また後半では、前半に説明した機能を使って簡易的なツールを作成しております。
⚠この記事では、「tkinter」についてのみ説明をしております。ツールやアプリの作成には、機能のメインとなる他ライブラリとの組み合わせが必要です。
watanabe-ichiro-nikki.hatenablog.com
ウィンドウ内の各パーツ
ウィンドウとフレーム
import tkinter as tk # ウィンドウ作成 root = tk.Tk() # ウィンドウタイトルの表示 root.title("ウィンドウタイトル") # ウィンドウ内にフレーム作成 frame = tk.Frame(root) # フレームをウィンドウ内に配置 frame.grid() # 上記コードを表示 root.mainloop()
ボタンやテキスト入力欄などのウィジェットを表示させるためには、ウィンドウとフレームを用意する必要があります。
tk.Tk()により、ウィンドウが作成されます。
そのウィンドウ上にフレームを作成するために、tk.Frame(tk.Tk())を実行します。
最終行のroot.mainloop()が実行されることにより、それまでに記述したウィンドウやフレーム、ウィジェットなどが表示されることになります。
root.geometry("600x400")
上記コードを記述することで、表示されるウィンドウの初期サイズを指定できます。
そこから任意で拡大・縮小させることも可能です。
上記コードが記述されていない場合、
フレーム内のウィジェットが見えるくらいのサイズ
または
平坦な状態でウィンドウが表示されます。
テキスト表示と文字色変更
import tkinter as tk root = tk.Tk() root.title("ウィンドウタイトル") frame = tk.Frame(root) frame.grid() # テキストを表示+文字色変更 lbl_1 = tk.Label(frame, text="テキストを表示させる") lbl_2 = tk.Label(frame, text="テキストを表示させる", fg="red") lbl_3 = tk.Label(frame, text="テキストを表示させる", fg="white", bg="red") # テキスト配置 lbl_1.grid(column=0, row=0) lbl_2.grid(column=1, row=0) lbl_3.grid(column=2, row=0) root.mainloop()
フレーム上にテキストを表示させるためには、tk.Label()を使用します。
表示させたテキストの色を変えるためには、引数:fg (=foreground)で色を指定します。
また、テキストを塗りつぶしたいときは、引数:bg (=background)で色を指定します。
ボタンウィジェットと関数実行
import tkinter as tk root = tk.Tk() root.title("ウィンドウタイトル") frame = tk.Frame(root) frame.grid() lbl = tk.Label(frame, text="テキストを表示させる") lbl.grid(column=0, row=0) # ボタンの表示+関数設置 btn_1 = tk.Button(frame, text="ボタン1") btn_2 = tk.Button(frame, text="ボタン2", command=lambda x="関数実行": print(x)) # ボタン配置 btn_1.grid(column=1, row=0) btn_2.grid(column=1, row=1) root.mainloop()
「決定」「キャンセル」「OK」「実行」「終了」など、ボタンをクリックして操作する機会は多々あります。
ボタンウィジェットには、ボタン押下時に関数を実行する引数:command が用意されています。
上記コードを実行すると、ボタンが2つ現れます。
- ボタン1:関数なし
- ボタン2:関数あり(”関数実行”という文字列を出力する関数)
画像に示したのは、ボタン2を3回押したときの状態です。
”関数実行”という文字列が3回出力されています。
この関数を変えることで、ボタンに任意の機能を持たせることができます。
テキスト入力欄とget()関数
import tkinter as tk root = tk.Tk() root.title("ウィンドウタイトル") frame = tk.Frame(root) frame.grid() lbl = tk.Label(frame, text="テキストを表示させる") lbl.grid(column=0, row=0) # 入力欄の表示 txtbox = tk.Entry(frame) # 入力欄配置 txtbox.grid(column=1, row=0) # get()関数で入力欄の内容を取得 btn = tk.Button(frame, text="ボタン", command=lambda: print(txtbox.get())) btn.grid(column=1, row=1) root.mainloop()
tk.Entry()により、フレーム上にテキストボックスを作成することができます。
例えば、ユーザーに氏名や住所、年齢、電話番号などを入力してもらう際に必要になります。
また、get()関数を使うことでテキストボックスに入力された内容を取得することも可能です。
上記コードを実行すると、テキストボックスとボタンが表示されます。
テキストボックス内に”入力内容を取得”と入力してボタンを押すと、”入力内容を取得”が出力されました。
フレーム移動(画面遷移)
import tkinter as tk # 1枚目のフレーム(ボタン押下で2枚目へ進む) def frame_1(): root.title("1枚目のフレーム") frame = tk.Frame(root) frame.grid(row=0, column=0, sticky=tk.NSEW) btn = tk.Button(frame, text="次のフレームへ", command=frame_2) btn.grid() frame.tkraise() # 2枚目のフレーム(指定時間で1枚目へ戻る) def frame_2(): root.title("2枚目のフレーム") frame = tk.Frame(root) frame.grid(row=0, column=0, sticky=tk.NSEW) lbl = tk.Label(frame, text="2秒後、フレームを戻します") lbl.grid() lbl.after(2000, frame_1) frame.tkraise() root = tk.Tk() # frame_1()を実行 frame_1() root.mainloop()
フレームチェンジをする場合は、各フレームで関数を作成します。
上記コードでは、1枚目のフレームでボタンを押すと2枚目のフレームへ移動して、移動した2秒後に1枚目のフレームへ戻ります。
ボタンを押してフレームを移動する方法が最も簡単で作りやすいと思います。
ウィジェットの配置方法
grid()関数
import tkinter as tk root = tk.Tk() root.title("ウィンドウタイトル") frame = tk.Frame(root) frame.grid() lbl = tk.Label(frame, text="テキストを表示させる") txtbox = tk.Entry(frame) btn = tk.Button(frame, text="ボタン", fg="red") # grid()関数でウィジェットを配置 lbl.grid(column=0, row=0) txtbox.grid(column=1, row=0) btn.grid(column=2, row=0) root.mainloop()
grid()関数は、Excelのような行・列でウィジェットの位置を指定する方法です。
- row:行
- column:列
最も整列力があり感覚的に配置することが可能です。
また、数行・数列にまたがって配置することも可能なため、汎用性は高いといえます。
pack()関数
import tkinter as tk root = tk.Tk() root.title("ウィンドウタイトル") frame = tk.Frame(root) frame.grid() lbl = tk.Label(frame, text="テキストを表示させる") txtbox = tk.Entry(frame) btn = tk.Button(frame, text="ボタン", fg="red") # pack()関数でウィジェットを配置 lbl.pack(side="top") txtbox.pack(side="left") btn.pack(side="right") root.mainloop()
pack()関数は、上下左右(top|bottom|left|right)で配置場所を指定する方法です。
特徴としては、指定する順番によってウィジェットの配置が変化する点が挙げられます。
上記コードの指定順を入れ替えると下図のようになります。
place()関数
import tkinter as tk root = tk.Tk() root.title("ウィンドウタイトル") root.grid_rowconfigure(0, weight=1) root.grid_columnconfigure(0, weight=1) frame = tk.Frame(root) frame.grid(row=0, column=0, sticky=tk.NSEW) lbl = tk.Label(frame, text="テキストを表示させる") txtbox = tk.Entry(frame) btn = tk.Button(frame, text="ボタン", fg="red") # place()関数でウィジェットを配置 lbl.place(x=50, y=20) txtbox.place(x=100, y=50, width=200) btn.place(x=200, y=100, width=100, height=50) root.mainloop()
place()関数は、フレーム内の座標を指定してウィジェットを配置する方法です。
特徴としては、「width:幅」と「height:高さ」を指定することでウィジェットの表示サイズを変更できる点が挙げられます。
ただし、place()関数でウィジェットを配置する場合、”ウィンドウ内のエリアサイズ変化”と”フレームのサイズ変化”を関連付ける必要があります。
そうすることで、「フレームのサイズ=基準となる座標系」となります。
ウィンドウを拡大・縮小させる、または、ウィンドウのサイズを指定することで、place()関数で指定した座標にウィジェットが表示されます。
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
grid_rowconfigure()は、ウィンドウ内の「行」に対してエリア変化率を指定できます。
grid_columnconfigure()は、ウィンドウ内の「列」に対してエリア変化率を指定できます。
これらの第一引数は場所を表します。「0」は0行目・0列目という意味になります。
引数:weightは、ウィンドウを拡大・縮小したときの第一引数におけるエリア変化率を指定できます。
よって、上記コードの意味は、
ウィンドウ内の0行・0列目に対して、エリア変化率を等倍に指定する
となります。
frame.grid(row=0, column=0, sticky=tk.NSEW)
上記コードは、フレームをgrid()関数で配置するときの記述です。
引数:row・columnは、フレームを配置するウィンドウのエリアを表しています。
引数:stickyは、フレームを配置したエリアが拡大・縮小したときに、フレームをどのように変化させるかを指定できます。
tk.NSEWは、上下左右(N:北|S:南|E:東|W:西)への変化を表しています。
他には、tk.NS:上下や tk.EW:左右といった指定も可能です。
簡単なツール作成:四則演算ツール
import tkinter as tk from decimal import Decimal #----------------------------------最初のフレーム---------------------------------- # フレーム作成 def home_frame(): # ウィンドウのタイトル指定 root.title("四則演算ツール") # # フレーム作成 frame = tk.Frame(root) frame.grid(row=0, column=0, sticky=tk.NSEW) # # ウィジェット作成 lbl = tk.Label(frame, text="四則演算ツール", font=(yu_font, 30)) btn1_1 = tk.Button(frame, text="足し算", font=(yu_font, 20), command=addition) btn1_2 = tk.Button(frame, text="引き算", font=(yu_font, 20), command=subtraction) btn1_3 = tk.Button(frame, text="掛け算", font=(yu_font, 20), command=multiplication) btn1_4 = tk.Button(frame, text="割り算", font=(yu_font, 20), command=division) # # ウィジェット配置 lbl.place(x=270, y=50) btn1_1.place(x=150, y=180, width=200, height=100) btn1_2.place(x=450, y=180, width=200, height=100) btn1_3.place(x=150, y=350, width=200, height=100) btn1_4.place(x=450, y=350, width=200, height=100) # # フレームを最上面へ frame.tkraise() # #----------------------------------------------------------------------------------- #----------------------------------足し算のフレーム---------------------------------- # フレーム作成 def addition(): # ウィンドウのタイトル指定 root.title("四則演算ツール|足し算") # # フレーム作成 frame = tk.Frame(root) frame.grid(row=0, column=0, sticky=tk.NSEW) # # ウィジェット作成 lbl_1 = tk.Label(frame, text="足し算", font=(yu_font, 30)) box_1 = tk.Entry(frame, font=(yu_font, 15)) lbl_2 = tk.Label(frame, text="+", font=(yu_font, 30)) lbl_3 = tk.Label(frame, text="⇓", font=(yu_font, 30)) box_2 = tk.Entry(frame, font=(yu_font, 15)) box_3 = tk.Entry(frame, font=(yu_font, 15)) btn = tk.Button(frame, text="計算", font=(yu_font, 20), command=lambda: box_3.insert(tk.END,str(func_1(box_3,box_1.get(),box_2.get())))) back = tk.Button(frame, text="ホームへ", command=home_frame) # # ウィジェット配置 lbl_1.place(x=300, y=50, width=200, height=40) box_1.place(x=100, y=150, width=200, height=40) lbl_2.place(x=375, y=150, width=50, height=40) lbl_3.place(x=375, y=230, width=50, height=40) box_2.place(x=500, y=150, width=200, height=40) box_3.place(x=300, y=300, width=200, height=40) btn.place(x=350, y=400, width=100, height=50) back.place(x=370, y=460, width=60, height=30) # # フレームを最上面へ frame.tkraise() # # 足し算 def func_1(arg1, arg2, arg3): dele = arg1.delete(0, tk.END) calc = Decimal(arg2) + Decimal(arg3) return calc #----------------------------------------------------------------------------------- #----------------------------------引き算のフレーム---------------------------------- # フレーム作成 def subtraction(): # ウィンドウのタイトル指定 root.title("四則演算ツール|引き算") # # フレーム作成 frame = tk.Frame(root) frame.grid(row=0, column=0, sticky=tk.NSEW) # # ウィジェット作成 lbl_1 = tk.Label(frame, text="引き算", font=(yu_font, 30)) box_1 = tk.Entry(frame, font=(yu_font, 15)) lbl_2 = tk.Label(frame, text="-", font=(yu_font, 30)) lbl_3 = tk.Label(frame, text="⇓", font=(yu_font, 30)) box_2 = tk.Entry(frame, font=(yu_font, 15)) box_3 = tk.Entry(frame, font=(yu_font, 15)) btn = tk.Button(frame, text="計算", font=(yu_font, 20), command=lambda: box_3.insert(tk.END,str(func_2(box_3,box_1.get(),box_2.get())))) back = tk.Button(frame, text="ホームへ", command=home_frame) # # ウィジェット配置 lbl_1.place(x=300, y=50, width=200, height=40) box_1.place(x=100, y=150, width=200, height=40) lbl_2.place(x=375, y=150, width=50, height=40) lbl_3.place(x=375, y=230, width=50, height=40) box_2.place(x=500, y=150, width=200, height=40) box_3.place(x=300, y=300, width=200, height=40) btn.place(x=350, y=400, width=100, height=50) back.place(x=370, y=460, width=60, height=30) # # フレームを最上面へ frame.tkraise() # # 引き算 def func_2(arg1, arg2, arg3): dele = arg1.delete(0, tk.END) calc = Decimal(arg2) - Decimal(arg3) return calc #----------------------------------------------------------------------------------- #----------------------------------掛け算のフレーム---------------------------------- # フレーム作成 def multiplication(): # ウィンドウのタイトル指定 root.title("四則演算ツール|掛け算") # # フレーム作成 frame = tk.Frame(root) frame.grid(row=0, column=0, sticky=tk.NSEW) # # ウィジェット作成 lbl_1 = tk.Label(frame, text="掛け算", font=(yu_font, 30)) box_1 = tk.Entry(frame, font=(yu_font, 15)) lbl_2 = tk.Label(frame, text="×", font=(yu_font, 30)) lbl_3 = tk.Label(frame, text="⇓", font=(yu_font, 30)) box_2 = tk.Entry(frame, font=(yu_font, 15)) box_3 = tk.Entry(frame, font=(yu_font, 15)) btn = tk.Button(frame, text="計算", font=(yu_font, 20), command=lambda: box_3.insert(tk.END,str(func_3(box_3,box_1.get(),box_2.get())))) back = tk.Button(frame, text="ホームへ", command=home_frame) # # ウィジェット配置 lbl_1.place(x=300, y=50, width=200, height=40) box_1.place(x=100, y=150, width=200, height=40) lbl_2.place(x=375, y=150, width=50, height=40) lbl_3.place(x=375, y=230, width=50, height=40) box_2.place(x=500, y=150, width=200, height=40) box_3.place(x=300, y=300, width=200, height=40) btn.place(x=350, y=400, width=100, height=50) back.place(x=370, y=460, width=60, height=30) # # フレームを最上面へ frame.tkraise() # # 掛け算 def func_3(arg1, arg2, arg3): dele = arg1.delete(0, tk.END) calc = Decimal(arg2) * Decimal(arg3) return calc #----------------------------------------------------------------------------------- #----------------------------------割り算のフレーム---------------------------------- # フレーム作成 def division(): # ウィンドウのタイトル指定 root.title("四則演算ツール|割り算") # # フレーム作成 frame = tk.Frame(root) frame.grid(row=0, column=0, sticky=tk.NSEW) # # ウィジェット作成 lbl_1 = tk.Label(frame, text="割り算", font=(yu_font, 30)) box_1 = tk.Entry(frame, font=(yu_font, 15)) lbl_2 = tk.Label(frame, text="÷", font=(yu_font, 30)) lbl_3 = tk.Label(frame, text="⇓", font=(yu_font, 30)) box_2 = tk.Entry(frame, font=(yu_font, 15)) box_3 = tk.Entry(frame, font=(yu_font, 15)) btn = tk.Button(frame, text="計算", font=(yu_font, 20), command=lambda: box_3.insert(tk.END,str(func_4(box_3,box_1.get(),box_2.get())))) back = tk.Button(frame, text="ホームへ", command=home_frame) # # ウィジェット配置 lbl_1.place(x=300, y=50, width=200, height=40) box_1.place(x=100, y=150, width=200, height=40) lbl_2.place(x=375, y=150, width=50, height=40) lbl_3.place(x=375, y=230, width=50, height=40) box_2.place(x=500, y=150, width=200, height=40) box_3.place(x=300, y=300, width=200, height=40) btn.place(x=350, y=400, width=100, height=50) back.place(x=370, y=460, width=60, height=30) # # フレームを最上面へ frame.tkraise() # # 割り算 def func_4(arg1, arg2, arg3): dele = arg1.delete(0, tk.END) calc = Decimal(arg2) / Decimal(arg3) return calc #----------------------------------------------------------------------------------- #---------------------------------------設定--------------------------------------- # ウィンドウ作成 root = tk.Tk() # ウィンドウの大きさを固定 root.minsize(width=800, height=500) root.maxsize(width=800, height=500) # ウィンドウ内部のエリア変化率を1:1に固定 root.grid_rowconfigure(0, weight=1) root.grid_columnconfigure(0, weight=1) # フォント指定 yu_font = "游ゴシック" #----------------------------------------------------------------------------------- home_frame() root.mainloop()
これまで説明してきた機能を使って簡易的な四則演算ツールを作成してみました。
ウィジェットの配置は全てplace()関数で指定しています。
また、計算には「decimal」を使用しています。
「decimal」は、十進数を正確に表現できるPythonライブラリです。
「decimal」を使用することで、皆さんが行なっている「普通の計算」をPython上で実現させることが可能になります。
外部リンク:decimal - Decimal fixed point and floating point arithmetic
root.minsize(width=600, height=400)
root.maxsize(width=600, height=400)
上記コードを記述することで、ウィンドウの最小サイズと最大サイズを指定することができます。
また、「最小サイズ=最大サイズ」とすることでウィンドウサイズを完全に固定できます。
特に、place()関数で作成したレイアウトが崩れてしまうのを防ぐ有効な手段といえます。
番外編:メニューの設置
import tkinter as tk root = tk.Tk() root.title("ウィンドウタイトル") frame = tk.Frame(root) frame.grid(row=0, column=0, sticky=tk.NSEW) # メニューの作成 menubar = tk.Menu(root) in_file = tk.Menu(root) # メニューの設置 root.config(menu=menubar) # メニューの中身作成 menubar.add_cascade(label="ファイル", menu=in_file) menubar.add_cascade(label="編集") menubar.add_cascade(label="表示") # 「ファイル」の中身作成 in_file.add_command(label="新規作成", command=lambda y="関数実行": print(y)) in_file.add_command(label="ファイルを開く") in_file.add_command(label="名前を付けて保存する") root.mainloop()
tk.Menu(tk.Tk())で、ウィンドウにメニューを設置することができます。
そのメニューに「ファイル」「編集」「表示」などを追加するためには、add_cascade()関数を使用します。
また、add_cascade()関数の引数:menuに、別変数に代入したtk.Menu(tk.Tk())を指定することで「ファイル」などの中にもメニューを作成できます。
上記コードでは、ファイル > 新規作成に関数をおいています。
新規作成をクリックすると、”関数実行”という文字列が出力されます。
ツールやアプリを作成する際には、この関数を新規作成に応じた機能を持つ関数に置き換えることで、「アプリ感」がより増してくるので使ってみてください。
まとめ
この記事では、簡単なツール・アプリを作るために必要な「tkinter」の機能を実行結果を交えながら説明し、それらの機能を使った簡易的なツールの作成を行ないました。
ここ数年で、小学生や高校生のプログラミング教育が必修化されるなど、今後のAI・IoTの時代を見据えた動きが活発になってきました。
また、最近では「ノーコード」による設計や開発なども耳にします。
未来に向けて世の中が変化し、新しい手法が次々と生まれていく現代において、Pythonのコーディング技術は武器になると思っています。
ノーコードツールも結局のところ、バックにはプログラムが回っていて、そのソースコードを書く人が必要になります。
今回のようなGUIを作成する知識や経験は、今後さらに重要なものになるでしょう。
ぜひ皆さんも、自分なりのGUI作品を作ってみてください。