さて、これで一通りじゃんけんゲームは完成したことだし、このゲームを誰かに遊んでもらえるようにしようか!
え、別にそのままプログラムを渡せば遊んでもらえないの??
葵さんや、最初の頃を思い出してほしいんだけど、他の人はどうやってPythonのプログラムを動かしたら良いか分かるかな??
あ、そういえば、ちゃんとPythonを動かせる環境を用意しないと出来ないじゃん!
そうだよね
Pythonはどのパソコンにも入ってるわけじゃないからプログラムを渡しただけじゃダメなんだよ
だからここでは、自分で作ったPythonのプログラムを誰でも使えるように「実行ファイル化」する方法を紹介するね!
実行ファイル??
実行ファイルは、Windowsだと「〇〇.exe」、Macだと「〇〇.app」って拡張子が付いてるファイルのことで、それをダブルクリックすることで実行するアプリだよ
ああ!パソコンのソフトとかスマホのアプリとかか!!
そうだね
それで、この実行ファイルにしてくれるライブラリが「PyInstaller」だよ
ライブラリってことは、今のプログラムを何か変更したりするの?
「PyInstaller」はプログラム自体は変更せずに、ターミナル(コマンドプロンプト)上で使うよ
基本的には実行ファイル化したいプログラムがあるカレントディレクトリまで移動して、「特定のコマンド」を実行してあげれば完成するんだけど、その前に注意してほしいことがあるんだ
注意してほしいこと??
まず1つ目は「PyInstallerを実行したOSに依存する実行ファイルになる」ってことだね
Windowsではexeファイル、Macではappファイルになるから、Windowsユーザーが作成したアプリをMacユーザーは使えないんだ
それは地味に困るかも
ちなみにMac同士であっても、Intel製かAppleシリコン製で違っていれば使えないから気をつけてね
(...その辺はよく分からない)
次に2つ目なんだけど、こっちの方が重要で、「そのままPyInstallerを使うとアプリに必要ない外部ライブラリが全て実行ファイルに入ってしまう」ってことだよ
え、それってつまり、今回のじゃんけんゲームを実行ファイル化したら、その中に使ってない「OpenCV」とかが入っちゃうってこと!?
そういうこと
だから、このじゃんけんゲームも実行ファイル化したら容量が数GBとかになっちゃうかも
ただじゃんけんできるだけなのにそれはヤバい
...ということで、その問題を解決する方法として新しく「仮想環境」っていうのを紹介するよ!
なんか難しそうなのが現れたな!?
仮想環境っていうのは、自分の家に「何かを作るための一時的な作業部屋」を作るイメージだよ
ほうほう
なんか秘密基地っぽいね!
その部屋は最初は空っぽで、何かを作るために余計な道具は持ち込まず、最低限必要の道具だけで作業をするって感じだね
分かるような分からないような...
じゃあここからは、実際に動かしてみながら説明するね
今、すでにターミナルのカレントディレクトリは今回作った「janken.py」がある場所と同じ場所だとするよ
そのまま続けてきてるので大丈夫だと思います...!
じゃあまずは、そのカレントディレクトリに仮装環境を用意しよう!
ターミナルで次のコマンドを実行しよう
Windowsの場合
python -m venv janken_venv
Macの場合
python3 -m venv janken_venv
これでまずは「janken_venv」っていう名前の仮想環境が用意できたよ!
あっ!janken.pyを置いてるフォルダに「janken_venv」っていうフォルダができてるね!!
ちなみにさっきのコマンドの1番最後の「janken_venv」ってところは仮想環境の名前を決めるとことだから、別の名前にしても大丈夫だよ
これで今、作業部屋を用意できたって感じなんだね!
そんな感じだね!じゃあ次は作った仮想環境の中に入るコマンドを実行してみよう
Windowsの場合
.\janken_venv\Scripts\activate
Macの場合
source janken_venv/bin/activate
あ、なんかターミナルの1番左に「(janken_venv)」ってのが出てきた!これが中に入ったっていう目印なんだね!
そういうこと
この状態でWindowsなら「pip list」、Macなら「pip3 list」を実行してみると、何もないのが確認できると思うよ!
ほんとだ!今まで色んなライブラリをpipで入れてきたのに何もない!!
今、仮想環境の中は初めてパソコンにPythonを入れた時と同じ状態になってるから、改めてpipのコマンドで「TkEasyGUI」を追加しよう!
Windowsの場合
pip install TkEasyGUI
Macの場合
pip3 install TkEasyGUI
おっけー!ちゃんとインストールできたよ!
じゃあこの状態で、janken.pyが実行できるか確認してみようか
実行方法は今までと変わらないよ!
大丈夫そう!!
そしたら次は、「PyInstaller」をインストールしよう!
Windowsの場合
pip install pyinstaller
Macの場合
pip3 install pyinstaller
インストール完了!!
次に、今回はゲーム内に画像を使用してるけど、実行ファイルで動かした時にはそのまま読み込んでくれないって問題があるから、少し調整を加えるよ
import TkEasyGUI as eg
import random
import sys, os # 追加
# PyInstaller対応:開発中・実行ファイルの両方で正しいパスを返す
def resource_path(relative_path):
if hasattr(sys, '_MEIPASS'):
return os.path.join(sys._MEIPASS, relative_path)
return os.path.join(os.path.abspath("."), relative_path)
# 定数
HANDS = ["グー", "チョキ", "パー"]
HAND_IMAGES = {
"グー": resource_path("janken_gu.png"), # 変更
"チョキ": resource_path("janken_choki.png"), # 変更
"パー": resource_path("janken_pa.png") # 変更
}
WIN_RULES = {
"グー": "チョキ",
"チョキ": "パー",
"パー": "グー"
}
完璧には分からないけど、画像の場所を細かく指定するために「絶対パス」にしているんだね!
ちょっと手間はかかるけど、今後画像を使うツールを作る時は「resource_path()」を丸パクリすれば良いよ!
これであとは実行ファイル化をするだけだよ
次のコマンドを実行してみよう!
Windowsの場合
pyinstaller janken.py --noconsole --clean --add-data "janken_gu.png;." --add-data "janken_choki.png;." --add-data "janken_pa.png;."
Macの場合
pyinstaller janken.py --noconsole --clean --add-data "janken_gu.png:." --add-data "janken_choki.png:." --add-data "janken_pa.png:."
なんか長いコマンドだね汗
あ、なんか大量に文字が出てきてる!!
今回は画像を使ってるから「--add-data」を使ってるけど、使わない場合は「pyinstaller janken.py --noconsole --clean」だけで良いよ!
あ、しばらく待ってたら終わった!!なんかフォルダの中に色んなものが追加されてるね
その中に「dist」ってフォルダはないかな?
あるよ!...あ!中に「janken.exe」ってのがあるよ!!
お!見つかったね!!じゃあそれをダブルクリックしてみよう
おおー!!ちゃんと動いた!!
これで実行ファイル化の完了だよ!その「dist」ってフォルダを誰かに渡してあげると、その人もダブルクリックだけで使えるようになるよ
やったね!!
ちなみに、PyInstallerのコマンドに付けられるオプションは他にもあるから、自分のやりたいことに合わせて調べてみてね!
りょうかい!!ところで、仮想環境から抜け出す方法はないの??
あ、忘れてたね汗
仮想環境から抜け出す時は次のコマンドを実行すればOKだよ!
deactivate
脱出完了!!
さて、これでひとまずじゃんけんゲームが完成だよ!ここからは、「CPUがじゃんけんの手を出す確率を過去にプレイヤーが出した手の傾向で変化させる」とか「何回勝負で勝ち負けを決める」みたいに自分でオリジナル要素を追加してみてね!
自分で考えるのはなかなかハードルが高そう...
もし自分で1から考えるのが難しいなら、「ChatGPT」とかの生成AIをどんどん活用したら良いと思うよ!
学習用のサイトでそれ言っちゃうのね...
まぁ基礎はしっかり学べてると思うしね!今まではどう聞いて良いか分からなかっただろうし、そのプログラムが正しいかどうかって分別が付かなかったと思うけど、きっと今なら大丈夫じゃないかな
まずは何かを作りきる方が大事だもんね!頑張ってみる!!
完成した全体のプログラム
import TkEasyGUI as eg
import random
import sys
import os
# PyInstaller対応:開発中・実行ファイルの両方で正しいパスを返す
def resource_path(relative_path):
if hasattr(sys, '_MEIPASS'):
return os.path.join(sys._MEIPASS, relative_path)
return os.path.join(os.path.abspath("."), relative_path)
# 定数
HANDS = ["グー", "チョキ", "パー"]
HAND_IMAGES = {
"グー": resource_path("janken_gu.png"),
"チョキ": resource_path("janken_choki.png"),
"パー": resource_path("janken_pa.png")
}
WIN_RULES = {
"グー": "チョキ",
"チョキ": "パー",
"パー": "グー"
}
class JankenGame:
def __init__(self):
# スコア変数
self.win_count = 0
self.lose_count = 0
self.draw_count = 0
self.window = None
def check_hands(self,player_hand, cpu_hand):
if player_hand == cpu_hand:
self.draw_count += 1
return "あいこだよ"
elif WIN_RULES[player_hand] == cpu_hand:
self.win_count += 1
return "勝ち!!"
else:
self.lose_count += 1
return "負け..."
def update_hand_image(self):
# 処理後のじゃんけんの手の画像の更新
self.window["win_score"].update(f"勝ち: {self.win_count}")
self.window["lose_score"].update(f"負け: {self.lose_count}")
self.window["draw_score"].update(f"あいこ: {self.draw_count}")
def play_janken(self, player):
# じゃんけんの手のボタンを押した時の処理
cpu = random.choice(HANDS)
self.window["player_hand"].update(HAND_IMAGES[player])
self.window["cpu_hand"].update(HAND_IMAGES[cpu])
self.window["result"].update(f"プレイヤーの手は「{player}」、CPUの手は「{cpu}」です")
eg.popup(self.check_hands(player, cpu))
self.update_hand_image()
def score_reset(self):
# リセットボタンを押した時の処理
self.window["player_hand"].update(data=b"")
self.window["cpu_hand"].update(data=b"")
self.window["result"].update("")
self.win_count = 0
self.lose_count = 0
self.draw_count = 0
self.update_hand_image()
eg.popup("スコアをリセットしました!")
def create_layout(self):
# レイアウト(画面構成)
col1 = eg.Column([[eg.Image(HAND_IMAGES["グー"], size=(100, 100))], [eg.Button("グー")]])
col2 = eg.Column([[eg.Image(HAND_IMAGES["チョキ"], size=(100, 100))], [eg.Button("チョキ")]])
col3 = eg.Column([[eg.Image(HAND_IMAGES["パー"], size=(100, 100))], [eg.Button("パー")]])
layout = [
[eg.Text("じゃんけんの手を選ぼう!")],
[col1, col2, col3],
[eg.Text("プレイヤーの手"),
eg.Image(None, size=(100, 100),key="player_hand"),
eg.Text("vs"),
eg.Image(None, size=(100, 100), key="cpu_hand"),
eg.Text("CPUの手")],
[eg.Text("", key="result")],
[eg.Text("勝ち: 0", key="win_score"),
eg.Text("負け: 0", key="lose_score"),
eg.Text("あいこ: 0", key="draw_score")],
[eg.Button("リセット")]
]
return layout
def run(self):
# ウィンドウを作成
self.window = eg.Window("じゃんけんアプリ", self.create_layout(), element_justification="center")
# イベントループ(閉じるまで表示)
while True:
event, values = self.window.read()
if event in HANDS:
self.play_janken(event)
if event == "リセット":
self.score_reset()
if event is eg.WINDOW_CLOSED:
break
# ウィンドウを閉じる
self.window.close()
# ゲームを実行
game = JankenGame()
game.run()
