【Python】Tkinterによる80行で作るGUIアプリ「簡易電卓」

# はじめに

こんにちは。
今回は**「簡易電卓」**を作っていきたいと思います。
完成するとこんな感じに動きます！


![簡易電卓.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309633/8b549290-9d05-fec3-a9b4-3c3e8d423263.gif)


# 環境
- Windows 10 home
- Python 3.7.1



# 簡易電卓の制作
今回はまず、完成したプログラムから書いていこうと思います。

## 完成したプログラム

```簡易電卓.py
import tkinter as tk

class Application(tk.Frame):
	def __init__(self,master):
		super().__init__(master)
		self.pack()

		master.geometry("700x155")
		master.title("簡易電卓")

		self.checkSym=False
		self.textNumber=""

		self.createButton()
		self.createCanvas()

		master.after(50, self.update)

	def createButton(self): #ボタンの配置
		tk.Button(text="AC",command=self.allclear,width=13).place(x=0, y=80)
		tk.Button(text="=",command=self.calc,width=13).place(x=100, y=80)

		tk.Button(text="+",command=lambda:self.buttonClick("+"),width=13).place(x=200, y=80)
		tk.Button(text="-",command=lambda:self.buttonClick("-"),width=13).place(x=300, y=80)
		tk.Button(text="×",command=lambda:self.buttonClick("×"),width=13).place(x=400, y=80)
		tk.Button(text="÷",command=lambda:self.buttonClick("÷"),width=13).place(x=500, y=80)
		tk.Button(text="%",command=lambda:self.buttonClick("%"),width=13).place(x=600, y=80)

		tk.Button(text="0",command=lambda:self.buttonClick(0),width=19).place(x=0, y=105)
		tk.Button(text="1",command=lambda:self.buttonClick(1),width=19).place(x=140, y=105)
		tk.Button(text="2",command=lambda:self.buttonClick(2),width=19).place(x=280, y=105)
		tk.Button(text="3",command=lambda:self.buttonClick(3),width=19).place(x=420, y=105)
		tk.Button(text="4",command=lambda:self.buttonClick(4),width=19).place(x=560, y=105)
		tk.Button(text="5",command=lambda:self.buttonClick(5),width=19).place(x=0, y=130)
		tk.Button(text="6",command=lambda:self.buttonClick(6),width=19).place(x=140, y=130)
		tk.Button(text="7",command=lambda:self.buttonClick(7),width=19).place(x=280, y=130)
		tk.Button(text="8",command=lambda:self.buttonClick(8),width=19).place(x=420, y=130)
		tk.Button(text="9",command=lambda:self.buttonClick(9),width=19).place(x=560, y=130)

	def buttonClick(self,txt):
		if txt == 0 and (self.textNumber == "" or self.textNumber[-1] == "+" or self.textNumber[-1] == "-" or self.textNumber[-1] == "×" or self.textNumber[-1] == "÷"): return

		if type(txt) == int:
			self.textNumber+=str(txt)
		elif self.textNumber != "" and type(txt) == str and self.checkSym == False:
			self.textNumber+=txt
			self.checkSym = True

	def calc(self):
		if self.textNumber.find("+") != -1 and self.textNumber[-1] != "+": self.textNumber=str(int(self.textNumber[:self.textNumber.find("+")]) + int(self.textNumber[self.textNumber.find("+")+1:]))
		elif self.textNumber.find("-") != -1 and self.textNumber[-1] != "-": self.textNumber=str(int(self.textNumber[:self.textNumber.find("-")]) - int(self.textNumber[self.textNumber.find("-")+1:]))
		elif self.textNumber.find("×") != -1 and self.textNumber[-1] != "×": self.textNumber=str(int(self.textNumber[:self.textNumber.find("×")]) * int(self.textNumber[self.textNumber.find("×")+1:]))
		elif self.textNumber.find("÷") != -1 and self.textNumber[-1] != "÷": self.textNumber=str(int(self.textNumber[:self.textNumber.find("÷")]) / int(self.textNumber[self.textNumber.find("÷")+1:]))
		elif self.textNumber.find("%") != -1 and self.textNumber[-1] != "%": self.textNumber=str(int(self.textNumber[:self.textNumber.find("%")]) % int(self.textNumber[self.textNumber.find("%")+1:]))
		self.checkSym=False

	def allclear(self):
		self.textNumber=""
		self.checkSym=False

	def createCanvas(self): #キャンバスの作成
		self.canvas = tk.Canvas(self.master,width=700,height=80,bg="black")
		self.canvas.pack()
		self.resultText = self.canvas.create_text(680,40,text=self.textNumber,fill="white",font=("Helvetica",30,"bold"),anchor="e")

	def update(self):
		self.canvas.delete(self.resultText)
		self.resultText = self.canvas.create_text(680,40,text=self.textNumber,fill="white",font=("Helvetica",30,"bold"),anchor="e")

		self.master.after(50,self.update)

def main():
	win = tk.Tk()
	win.resizable(width=False, height=False) #ウィンドウを固定サイズに
	app = Application(master=win)
	app.mainloop()

if __name__ == "__main__":
	main()
```

こちらが完成したプログラムになります。うまく動作したでしょうか。
ここから、**ウィンドウの作成と、createButtonメソッド、createCanvasメソッド**について説明していきます。


## ウィンドウの作成

サイズは**700×155**です。タイトル名は**簡易電卓**です。
ウィンドウの作り方の詳しい内容は[PythonによるTkinterを使った雛形（クラス化手法）](https://qiita.com/MichihiroTsukano/items/4d8721aaa59e1c913d9a)の記事をご参照ください。


## createButtonメソッドについて
このcreateButtonメソッドによって、**ボタンを配置**していきます。
ボタンをクリックすることでbuttonClickメソッドや、calcメソッド、allclearメソッドを呼んでいます。


### buttonClickメソッド


```py
def buttonClick(self,txt):
	if txt == 0 and (self.textNumber == "" or self.textNumber[-1] == "+" or self.textNumber[-1] == "-" or self.textNumber[-1] == "×" or self.textNumber[-1] == "÷"): return

	if type(txt) == int:
		self.textNumber+=str(txt)
	elif self.textNumber != "" and type(txt) == str and self.checkSym == False:
		self.textNumber+=txt
		self.checkSym = True
```

こちらがbuttonClickメソッドになります。引数にtxtを持っています。

一つ目の判定では受け取ったtxtが0であり、実際に表示されるテキストが空欄か計算記号だった場合はreturnをしています。
これは、**最初に入力する数字が0だったり、計算記号の次に入力する数字が0になることを防ぐため**です。

二つ目の判定では、受け取ったtxtの型がint型かstr型で判別しており、str型の方では、さらに実際に表示されるテキストが空欄でない状態とcheckSymがFalseの時に動作するようにしています。これは、**最初に計算記号を入力することを防ぐことと、計算記号を二個以上入力できないようにするため**です。
そして、それぞれ実際に表示されるテキストに**文字を追加**するといったプログラムになっています。


### calcメソッド

```py
def calc(self):
	if self.textNumber.find("+") != -1 and self.textNumber[-1] != "+": self.textNumber=str(int(self.textNumber[:self.textNumber.find("+")]) + int(self.textNumber[self.textNumber.find("+")+1:]))
	elif self.textNumber.find("-") != -1 and self.textNumber[-1] != "-": self.textNumber=str(int(self.textNumber[:self.textNumber.find("-")]) - int(self.textNumber[self.textNumber.find("-")+1:]))
	elif self.textNumber.find("×") != -1 and self.textNumber[-1] != "×": self.textNumber=str(int(self.textNumber[:self.textNumber.find("×")]) * int(self.textNumber[self.textNumber.find("×")+1:]))
	elif self.textNumber.find("÷") != -1 and self.textNumber[-1] != "÷": self.textNumber=str(int(self.textNumber[:self.textNumber.find("÷")]) / int(self.textNumber[self.textNumber.find("÷")+1:]))
	elif self.textNumber.find("%") != -1 and self.textNumber[-1] != "%": self.textNumber=str(int(self.textNumber[:self.textNumber.find("%")]) % int(self.textNumber[self.textNumber.find("%")+1:]))
	self.checkSym=False
```

こちらがcalcメソッドになります。この関数で計算を行います。
まず、**実際に表示されるテキストの中にある計算記号を見つけ、その記号の種類で判別をし、それぞれ計算をするプログラム**になります。


### allclearメソッド
この関数では、その名の通りなのですが、表示しているテキストを空欄にします。
つまり、削除するということです。

## createCanvasメソッドについて
この関数でスクリーンのようなキャンバスを作成し、そこに文字を実際に表示していきます。
updateメソッドで、随時表示しているテキストを更新している感じになります。


# おわりに

以上で「簡易電卓の制作」は終わりになります。
もっと簡単に作りたかったのですが、判定が少しややこしくなってしまいました。
いつもより説明を多めにしたので、よろしくお願いします。:wink:

ここまで読んでいただき、ありがとうございました。