【Python】Tkinterによる120行で作るGUIアプリ「RSA暗号クイズ」

# はじめに

こんにちは。
今回は「RSA暗号クイズ」を作っていきたいと思います。
この記事では、RSA暗号についての解説はしないので、理論を知りたい方は別の記事などを参考にしてください。
完成すると以下のように動きます！
問題が出題されて、それに対する解答を4つのボタンから1つを選択してクリックする形式です。
正解すると、1点加算し、不正解だと、1点減点されます。

![RSA暗号クイズ.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309633/438e006a-c70c-5607-3b28-a9343a9a4ebe.gif)


# 環境
- Windows 10 home
- Python 3.7.1


# 「RSA暗号クイズ」の制作

## 問題について

```
0 : AからBにメールを送信！暗号化！
1 : AからBにメールを送信！復号！
2 : BからAにメールを送信！暗号化！
3 : BからAにメールを送信！復号！
4 : AからBにメールを送信！デジタル署名!
5 : AからBにメールを送信！送信元確認！
6 : BからAにメールを送信！デジタル署名！
7 : BからAにメールを送信！送信元確認！
```
上記のような問題が配列に格納されていて、ランダムに一問出題されます。
解答はそれぞれ以下のようになります。

```
0 : 公開鍵B
1 : 秘密鍵B
2 : 公開鍵A
3 : 秘密鍵A
4 : 秘密鍵A
5 : 公開鍵A
6 : 秘密鍵B
7 : 公開鍵B
```


## インポート

使うライブラリは以下の3つです。

```py
import tkinter as tk
import random as rd
import time
```


## ウィンドウの作成

```py
import tkinter as tk
import random as rd
import time

class Model():
    def __init__(self):
        self.width=600
        self.height=500

class View():
    def __init__(self,master,model):
        self.master = master
        self.model = model

class Controller():
    def __init__(self,master,model,view):
        self.master = master
        self.model = model
        self.view = view

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

        self.model = Model()

        master.geometry(str(self.model.width)+"x"+str(self.model.height))
        master.title("RSA暗号クイズ")

        self.view = View(master,self.model)
        self.controller = Controller(master,self.model,self.view)

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

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

このプログラムでウィンドウを作っていきます。
サイズは**600×500**です。タイトル名は**RSA暗号クイズ**です。
詳しくは[【Python】Tkinterを使った雛形（MVCモデル）](https://qiita.com/MichihiroTsukano/items/8687962247cae41625f7)の記事をご参照ください。



# 完成したプログラム

```RSA暗号クイズ.py
import tkinter as tk
import random as rd
import time

class Model():
	def __init__(self):
		self.width=600
		self.height=500

		self.score=0
		self.question_number = rd.randint(0,7)
		self.question_list=["AからBにメールを送信！暗号化！","AからBにメールを送信！復号！",
							"BからAにメールを送信！暗号化！","BからAにメールを送信！復号！",
							"AからBにメールを送信！デジタル署名!","AからBにメールを送信！送信元確認！",
							"BからAにメールを送信！デジタル署名！","BからAにメールを送信！送信元確認！"]

	def createCircle(self,canvas,x,y,color,name):
		canvas.create_oval(x-50,y-50,x+50,y+50,fill=color)
		canvas.create_text(x,y,text=name,font=("Helvetica",18,"bold"),fill="white")

class View():
	def __init__(self,master,model):
		self.master = master
		self.model = model

		self.canvas = tk.Canvas(self.master,width=self.model.width,height=self.model.height,bg="black")
		self.canvas.pack()

		self.userA = self.model.createCircle(self.canvas,150,280,"red","userA")
		self.userB = self.model.createCircle(self.canvas,450,280,"blue","userB")

		self.bPriKeyA = tk.Button(self.master,text="秘密鍵",width=10)
		self.bPriKeyA.place(x=110, y=360)
		self.bPubKeyA = tk.Button(self.master,text="公開鍵",width=10)
		self.bPubKeyA.place(x=110, y=420)

		self.bPriKeyB = tk.Button(self.master,text="秘密鍵",width=10)
		self.bPriKeyB.place(x=410, y=360)
		self.bPubKeyB = tk.Button(self.master,text="公開鍵",width=10)
		self.bPubKeyB.place(x=410, y=420)

		self.canvas.create_line(0,200,600,200,fill="white")
		self.canvas.create_line(300,200,300,500,fill="white")
		self.canvas.create_polygon(20,30,580,30,580,110,20,110,fill="skyblue")

		self.canvas.create_text(300,70,text=self.model.question_list[self.model.question_number],font=("Helvetica",20,"bold"),fill="black",tag="question")
		self.canvas.create_text(525,150,text=round(self.model.score,2),font=("Helvetica",40,"bold"),fill="orange",tag="score")

	def update(self):
		self.canvas.delete("question")
		self.canvas.create_text(300,70,text=self.model.question_list[self.model.question_number],font=("Helvetica",20,"bold"),fill="black",tag="question")

		self.canvas.delete("score")
		self.canvas.create_text(500,150,text=round(self.model.score,2),font=("Helvetica",40,"bold"),fill="orange",tag="score")

class Controller():
	def __init__(self,master,model,view):
		self.master = master
		self.model = model
		self.view = view

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

		self.view.bPriKeyA["command"] = self.bPriKeyAClick
		self.view.bPubKeyA["command"] = self.bPubKeyAClick
		self.view.bPriKeyB["command"] = self.bPriKeyBClick
		self.view.bPubKeyB["command"] = self.bPubKeyBClick

	def bPriKeyAClick(self):
		if self.model.question_number == 3 or self.model.question_number == 4: self.correct()
		else: self.incorrect()
		self.model.question_number = rd.randint(0,7)

	def bPubKeyAClick(self):
		if self.model.question_number == 2 or self.model.question_number == 5: self.correct()
		else: self.incorrect()
		self.model.question_number = rd.randint(0,7)

	def bPriKeyBClick(self):
		if self.model.question_number == 1 or self.model.question_number == 6: self.correct()
		else: self.incorrect()
		self.model.question_number = rd.randint(0,7)

	def bPubKeyBClick(self):
		if self.model.question_number == 0 or self.model.question_number == 7: self.correct()
		else: self.incorrect()
		self.model.question_number = rd.randint(0,7)

	def correct(self):
		print("正解!!")
		self.model.score+=1

	def incorrect(self):
		print("不正解><")
		self.model.score-=1

	def update(self):
		self.view.update()

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

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

		self.model = Model()

		master.geometry(str(self.model.width)+"x"+str(self.model.height))
		master.title("RSA暗号クイズ")

		self.view = View(master,self.model)
		self.controller = Controller(master,self.model,self.view)

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

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

プログラムに関しては、
まず、円やボタン、線、問題の枠となる図形、テキストなどのUIを配置します。
次に、ボタンをクリックした時の判定に関する処理等を記述していきます。


# おわりに
以上でRSA暗号クイズは完成になります。
ここまで読んでいただき、ありがとうございました。