以前やりましたが、今度はtkinterでBMIを計算してみます。
なお、イラストはこちらのサイトのものを使用しています → https://www.ac-illust.com/
画面レイアウト
ウィンドウにFrameウィジェットをpackで配置し、そのFrameの中に色々なウィジェットをgridで配置しています。
Entryウィジェットの利用と、Labelウィジェットに画像を表示する方法は初めてですので後述します。
フォルダ構成
1 2 3 4 5 6 7 |
任意のフォルダ/ ├ main.py(pythonファイル) └ img/(画像フォルダ) ├ 1.png ├ 2.png ├ 3.png └ 4.png |
ソースコード
今回はFrameウィジェットを継承したクラスを使用して書きました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
""" BMI ボディマス指数計算 """ from tkinter import * class App(Frame): """tkinterのFrameクラスを継承して作成""" def __init__(self, master=None): """コンストラクタ""" # Frameクラスのコンストラクタを呼び super().__init__(master, padx=10, pady=10) # root(masterはrootを代入)にpackして配置 # packはウィジェットを行または列に配置します # この場合rootの真ん中にFrameウィジェットが配置された状態です self.pack() # 最初とリセットを押した時に表示する文字列 self.initial = "身長と体重を入力して\nボタンを押して下さい\n" # self.lab6で使う文字列変数を設定 self.var1 = StringVar() # 最初はself.initialを代入 self.var1.set(self.initial) # 以降、Frameにウィジェットをgridで配置 self.lab1 = Label(self, text="BMI ボディマス指数計算") self.lab1.grid(row=0, column=0, columnspan=4) self.lab2 = Label(self, text="身長") self.lab2.grid(row=1, column=0) # Entryウィジェットはテキスト入力 justify=RIGHTで右寄せにしている self.ent1 = Entry(self, justify=RIGHT) self.ent1.grid(row=1, column=1, columnspan=2) # 最初にent1にフォーカスさせる self.ent1.focus() # bindメソッドでEnterボタンを押したときにもself.calcを呼び出す self.ent1.bind("<Return>", self.calc) self.lab4 = Label(self, text="cm") self.lab4.grid(row=1, column=3) self.lab3 = Label(self, text="体重") self.lab3.grid(row=2, column=0) self.ent2 = Entry(self, justify=RIGHT) self.ent2.grid(row=2, column=1, columnspan=2) # bindメソッドでEnterボタンを押したときにもself.calcを呼び出す self.ent2.bind("<Return>", self.calc) self.lab5 = Label(self, text="kg") self.lab5.grid(row=2, column=3) # ボタンを押した時にself.calcメソッドを呼ぶ self.btn1 = Button(self, text="計算", command=self.calc) self.btn1.grid(row=3, column=1) # ボタンを押した時にself.clearメソッドを呼ぶ self.btn2 = Button(self, text="リセット", command=self.clear) self.btn2.grid(row=3, column=2) # textvariableでself.var1が変わればLabelのテキストも変わる self.lab6 = Label(self, textvariable=self.var1) self.lab6.grid(row=4, column=0, columnspan=4) # PhotoImageのオブジェクトを作成 self.img = PhotoImage(file="img/1.png") # image=self.imgでPhotoImageのオブジェクトを表示させる self.lab7 = Label(self, image=self.img) self.lab7.grid(row=5, column=0, columnspan=4) def calc(self, _=None): """計算ボタンが押されたら発動 Buttonのcommandとbindから呼ばれる bindの場合event引数も受けなければいけないが このメソッドでは使わないので_=Noneにしている 何が入力されるかわからないのでtry~exceptで例外処理""" try: # Entryの内容を取得 height = float(self.ent1.get()) weight = float(self.ent2.get()) # BMIを計算 bmi = round(weight / ((height / 100) * (height / 100)), 2) # 適正体重を計算 right_weight = round(((height / 100) * (height / 100)) * 22, 2) # if文で分岐 if bmi < 18.5: result = "低体重" # PhotoImageのオブジェクトのfileを変えて self.img["file"] = "img/2.png" # self.lab7のimageを変える self.lab7["image"] = self.img elif 18.5 <= bmi < 25: result = "普通体重" self.img["file"] = "img/3.png" self.lab7["image"] = self.img elif 25 <= bmi < 30: result = "肥満(1度)" self.img["file"] = "img/4.png" self.lab7["image"] = self.img elif 30 <= bmi < 35: result = "肥満(2度)" self.img["file"] = "img/4.png" self.lab7["image"] = self.img elif 35 <= bmi < 40: result = "肥満(3度)高度肥満" self.img["file"] = "img/4.png" self.lab7["image"] = self.img elif 40 <= bmi: result = "肥満(4度)高度肥満" self.img["file"] = "img/4.png" self.lab7["image"] = self.img ans = f"あなたのBMIは{bmi}です。\n{result}です。\n適正体重は{right_weight}kgです。" # self.var1に文字列をセット self.var1.set(ans) except ValueError: # エラーメッセージ self.var1.set("ValueError\n半角の数字を入力して下さい\n") except ZeroDivisionError: # エラーメッセージ self.var1.set("ZeroDivisionError\n0で割れません\n") def clear(self): """リセットボタンが押されたら発動""" self.img["file"] = "img/1.png" self.lab7["image"] = self.img self.var1.set(self.initial) # Entryに書いている文字の最初[0]から最後まで消すという意味 self.ent1.delete(0, END) self.ent2.delete(0, END) self.ent1.focus() # Tkクラスをインスタンス化 root = Tk() # ウィンドウのタイトル指定 root.title("BMI") # すべてのウィジェットのフォント指定 root.option_add("*Font", "メイリオ 12") # Appクラスをインスタンス化 app = App(master=root) # mainloopメソッドでメインループを呼び出しイベントを待つ root.mainloop() |
146行目でTkクラスをインスタンス化して、148行目でウィンドウのタイトルを指定し、150行目でフォントを指定し、152行目でAppクラスをインスタンス化し、154行目のmainloopメソッドでメインループを呼び出しイベントを待ちます。
7行目class App(Frame):でtkinterのFrameクラスを継承してAppクラスを定義します。
Frameウィジェットは、それ自体は見えないウィジェットですが、その中に他のウィジェットを配置して使います(HTMLのdivタグみたいなもの)。
13行目でFrameクラスのコンストラクタを呼び、17行目で自らをpackで配置します。
packでの配置は、言葉では説明しづらいのですが、標準では縦中央に配置します。packの次にまたpackすると、その下に配置されます。オプションで左よせ、右よせ、上よせ、下よせなどができます。
34行目でEntryウィジェットが出てきます。Entryウィジェットは1行のテキスト入力です(HTMLのinput type=”text”みたいなもの)。複数行テキスト入力の場合はTextウィジェットを使います。
37行目。focusメソッドでEntryウィジェットにフォーカスを当てます。focusはfocus_setの別名ですのでfocus_set()を使用しても同じです。
39行目。bind(“<Return>”, self.calc)とは、Enterキーを押すイベントと、メソッドのcalcを呼ぶことをバインド(縛る)します。つまりEnterキーを押すとメソッドcalcを呼びます。
イベントには下の他にも色々あります。
<Button-1> マウス左クリック
<Button-3> マウス右クリック
<Double-Button-1> マウス左ダブルクリック
<space> spaceキーが押された時
<Control-Key-f> Ctrlキーとfキーが押された
などなど
56行目。command=self.calcでボタンが押された時にcalcメソッドを実行します。
64行目。22行目で定義したStringVarの変数をLabelの内容に関連付けます。
68行目。PhotoImageオブジェクトを作成。
71行目。LabelにPhotoImageオブジェクトを表示する。jpg画像は別の方法でないと表示できません(PILを使用)。
74行目。計算ボタンが押されるか、Enterボタンが押された時に発動するメソッド。
82行目。Entryの内容を取得するのはget()メソッド。
85行目。BMIを計算。
87行目。適正体重を計算。
93行目。PhotoImageのオブジェクトのfileを変更。
95行目。lab7のオブジェクトのimageを変更。
140行目。Entryの文字を消すには、delete(0, END)。
実行結果の画像