【Python】Tkinterによる150行で作るGUIアプリ「じゃんけんゲーム」

# はじめに
こんにちは。今回は「GUIじゃんけんゲーム」を制作しました。
完成するとこんな感じに動きます！
![じゃんけんゲーム.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309633/862e4431-a335-1852-e5b6-59f4b4ebe516.gif)



以前作った[**英単語ソフトの記事**](https://qiita.com/MichihiroTsukano/items/20e67c92bd077b095885)で、プログラムの構成について、アドバイスとプログラムの見直までしていただきました。本当にありがとうございます。そこで、この「**GUIじゃんけんゲーム**」でそれを実践しようと思い、作り始めました。

アドバイスの内容は以下の通りになります。
>GUIアプリは、処理を層に分けると可読性が高くなると思います。
有名な分け方にMVC(Model-View-Controller)があります。
Modelはデータ、Viewはデータ表示、Controllerはデータ操作と表示との橋渡しを担当します。
GUI部品はModelでもありViewでもありControllerでもあったりするので、分離が難しいのですが、Viewでデータ処理しないように気を付けることはできます。

アドバイスの内容通り、**MVCモデル**で実装していきたいと思います。初めてのことなので、うまくできていない部分があったらすみません。


# 環境
- Windows 10 home
- Python 3.7.1


ファイルの構成は以下のようになります。

```

├──じゃんけんゲーム/
      ├── 素材/
      │　　├── グー.png
      │　　├── チョキ.png
      │　　└── パー.png
      └── じゃんけんゲーム.py

```



# GUIじゃんけんゲーム
ここから、ウィンドウの作成、データ、データ表示、データ操作の順に記載していきます。
最後の方に完成したプログラムを載せるので、まず実行したい方は一気にそっちを見てください！


```py

import tkinter as tk
import random as rd
```
使うライブラリはこの二つになります。


## ウィンドウの作成

まず、ウィンドウの作成からになります。
[**雛形**](https://qiita.com/MichihiroTsukano/items/4d8721aaa59e1c913d9a)の記事を基本に作っていきます。

```py

import tkinter as tk

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

        master.geometry("350x300")
        master.title("じゃんけんゲーム")

        self.pack()

def main():
    win = tk.Tk()
    app = Application(master = win)
    app.mainloop()


if __name__ == "__main__":

    main()

```

このプログラムでウィンドウを作っていきます。
ウィンドウサイズは300×350。
タイトルはじゃんけんゲームにしています。


## データ
次に、グー、チョキ、パーの画像を入れます。
この素材に関しては[**Piskel**](https://www.piskelapp.com/)で制作しました。



![グー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309633/b68fadfd-f527-5a32-62b0-088267cff28d.png)　　　　　![チョキ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309633/bd9f711e-26f5-daa5-0315-83de9654db99.png)　　　　　![パー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309633/15425aa6-5e25-d07a-8ea4-193cea37a79b.png)


```py

Player = []
Enemy = []

Player.append(Image("グー.png", 25, 220, 3))
Player.append(Image("チョキ.png", 120, 205, 3))
Player.append(Image("パー.png", 200, 220, 3))

Enemy.append(Image("グー.png", 100, 25, 2))
Enemy.append(Image("チョキ.png", 100, 15, 2))
Enemy.append(Image("パー.png", 100, 25, 2))

```
それぞれ、main関数内で、PlayerとEnemyのリストに入れていきます。


## データ表示

データの表示に関してはApplicationクラスが処理をします。
main関数でインスタンスappを生成しています。

```py

app = Application(Player, Enemy, master = win)
```


## データ操作

データ操作はmain関数で処理していきます。

```py

app.RButton["command"] = RJudge
app.SButton["command"] = SJudge
app.PButton["command"] = PJudge
app.RetryButton["command"] = RetryJudge
```
上記がボタンをクリックすると、それぞれに対応する関数が反応するようなコードになります。




# 完成したプログラム
こちらが完成したプログラムになります。


```じゃんけんゲーム.py
import tkinter as tk
import random as rd

class Image():
	def __init__(self, image, x, y, z):
		self.image = tk.PhotoImage(file = "素材/" + image).subsample(z)
		self.x = x
		self.y = y

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

		master.geometry("300x350")
		master.title("じゃんけんゲーム")

		self.Player = player
		self.Enemy = enemy
		self.RandCounter = 0
		self.JudgeNumber = 0
		self.EnemyLabel = []

		for i in range(3):
			tk.Label(master, image = self.Player[i].image).place(x = self.Player[i].x, y = self.Player[i].y)

		for i in range(3):
			self.EnemyLabel.append(tk.Label(master, image = self.Enemy[i].image))

		self.RButton = tk.Button(master, text = "グー")
		self.RButton.place(x = 45, y = 300)

		self.SButton = tk.Button(master, text = "チョキ")
		self.SButton.place(x = 130, y = 300)

		self.PButton = tk.Button(master, text = "パー")
		self.PButton.place(x = 225, y = 300)

		self.RetryButton = tk.Button(master, text = "リトライ！")
		self.RetryButton.place(x = 220, y = 160)

		self.WinText = tk.Label(master, text = "勝ち！", fg = "red", font = ("Impact", 20, "bold"))
		self.LoseText = tk.Label(master, text = "負け！", fg = "black", font = ("Impact", 20, "bold"))
		self.DrowText = tk.Label(master, text = "引き分け！", fg = "green", font = ("Impact", 20, "bold"))

		master.after(50, self.Update)

		self.pack()

	def Update(self):

		if self.JudgeNumber == 0:
			self.EnemyLabel[self.RandCounter].place_forget()
			self.RandCounter = rd.randint(0,2)
			self.PrintEnemy(self.RandCounter)

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

	def PrintEnemy(self, randCounter):

		if randCounter == 0:
			self.EnemyLabel[randCounter].place(x = self.Enemy[randCounter].x, y = self.Enemy[randCounter].y)

		elif randCounter == 1:
			self.EnemyLabel[randCounter].place(x = self.Enemy[randCounter].x, y = self.Enemy[randCounter].y)

		else:
			self.EnemyLabel[randCounter].place(x = self.Enemy[randCounter].x, y = self.Enemy[randCounter].y)

	def Win(self):
		self.WinText.place(x = 50, y = 150)

	def Lose(self):
		self.LoseText.place(x = 50, y = 150)

	def Drow(self):
		self.DrowText.place(x = 50, y = 150)


def main():

	win = tk.Tk()

	Player = []
	Enemy = []

	Player.append(Image("グー.png", 25, 220, 3))
	Player.append(Image("チョキ.png", 120, 205, 3))
	Player.append(Image("パー.png", 200, 220, 3))

	Enemy.append(Image("グー.png", 100, 25, 2))
	Enemy.append(Image("チョキ.png", 100, 15, 2))
	Enemy.append(Image("パー.png", 100, 25, 2))


	app = Application(Player, Enemy, master = win)


	def RJudge():
		app.JudgeNumber = 1

		if app.RandCounter == 0:
			app.Drow()
		elif app.RandCounter == 1:
			app.Win()
		elif app.RandCounter == 2:
			app.Lose()

	def SJudge():
		app.JudgeNumber = 1

		if app.RandCounter == 0:
			app.Lose()
		elif app.RandCounter == 1:
			app.Drow()
		elif app.RandCounter == 2:
			app.Win()

	def PJudge():
		app.JudgeNumber = 1

		if app.RandCounter == 0:
			app.Win()
		elif app.RandCounter == 1:
			app.Lose()
		elif app.RandCounter == 2:
			app.Drow()

	def RetryJudge():
		app.DrowText.place_forget()
		app.WinText.place_forget()
		app.LoseText.place_forget()
		app.JudgeNumber = 0

	app.RButton["command"] = RJudge
	app.SButton["command"] = SJudge
	app.PButton["command"] = PJudge
	app.RetryButton["command"] = RetryJudge

	app.mainloop()

if __name__ == "__main__":

	main()

```


# 終わりに

ここまで、読んでいただいてありがとうございます。ご参考になれば嬉しいです。
今回はMVCモデルで実装を目指して制作しました。単なるじゃんけんのゲームですが、初めての挑戦だったので、時間はかかりました。しかし、ここ最近で一番成長を実感しています。