今回はPythonによるプログラミングを行う.必要に応じて下記のサイトも参照.
参考サイト:情報処理実習1,Python入門
GUI とは「Graphical User Interface(グラフィカルユーザインターフェイス)」の略で,画面上のウィンドウやボタン,入力ボックスといった視覚的に操作できる部品で構成されるインターフェイスの総称である.
これにたいして,CUI「Character User Interface」は,文字ベースのコマンドの入力および出力を前提としたユーザーインターフェースである.例:VSCodeのターミナル,Windowsのコマンドプロンプトなど.
一般に,ユーザの操作や,プログラムがユーザに対して情報を提示する機能をユーザーインターフェイス(UI)と呼ぶ.
GUIの概念はプログラミング言語とは独立したものであるが,実際には言語によってその難易度,複雑さは大きく異なる.
ここでは,比較的容易にGUIを実装でき,Pythonの標準ライブラリであるtkinterを用いて,GUIの基本概念であるウインドウや,各種ウィジェット(ボタン,テキストボックス,ラジオボタン,コンボボックスなど)を,図とコード例で分かりやすく説明する.
(tkinter = Tk interface)
まずGUIの用語と役割を図で把握し,その後で実際に動く最小のコード例と,GUIを構成する各部品(ウィジェット)の使い方を学ぶ.
GUI で動作するソフトウエアの構築には,以下のような用語を理解しておくことが重要である.
tk.Tk() によって生成される.
アプリ全体の入れ物にあたり,タイトルバーや,最大化・最小化・閉じるボタンなどが含まれる.
すべてのウィジェットはこのウィンドウの中に置かれる.
command=関数名 として指定する.
root.mainloop() を呼ぶことでプログラムが GUI として動作し続ける.
GUI アプリは「ウィンドウ → コンテナ → ウィジェット」という階層で構成される.
基本的には,「イベント発生」 → 「コールバック関数が呼ばれる」 → 「画面に変化を提示」という流れで動作.
まずはウィンドウを表示する最小限のコードを示す.
正しくPython がインストールされている環境で実行すれば,画面内のどこかに小さなウィンドウが表示される.
これを,ルートウインドウという.
このウインドウを閉じると,プログラムが終了する.
d:\cprog20\myprog\> python 153R??????-??-?.py注意:GUIプログラムを実行すると,ウインドウがエディタの後ろに出現する場合があるので,よく探してみよう.
import tkinter as tk
root = tk.Tk() # ルートウィンドウを作る
root.title('GUI sample') # ウィンドウのタイトルを設定
root.geometry('400x250') # ウインドウの幅と高さを指定(pixel単位)
root.mainloop() # イベントループを開始(ここでウィンドウが表示され続ける)
root.title()などは,クラスのメンバ関数であり,メソッドと呼ばれる.
ここまででGUIの基盤となるウインドウを無事に開くことができたので,次は,よく用いられるウィジェットについて、ソースコードと実行結果を確認しながら,それぞれの特徴と基本的な使い方を解説する.
多くのGUIプログラムには,以下のような「OK」や「キャンセル」などのボタンが使われている.
import tkinter as tk
# ボタンが押された時の処理を記したコールバック関数
def on_click():
print(' You pressed button!') # ターミナルに文字を表示
# ウインドウを表示
root = tk.Tk()
root.title('Button sample')
root.geometry('400x250')
# ボタンを表示
btn = tk.Button(root, text='Button1', command=on_click) # ボタン名称や,コールバック関数を指定.
btn.pack(padx=10, pady=10) # ウイジェットをウインドウ内に配置
root.mainloop()
このプログラムでは,ボタンが押された場合に,on_click()が呼ばれる.
GUIプログラミングでは,ユーザーからの入力に対して何かの動作を開始するようなプログラムとなる.
このようなプログラムをイベント駆動型(event-driven)と呼ぶ.
イベントドリブンプログラミングでは,発生したイベントに対して,処理を関数として設定する.上の例では,引数command=の部分がこれに相当する.
このような関数をコールバック関数と呼ぶ.
コールバック関数の名前は任意であるが,一般的には on??? という名称をつけることが多い.
btn.pack()は,ボタンをウインドウに配置する関数である.
これとは別に,.place()を用いることで,x座標,y座標,幅や高さなどを細かく指定する方法もある.
上の例では,ターミナルにprint()で文字列を表示するが,GUIプログラムらしくウインドウ内に文字列を表示する場合は,ラベルを用いる.
import tkinter as tk
# ボタンが押された時の処理を記したコールバック関数
def on_click():
print('You pressed button!') # ターミナルに出力
label.config(text = 'OMG!') # ウインドウ内のラベlabelを更新
# ウインドウを表示
root = tk.Tk()
root.title('Button sample')
root.geometry('400x250')
# ボタンを表示
btn = tk.Button(root, text='Button1', command=on_click) # コールバック関数を指定.
btn.pack(padx=10, pady=10) # ウイジェットをウインドウ内に配置
# 文字列を表示(label)
label = tk.Label(root, text='Label here')
label.pack(pady=20)
root.mainloop()
padx, padyは余白のサイズである.この値を変更してどのように変化するか試してみよう. font=('',16)を追加してみよう.この整数値はフォントのサイズである.label = tk.Label(root, font=('',16), text='Label here')複数のボタンを設置する場合は,tk.Button()を複数回呼び出し,それぞれコールバック関数を用意し,pack(配置)する.
import tkinter as tk
# ボタン 1 が押された時の処理を記したコールバック関数
def on_click1():
print('Hello')
# ボタン 2 が押された時の処理を記したコールバック関数
def on_click2():
print('Good Bye')
root = tk.Tk()
root.title('Multiple Button sample')
root.geometry('400x250')
btn1 = tk.Button(root, text='Button1', command=on_click1)
btn1.pack(padx=10, pady=10)
btn2 = tk.Button(root, text='Button2', command=on_click2)
btn2.pack(padx=10, pady=10)
root.mainloop()
ユーザーから文字・数字などを入力させる(表示もできる)ウィジェットをテキストボックスと呼び,tkinter では Entry と呼ばれる.
import tkinter as tk
# ボタンが押された時の処理
def on_click():
print('txt =', e.get()) # Entryの中身を読み取ってターミナルに出力
root = tk.Tk()
root.title('Entry sample')
root.geometry('400x250')
# エントリを生成
e = tk.Entry(root) # Entryを生成
e.insert(tk.END, 'ここに入力') # 初期文字列を設定
e.update() # 文字表示を更新
e.pack()
# ボタンを生成
btn = tk.Button(root, text='Click me', command=on_click)
btn.pack(padx=10, pady=10)
root.mainloop()
e.get()は文字列を返すので,これを整数や実数に変換するには int(e.get()) または float(e.get()) とする.
このように,GUIプログラミングにおいては,ユーザからの何らかの入力に応じて,関数(イベントハンドラ)が呼ばれて処理が行われる点に特徴がある.
図のように,複数の選択肢の中から一つだけを選択したい場合がある.これをラジオボタンと呼ぶ.
(これに対して,チェックボックスでは,複数選択が可能.)
import tkinter as tk
# イベントハンドラ.
def show_selection():
label.config(text=f'選択中:{var.get()}')
root = tk.Tk()
root.title('Radiobutton Sample')
root.geometry('400x250')
# ウィジェット(ラジオボタン)用の変数
var = tk.StringVar(value='Pizza')
# ラジオボタン.同じイベントハンドラを指定
r1 = tk.Radiobutton(root, text='I like Pizza.', variable=var, value='Pizza', command=show_selection)
r2 = tk.Radiobutton(root, text='I like Pasta.', variable=var, value='Pasta', command=show_selection)
r3 = tk.Radiobutton(root, text='I like Penne.', variable=var, value='Penne', command=show_selection)
r1.pack(padx=20, pady=5)
r2.pack(padx=20, pady=5)
r3.pack(padx=20, pady=5)
# ラベル
label = tk.Label(root, text='1つを選択')
label.pack(pady=20)
root.mainloop()
ラジオボックスでは,ボタンのようにクリックされたときではなく,選択値が変更されたときに呼ばれるコールバック関数を設定する.
tk.StringVarは,ウィジェット用の特別な文字列変数である.
これを用いると,テキスト内容とウィジェットの表示とが自動で連動する.
ttk モジュールの Combobox は,あらかじめ用意されたリストから一つを選択するウィジェットである.
import tkinter as tk
from tkinter import ttk
# イベントハンドラ
def show_selected(event):
label.config(text=f'選択中:{cb.get()}')
print('You choose ' + cb.get())
root = tk.Tk()
root.title('Combobox sample')
root.geometry('400x250')
# コンボボックス
selected_value = tk.StringVar() # 選択された値を保存する変数
cb = ttk.Combobox(root, values=['Materials', 'Fluid Dynamics', 'Thermodynamics', 'Machine Dynamics'])
cb.current(0) # 初期の選択肢(0番目)
cb.pack(padx=20, pady=20)
cb.bind('<<ComboboxSelected>>', show_selected) # イベントハンドラを追加
# ラベル
label = tk.Label(root, text='1つを選択')
label.pack(pady=20)
root.mainloop()
ここまでの要素をまとめて,エントリおよびコンボボックスを選択して,返信の文字列を表示する簡単なフォームを作ってみよう.
このプログラムは,テキストボックスに名前を入力し,コンボボックスから機械工学4力学の科目名を選択し,ボタンをクリックするとメッセージを表示する.
import tkinter as tk
from tkinter import ttk
# ボタンがクリックされた時のイベントハンドラ
def submit():
name = entry_name.get()
subject = combo.get()
if not name:
label_result.config(text='Please input your name!')
return
label_result.config(text=f'Welcome {name}. You like {subject}')
root = tk.Tk()
root.title('Form sample')
root.geometry('300x250')
# 名前を入力(エントリ)
tk.Label(root, text='Name').grid(row=0, column=0, padx=6, pady=6, sticky='w') # 表示用ラベル
entry_name = tk.Entry(root)
entry_name.grid(row=0, column=1, padx=6, pady=6)
# 好きな科目を選択(コンボボックス)
tk.Label(root, text='Subject').grid(row=1, column=0, padx=6, pady=6, sticky='w') # 表示用ラベル
combo = ttk.Combobox(root, values=['Materials', 'Fluid Dynamics', 'Thermodynamics', 'Machine Dynamics'])
combo.current(0)
combo.grid(row=1, column=1, padx=6, pady=6)
# 送信ボタン
btn = tk.Button(root, text='Submit', command=submit)
btn.grid(row=2, column=0, columnspan=2, pady=10)
# メッセージ表示用(ラベル)
label_result = tk.Label(root, text='')
label_result.grid(row=3, column=0, columnspan=2, pady=6)
root.mainloop()
このサンプルでは,ウィジェットの配置に pack()よりもより細かなコントロールが可能なgrid() を使っている.
詳細については,公式ドキュメントおよび下記のウィジェット配置方法の説明を参照.
tkinterでは,ウィジェットを画面上に配置するために,主に pack,grid,place の3つの方法が用意されている.
それぞれの特徴と使い分けを以下に示す.
| 方法 | 特徴 | 主な用途 | 難易度 |
|---|---|---|---|
| pack | 追加順に上下・左右へ配置 | 簡単なGUI,縦並びレイアウト | 低 |
| grid | 行・列で表形式に配置 | 入力フォーム,設定画面 | 中 |
| place | 座標を指定して配置 | 微調整,実験用 | 高 |
pack は最も簡単な配置方法で,ウィジェットを追加した順に自動的に配置する.
import tkinter as tk
root = tk.Tk()
root.title('pack の例')
tk.Label(root, text='Label 1').pack(pady=5) # packをまとめて書く方法
tk.Label(root, text='Label 2').pack(pady=5)
tk.Button(root, text='Button').pack(pady=5)
root.mainloop()
packの主なオプション:
side:配置方向(TOP, BOTTOM, LEFT, RIGHT)padx, pady:余白fill:領域の埋め方expand:余白を広げるかどうかgrid は行 (row) と列 (column) を指定して配置する方法で,フォーム形式のGUIに適している.
import tkinter as tk
root = tk.Tk()
root.title('grid sample')
tk.Label(root, text='名前').grid(row=0, column=0, padx=5, pady=5)
tk.Entry(root).grid(row=0, column=1, padx=5, pady=5)
tk.Label(root, text='学生番号').grid(row=1, column=0, padx=5, pady=5)
tk.Entry(root).grid(row=1, column=1, padx=5, pady=5)
tk.Button(root, text='送信').grid(row=2, column=0, columnspan=2, pady=10)
root.mainloop()
注意: 同一コンテナ内で pack と grid は併用できない.どちらかのみを使用.
place は座標値を直接指定してウィジェットを配置する.
細かい制御が可能だが,ウィンドウサイズ変更に弱いという欠点がある.
import tkinter as tk
root = tk.Tk()
root.geometry('300x200')
tk.Button(root, text='左上').place(x=20, y=20)
tk.Button(root, text='中央').place(relx=0.5, rely=0.5, anchor=tk.CENTER) # ウインドウ中央に固定
tk.Button(root, text='右下').place(x=200, y=150)
root.mainloop()
GUIプログラミングでは,画面構成に応じて適切な配置方法を選ぶことが重要で,ユーザーインターフェースの設計(各ウィジェットの配置,直感的なレイアウト,ウィジェットのサイズなど)が,そのアプリケーションの使いやすさに直結する.
ウインドウ内に[ADD]と[SUB]ボタンを設置し,それぞれをクリックすると変数の数値を +1, -1 してターミナルに表示せよ.
ヒント:ADDボタンの設置とコールバック関数の実装
import tkinter as tk
sum = 0 # global 変数
# ADDボタンが押された時のコールバック関数
def on_click_add():
global sum # global 変数を変更するためのおまじない
sum = sum+1
print('sum =', sum)
# SUBボタンが押された時のコールバック関数
# ここに追加(1)
root = tk.Tk()
root.title('Add/Sub sample')
root.geometry('400x250')
# ボタンの生成と配置
btn1 = tk.Button(root, text='ADD', command=on_click_add)
btn1.pack(padx=10, pady=10)
# ここに追加(2)
root.mainloop()
実行例
sum = 1 [ADD をクリック]
sum = 2 [ADD をクリック]
sum = 3 [ADD をクリック]
sum = 2 [SUB をクリック]
実数a, bをテキストボックス(エントリ)で入力し,ボタンをクリックすると,$\sqrt{a^2+b^2}$ を計算し,label として表示するプログラムを作成せよ.
テキストボックス2個に身長と体重を入力し,「計算」ボタンをクリックすると,BMIの計算をしてウインドウ内に表示するプログラムを作成せよ.
参考:WHO Body mass index (BMI),
東京都保健医療局