今回は電卓アプリを制作するためのUI実装編です。
PythonのTkinterを使用して、電卓の見た目を作成していきましょう!
いよいよプログラミングに入ります!
一緒に作っていきましょー!!
事前準備
Pythonのプログラミング環境が整っていないかたは、
以下の記事を参考に準備を整えていきましょう!
今回はUIを表示するためのTkinterの話題が中心ですので、
確認しておくとスムーズに進められます。
実装する仕様は以下の記事になります。
UI実装の方針
電卓のUIは4要素に分けることができます。
以下を順番に実装していきます。
- ウィンドウ
電卓の土台。図の水色の部分。
この上にボタンやラベルなどの要素を配置。
- フレーム
ウィンドウの中のどの範囲にラベルとボタンを置くかを定義。
見えない枠というイメージで使用。
- ラベル
入力した数値や計算結果を表示。
図の①計算結果の表示の部分。
- ボタン
数値や演算子などを入力。
図の②~⑤の部分。4×4個を規則正しく配置
規則正しく配置するために、Tkinterのgridを使用していきます。
ウィンドウの実装
まずはUIの土台となるウィンドウを実装します。
サイズは380×500、色は水色の#a0d8efとしてます。
import sys
import tkinter
import tkinter.font
# UIの作成.
def make_ui():
window = tkinter.Tk()
# タイトル
window.title("電卓")
# サイズ
window.geometry("380x500")
window.resizable(False, False)# ウィンドウサイズを変更不可にする.
# 色
window.config(bg="#a0d8ef")
window.update_idletasks()
# ウィンドウのループ処理
window.mainloop()
def main():
# UI作成.
make_ui()
if __name__ == "__main__":
main()
実行すると以下のような画面が表示されます。
これでウィンドウの実装は完了です。
フレームの実装
次はラベルとボタンを置く範囲を決めるフレームを配置します。
見えないUIにするため、見た目はウィンドウ実装後と変わりません。
make_ui()を以下のように書き換えます。
#空のFrameを作成という部分が追加されています。
# UIの作成.
def make_ui():
window = tkinter.Tk()
# タイトル.
window.title("電卓")
# サイズ.
window.geometry("380x500")
window.resizable(False, False)
# 色.
window.config(bg="#a0d8ef")
window.update_idletasks()
# 空のFrameを作成.
empty_frame = tkinter.Frame(window, width=100, height=100, bg="#a0d8ef")
empty_frame.grid(row=0, column=0, padx=10, pady=10)
# ウィンドウのループ処理.
window.mainloop()
ラベルの実装
次は入力した数値や計算結果を表示するラベルの実装です。
ラベルはボタンを押したときに頻繁にアクセスする変数のため、
make_ui以外でも呼び出せるようにグローバル変数として定義しておきます。
importの下あたりに書いておきましょう。
# import tkinter.fontの下に追加.
# グローバル変数.
g_result_label = None
ラベルやボタンを規則正しく配置するために、
Tkinterのgrid(グリッド)を使用して並べていきます。
ラベルはボタン4個分の長さが必要なので、columnspan=4に設定する点がポイントです。
フレームの下に以下のようにコードを追加します。
# 空のFrameを作成
# ~ 省略 ~
# 入力した数値・計算結果.
global g_result_label
g_result_label = tkinter.Label(empty_frame, text=str(0), height=5)
g_result_label.grid(columnspan=4, row=0, column=0, padx=5, pady=5, sticky=tkinter.W + tkinter.E)
フレームの上に配置するので、引数にはフレームのインスタンスであるempty_frameを設定します。
text=str(0)を入れて、初期値は0を入れておきましょう。
実行すると以下のような表示になります。
横の幅が足りないように見えますが、
次章のボタンを配置するとその横幅に合わせて伸びていくので問題ありません。
ボタンの実装
最後にボタンの実装です。
ボタンの実装では以下5点を進めていきます。
- 種類の定義
- ボタンの配置
- 文字列の定義
- 色の定義
- 押した時の処理
種類の定義
まずはボタンの種類を定義にして、何のボタンの処理かを判別できるように
していきます。
『enum』という書き方を使用しますので、importに以下を追加しましょう。
from enum import Enum
以下のようにボタンの定義を作成します。
ボタンごとに異なる数値を割り当てることで、判別ができるようにします。
# ボタンの種類.
class BtnType(Enum):
BTN_NOT = -1
BTN_0 = 0
BTN_1 = 1
BTN_2 = 2
BTN_3 = 3
BTN_4 = 4
BTN_5 = 5
BTN_6 = 6
BTN_7 = 7
BTN_8 = 8
BTN_9 = 9
BTN_PLUS = 10
BTN_MINUS = 11
BTN_DIV = 12
BTN_MUL = 13
BTN_EQUAL = 14
BTN_AC = 15
演算子や数値などのボタンの種類を判別する関数も作っていきましょう。
# 数値の判定.
def is_number(btn_type):
value_list = {
BtnType.BTN_0: '0',
BtnType.BTN_1: '1',
BtnType.BTN_2: '2',
BtnType.BTN_3: '3',
BtnType.BTN_4: '4',
BtnType.BTN_5: '5',
BtnType.BTN_6: '6',
BtnType.BTN_7: '7',
BtnType.BTN_8: '8',
BtnType.BTN_9: '9',
}
if btn_type in value_list:
return True
return False
# 演算子の判定.
def is_operator(btn_type):
operator_list = {
BtnType.BTN_DIV: '÷',
BtnType.BTN_MUL: '×',
BtnType.BTN_MINUS: '-',
BtnType.BTN_PLUS: '+',
}
if btn_type in operator_list:
return True
return False
# =の判定.
def is_equal(btn_type):
return btn_type == BtnType.BTN_EQUAL
# All Clearの判定.
def is_all_clear(btn_type):
return btn_type == BtnType.BTN_AC
ボタンの配置
次にボタンを表示していきます。
まずは文字や色はつけずに白い四角いボタンを実装してみましょう。
make_ui()内に以下のコードを追加します。
# 入力した数値・計算結果.
# ~ 省略 ~.
# ボタン.
btn_list = dict()
btn_list[0] = [BtnType.BTN_7, BtnType.BTN_8, BtnType.BTN_9, BtnType.BTN_PLUS] # 1行目.
btn_list[1] = [BtnType.BTN_4, BtnType.BTN_5, BtnType.BTN_6, BtnType.BTN_MINUS] # 2行目.
btn_list[2] = [BtnType.BTN_1, BtnType.BTN_2, BtnType.BTN_3, BtnType.BTN_MUL] # 3行目.
btn_list[3] = [BtnType.BTN_0, BtnType.BTN_AC, BtnType.BTN_EQUAL, BtnType.BTN_DIV]# 4行目.
# ボタンをgridで配置.
for k, v in btn_list.items():
row = k + 1
column = 0
for i in v:
btn = tkinter.Button(empty_frame
, height=5
, width=10
)
btn.grid(rowspan=1, row=row, column=column, padx=5, pady=5)
column += 1
btn_listにはBtnTypeをボタンのUI仕様にある配置通りに入れています。
その後のループで、上側のボタンから順番に作成しています。
for k, v in btn_list.items():が行のループで、for i in v:が列のループになります。
実行すると以下のように、白いボタンが表示されます。
計算結果を表示するラベルもボタンに合わせて横幅が伸びましたね!
文字列の定義
ボタン上に表示する文字列を取得できるように関数を作成します。
先ほど作成したBtnTypeの定義とひもづけて取得できるようにします。
# 文字列の取得.
def get_text(btn_type):
text_list = {
BtnType.BTN_0: '0',
BtnType.BTN_1: '1',
BtnType.BTN_2: '2',
BtnType.BTN_3: '3',
BtnType.BTN_4: '4',
BtnType.BTN_5: '5',
BtnType.BTN_6: '6',
BtnType.BTN_7: '7',
BtnType.BTN_8: '8',
BtnType.BTN_9: '9',
BtnType.BTN_PLUS: '+',
BtnType.BTN_MINUS: '-',
BtnType.BTN_MUL: '×',
BtnType.BTN_DIV: '÷',
BtnType.BTN_EQUAL: '=',
BtnType.BTN_AC: 'AC',
}
if btn_type not in text_list:
return ''
return text_list[btn_type]
ボタンの作成時にも忘れずに設定しましょう。
get_textをtkiter.Buttonの引数に追加します。
# ボタンをgridで配置.
for k, v in btn_list.items():
row = k + 1
column = 0
for i in v:
btn = tkinter.Button(empty_frame
, height=5
, width=10
, text=get_text(i) # 文字列の設定.
)
btn.grid(rowspan=1, row=row, column=column, padx=5, pady=5)
column += 1
電卓っぽくなってきましたね!
色の定義
ボタンごとの色を取得できる関数を作成していきます。
仕様としては以下のような配色にしていたので、そちらに合わせて作っていきます。
種類 | ボタンの色 | ボタンの文字色 |
---|---|---|
数値 | ■白 | ■黒 |
演算子 | ■青 | ■白 |
= | ■黄 | ■黒 |
AC | ■赤 | ■黒 |
まずはボタンの色からです。色はカラーコードで定義します。
# ボタンの色を取得.
def get_btn_color(btn_type):
# 演算子:青.
if is_operator(btn_type):
return '#19448e'
# =:黄.
if is_equal(btn_type):
return '#ffd900'
# AC:赤.
if is_all_clear(btn_type):
return '#e60033'
# その他:白.
return '#ffffff'
次に文字の色。
# 文字の色を取得.
def get_text_color(btn_type:BtnType):
if is_operator(btn_type):
return '#ffffff'
# その他:黒.
return '#000000'
ボタンと文字の色もtkiter.Buttonの引数に追加します。
btn = tkinter.Button(empty_frame
, height=5
, width=10
, text=get_text(i) # 文字列の設定.
, bg=get_btn_color(i) # ボタンの色.
, foreground=get_text_color(i) # 文字の色.
)
見た目は、ほとんど仕様通りにできましたね!
ボタンの文字サイズはTkinterでの調整は面倒なので今回はパス!
押した時の処理の実装
最後にボタンを押したときの処理を実装します。
具体的な内容は機能実装の際に作成しますので、形だけ作成していきます。
ボタンを押したときに呼び出す関数を作る関数を作成します。ややこしい
BtnTypeを引数にしてどのボタンの処理かを判別できるようにしましょう。
# ボタンを押したときの処理を返す関数.
def push_btn_command(btn_type):
def callback():
print("push:" + str(btn_type))
return callback
クロージャーと呼ばれる関数の中に関数を定義する特殊な書き方をしています。
この書き方は、値を保持できる関数を作成するときに有効です。
今回なら、BtnTypeを保持した関数を作れば、ボタンごとの処理を分けられるという狙いです。
※おまけで具体的な挙動を解説
作成したpush_btn_commandをボタンに登録します。
btn = tkinter.Button(empty_frame
, height=5
, width=10
, text=get_text(i) # 文字列の設定.
, bg=get_btn_color(i) # ボタンの色.
, foreground=get_text_color(i) # 文字の色.
, command=push_btn_command(i) # ボタンが押されたときの処理.
)
ここまで対応できたら、一度実行してみましょう。
ボタンを押して、実行結果の画面にpush:”押したBtnType”が表示されたら、OKです!
# 6を押したとき.
push:BtnType.BTN_6
# ACを押したとき.
push:BtnType.BTN_AC
お疲れさまでした!
まとめ
今回は電卓アプリを制作するためのUI実装編でした。
見た目の変化が大きい実装なので、わかりやすくて面白いですよね。
画面に何かを表示する実装が好きなかたは、
少し複雑にはなりますがエフェクトやアニメーションを扱う実装も楽しめるかもしれませんよ?
次回は裏方の処理である電卓の機能実装をしていきます。
完成まであと少し!
おまけ
クロージャーとは?
クロージャーとは関数の中で関数を定義し、中で定義した関数自体を返り値とする書き方です。
ざっくりと関数を作って返す機能を持つ関数の一種と言えるでしょう。
中で定義された関数は内部関数(クロージャー)と呼び、外側は外部関数と呼びます。
メリットとして、内部関数は外部関数で使う変数を保持し続けることができます。
ボタンを押したときに使ったコードを例に見ていきましょう。
def push_btn_command(btn_type): # 外部関数.
def callback(): # 内部関数(クロージャー).
print("push:" + str(btn_type))
return callback
btn_command = push_btn_command(5)
btn_command()# push:5.
btn_command()# push:5.
btn_command = push_btn_command(7)
btn_command()# push:7.
btn_command()# push:7.
push_btn_commandは、内部関数callbackを定義してreturnする関数です。
callback内では外部関数push_btn_commandの引数であるbtn_typeを使用しています。
実行すると…
引数が5の時はpush:5というログが表示され、引数を7にするとpush:7が表示されます。
このように内部関数callback内で使用している外部関数push_btn_commandの変数を
保持していることがわかります。
今回はこの特徴を活用して、ボタンのIDを保持した関数を作成しました。
おわり