取り敢えずpythonでGUIを使ってみる

# はじめに
<!-- 発端や概要を記載 -->
こんにちは、おおのと申します。僕の趣味はゲーセンで音ゲー（主にチュウニズム）をすることなのですが、僕よりずっと後から始めた友達があまりに上手すぎて絶望しています。
それはおいといて今回の本題です。プログラミングの勉強を始めてコマンドラインで動くツールを幾つか作ったりしたのですが普段使うツールって大体GUI使ってますよね。自分でもGUIで何か作りたいなと思い簡単なものを作ってみたので、プログラミング超初心者仲間さんに共有したいと思い記事を書きました。マジで使ってみただけで何の工夫もできておらず基本の基本を動かしてみたレポートという感じですがそれでも見てみたい人は見てくれると嬉しいです。
# 目次
<!-- タイトルとアンカー名を編集 -->
1. [チュウニズムのスコア計算ツール](#Chapter1)
1. [終わりに](#Chapter2)

<!-- 各チャプター -->
<a id="#Chapter1"></a>
# チュウニズムのスコア計算ツール
「はじめに」で述べたように僕はチュウニズムが好きなのですが、チュウニズムに限らずゲームでスコアを狙う時どの程度を目指せばいいかを事前に知っておくと遊びやすくなると思います。このゲームのスコア計算式は既に解明されているので、狙っているスコアを達成するにはどのくらいミスを削ればいいかというのを計算することができます。
チュウニズムやら音ゲーやらを知らない人も多いと思うので、具体的に何がしたいかということを説明します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2326432/48121d21-7547-e749-8d1a-cefd3447551d.png)
チュウニズムのプレイ画面ってこんな感じなのですが、画面上の方にJUSTICE CRITICAL, JUSTICE, ATTACK, MISSと書いてあると思います。左から順番にスコアの高い判定を示しており、右に行くほど得られるスコアの低い判定になります。画面にノーツというものが流れてくるのですが、それをタイミングぴったりで押せると一番左が加算され、タイミングがずれるにつれ右の方の判定に加算されてしまうという感じです。
チュウニズムでは満点が101万点になっていて、全てのノーツをJUSTICE CRITICALで取ると満点が取れます。対して、右のJUSTICEはJUSTICE CRITICALの100/101のスコア、その右のATTACKはJUSTICE CRITICALの50/101のスコア、その右のMISSはスコアが0となっています。ちなみに、普通にこのゲームをする上ではJUSTICEまでしか出さないでクリアするとALL JUSTICEというものが取れて、基本はそれを目指すことになるかなと思います（満点はやばい人しか狙いません）
点数計算をしたいときに必要になる情報はノーツが全部でいくつあるかということとJUSTICE CRITICAL以外の判定をいくつずつ出したかです。101万点が満点なので、出した判定に応じて削られる点数分101万から引いていけばよいですね。ちなみにこのゲームのスコアは小数点以下切り捨てのようなのでそれも織り交ぜることといたします。
参照：https://chunithm.gamerch.com/%E3%82%B2%E3%83%BC%E3%83%A0%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0#content_2_2
今回はGUIを使ってチュウニズムのスコア計算をしてくれるツールを作りました。以下にコードを載せます

```
import tkinter as tk
import tkinter.ttk as ttk
from tkinter import messagebox
import math
def calcscore():
    if int(entry0.get()) < (int(entry1.get()) + int(entry2.get()) + int(entry3.get())):
        messagebox.showinfo("Error",'ノーツ数とミスカウントが合いません')
    else:
        score = math.floor(1010000 - (int(entry1.get())*(1010000/int(entry0.get()))*(1/101) + int(entry2.get())*(1010000/int(entry0.get()))*(51/101) + int(entry3.get())*(1010000/int(entry0.get())))) #math.floorは小数点以下切り捨て、entry1はJUSTICEの数を示しており、その分だけ1/101を減点、entry2,3はATTACK、MISSに対応しており以下同様
        rank = ""
        if score >= 1009000:
            rank = 'SSS+'
        elif score >= 1007500 and score < 1009000:
            rank = "SSS"
        elif score >= 1005000 and score < 1007500:
            rank = "SS+"
        elif score >= 1000000 and score < 1005000:
            rank = "SS"
        elif score >= 990000 and score < 1000000:
            rank = "S+"
        elif score >= 975000 and score < 990000:
            rank = "S"
        else:
            rank = "under S"
        messagebox.showinfo("score",str(score) + " " + rank)
        

# rootフレームの設定
root = tk.Tk()
root.title("CHUNITHMのスコア計算")
root.geometry("200x120")


# 各種ウィジェットの作成
label0 = ttk.Label(root, text="ノーツ数：")
entry0 = ttk.Entry(root)
label1 = ttk.Label(root, text="justice数:")
entry1 = ttk.Entry(root)
label2 = ttk.Label(root, text="attack数:")
entry2 = ttk.Entry(root)
label3 = ttk.Label(root, text="miss数:")
entry3 = ttk.Entry(root)
button_execute = ttk.Button(root, text="実行", command=calcscore)

# 各種ウィジェットの設置
label0.grid(row=0, column=0)
entry0.grid(row=0, column=1)
label1.grid(row=1,column=0)
entry1.grid(row=1,column=1)
label2.grid(row=2,column=0)
entry2.grid(row=2,column=1)
label3.grid(row=3,column=0)
entry3.grid(row=3,column=1)
button_execute.grid(row=4, column=1)
entry1.insert(0,"0")
entry2.insert(0,"0")
entry3.insert(0,"0")

root.mainloop()
```
算術ライブラリのmathとGUIを使うためのライブラリtkinterを使ってみました。
実行すると以下のようなウィンドウが開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2326432/fae6aa99-00e0-18a5-5c77-1b552d9c5b68.png)
JUSTICE、ATTACK、MISSのカウントは先に0埋めしてあるので適宜数字を入れてください。ノーツ数は曲によって違うので調べて自分で入れてください。これで下のように適当な数字を入れて実行すると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2326432/3be706af-ad8b-99f7-cbb0-f41b10f3de24.png)
このように結果が出ます。スコアの実数値とスコアランクですね。
説明していませんでしたが、このゲームではスコアに応じてSSS+～Dというランクが付きます。申し訳程度にそれにも対応してみました。（どのスコアがどのランクに対応しているかは上の参考ページに載ってます）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2326432/c695fef1-22a7-7280-f2a9-12b668e6e4d2.png)
2300ノーツの曲ならこれくらいでSSSが取れるのがわかりますね。（ちなみに2300はかなり物量のある方だと思います）
では、このプログラムの説明をしていきたいと思います。


まずGUIのウィンドウの設計です。

```
# rootフレームの設定
root = tk.Tk()
root.title("CHUNITHMのスコア計算")
root.geometry("200x120")
```
rootはTKオブジェクト（画面上のウィンドウに対応）
titleでウィンドウの名前を指定
geometryでウィンドウの縦横比を指定

次はウィンドウ内に要素を置いていきます

```
# 各種ウィジェットの作成
label0 = ttk.Label(root, text="ノーツ数：")
entry0 = ttk.Entry(root)
label1 = ttk.Label(root, text="justice数:")
entry1 = ttk.Entry(root)
label2 = ttk.Label(root, text="attack数:")
entry2 = ttk.Entry(root)
label3 = ttk.Label(root, text="miss数:")
entry3 = ttk.Entry(root)
button_execute = ttk.Button(root, text="実行", command=calcscore)

# 各種ウィジェットの設置
label0.grid(row=0, column=0)
entry0.grid(row=0, column=1)
label1.grid(row=1,column=0)
entry1.grid(row=1,column=1)
label2.grid(row=2,column=0)
entry2.grid(row=2,column=1)
label3.grid(row=3,column=0)
entry3.grid(row=3,column=1)
button_execute.grid(row=4, column=1)
entry1.insert(0,"0")
entry2.insert(0,"0")
entry3.insert(0,"0")

root.mainloop()
```
ウィンドウに各種情報の入力フォームと実行ボタンを設置します。
ttk.Labelで入力フォーム横のテキストを設定し、ttk.Entryで入力フォームを、ttk.Buttonでボタンを設定します。
それぞれgridで配置する行と列をrowとcolumnで指定しながら配置します。最後にentry1～3に0を標準入力するようにしてroot.mainloopでイベントループを開始します。

これでツールのウィンドウが出来上がりました。

次に実行ボタンを押したときに動かす関数を定義します。

```
button_execute = ttk.Button(frame, text="実行", command=calcscore)
```
ここでボタンの作成と実行する関数の設定をしてますね。
以下が関数calcscoreです。

```
def calcscore():
    if int(entry0.get()) < (int(entry1.get()) + int(entry2.get()) + int(entry3.get())):
        messagebox.showinfo("Error",'ノーツ数とミスカウントが合いません')
    else:
        score = math.floor(1010000 - (int(entry1.get())*(1010000/int(entry0.get()))*(1/101) + int(entry2.get())*(1010000/int(entry0.get()))*(51/101) + int(entry3.get())*(1010000/int(entry0.get()))))
        rank = ""
        if score >= 1009000:
            rank = 'SSS+'
        elif score >= 1007500 and score < 1009000:
            rank = "SSS"
        elif score >= 1005000 and score < 1007500:
            rank = "SS+"
        elif score >= 1000000 and score < 1005000:
            rank = "SS"
        elif score >= 990000 and score < 1000000:
            rank = "S+"
        elif score >= 975000 and score < 990000:
            rank = "S"
        else:
            rank = "under S"
        messagebox.showinfo("score",str(score) + " " + rank)
```
entryの要素を読み取ったりしてscore及びrankを算出し、新たなポップアップウィンドウに表示します（messagebox.showinfoの引数で指定）


結局GUIを使ってツールを作るにはどうすればよいかといえばGUI用のウィンドウと実行時に動かしてあげる関数を作ってあげればいいわけですね。

<a id="#Chapter2"></a>
# おわりに
今回はかなりシンプルな題材で作ってみました。複数入力の必要になるツールにはコマンドラインよりこういった形の方が良いのではないかと思います。装飾など一切考えずとりあえず動くテンプレートみたいになってしまいましたが、実際にGUIを使って何かしらのきっかけになればと思います。
今回は読んでいただきありがとうございました。