2022/08/10(水) [n年前の日記]
#1 [python][windows] Pythonスクリプトをexe化したい
Windows10 x64 21H2 + Python 3.8.10 x64 上で、Pythonスクリプトをexeファイルにしたい。pygame を使ってWindows用のスクリーンセーバを作ってみたいのだけど、そのためには Pythonスクリプトファイルをexe化しないといけないので…。
今回は virtualenv を使って、Python 3.8 が動作する環境を作った。と言うのも、各exe化ツールは動作に不要なファイルまで含めてexe化してしまう時があるそうで。故に、virtualenv や venv 等を使って、動作に必要な最低限のモジュールだけがインストールされている環境を用意して、ソレをexe化するのが望ましいのだとか。
Pythonスクリプトをexe化するツールは色々あるけれど、今回は PyInstaller と py2exe を試用してみた。
virtualenv で環境を作った場合、PyInstaller、py2exe も、その環境にインストールして、その環境内で実行しないといけない。そうしないと、システム側にインストールした PyInstaller や py2exe を使ってしまって、動作に不要なモジュールまで入ってしまう。
ちなみに、昔の py2exe は Python 3.4 までしか対応してなかったらしいけど、今現在の py2exe 0.11.1.1 は Python 3.7 - 3.10 まで対応している。
_py2exe - PyPI
今回は virtualenv を使って、Python 3.8 が動作する環境を作った。と言うのも、各exe化ツールは動作に不要なファイルまで含めてexe化してしまう時があるそうで。故に、virtualenv や venv 等を使って、動作に必要な最低限のモジュールだけがインストールされている環境を用意して、ソレをexe化するのが望ましいのだとか。
Pythonスクリプトをexe化するツールは色々あるけれど、今回は PyInstaller と py2exe を試用してみた。
virtualenv で環境を作った場合、PyInstaller、py2exe も、その環境にインストールして、その環境内で実行しないといけない。そうしないと、システム側にインストールした PyInstaller や py2exe を使ってしまって、動作に不要なモジュールまで入ってしまう。
> env38\Scripts\activate (env38) ... > python -V Python 3.8.10 (env38) ... > python -m pip install pyinstaller -U (env38) ... > python -m pip install py2exe -U (env38) ... > python -m pip list | grep -e pyinstaller -e py2exe py2exe 0.11.1.1 pyinstaller 5.3 pyinstaller-hooks-contrib 2022.8 (env38) ... > deactivate
ちなみに、昔の py2exe は Python 3.4 までしか対応してなかったらしいけど、今現在の py2exe 0.11.1.1 は Python 3.7 - 3.10 まで対応している。
_py2exe - PyPI
◎ PyInstallerでexe化。 :
Python 3.8.10 + PyInstaller 5.3 を使って、先日作成した、tkinterのウインドウ内にpygameのウインドウを埋め込むスクリプト、01_embed_pygamewindow.py をexe化してみた。
_01_embed_pygamewindow.py
以下を打てば、exe化される。
distディレクトリ以下に、01_embed_pygamewindow.exe が作成された。実行したらちゃんと動作した。
ただ、ウインドウが開くまでちょっと時間がかかる。
PyInstaller で作成したexeは、実行すると C:\Users\USERNAME\AppData\Local\Temp\ 以下に仮ディレクトリを作成して、その中に実行に必要なファイルを全て解凍して、それらのファイルを実行しているらしい。また、アプリを終了すると、その仮ディレクトリも削除される。毎回実行するたびに、圧縮ファイルを解凍する処理が行われるので、起動が遅いのも当然だろうか。
ちなみに、一度 exeファイルに変換すると、.specファイルが作られる。その .specファイルの内容を修正して、pyinstaller hoge.spec とすれば、その .specファイルの内容に従って exeファイルが作成される。ただし、.spec を使わないで exe を作成すると、既に存在していた .spec は上書きされてしまうので注意。
余談。今回、実行ファイルを圧縮してファイルサイズを小さくしてくれる、UPX というツールを使わないように、--noupx を指定してみたのだけど。生成された exeファイルは、--noupx を指定しない場合と変わらないファイルサイズだった…。このオプション、実は機能していないのでは…?
_01_embed_pygamewindow.py
以下を打てば、exe化される。
pyinstaller --onefile --noconsole --noupx 01_embed_pygamewindow.py
- --onefile : 1つのexeファイルにする。
- --noconsole : exe実行時にコンソール(DOS窓)を開かない。
- --noupx : 実行ファイル圧縮ツール UPX を使わない。
distディレクトリ以下に、01_embed_pygamewindow.exe が作成された。実行したらちゃんと動作した。
ただ、ウインドウが開くまでちょっと時間がかかる。
PyInstaller で作成したexeは、実行すると C:\Users\USERNAME\AppData\Local\Temp\ 以下に仮ディレクトリを作成して、その中に実行に必要なファイルを全て解凍して、それらのファイルを実行しているらしい。また、アプリを終了すると、その仮ディレクトリも削除される。毎回実行するたびに、圧縮ファイルを解凍する処理が行われるので、起動が遅いのも当然だろうか。
ちなみに、一度 exeファイルに変換すると、.specファイルが作られる。その .specファイルの内容を修正して、pyinstaller hoge.spec とすれば、その .specファイルの内容に従って exeファイルが作成される。ただし、.spec を使わないで exe を作成すると、既に存在していた .spec は上書きされてしまうので注意。
余談。今回、実行ファイルを圧縮してファイルサイズを小さくしてくれる、UPX というツールを使わないように、--noupx を指定してみたのだけど。生成された exeファイルは、--noupx を指定しない場合と変わらないファイルサイズだった…。このオプション、実は機能していないのでは…?
◎ py2exeでexe化。 :
Python 3.8.10 + py2exe 0.11.1.1 で exe化してみる。
変換したい .py と同じ階層に setup.py を作成。この setup.py の記述で、以下のような指定ができるらしい。
_setup.py
python setup.py py2exe と打てば、distディレクトリ以下にexeファイルが生成される。実行したところ、ちゃんと動作してくれた。なんとなくだけど、PyInstaller と比べると起動は若干速いような気がした。
ただ、今回、"bundle_files": 1, を指定して、1つのexeファイルにするように指定したはずが、そうはなってくれなかった。
どうやら、py2exe を使って1つのexeファイルにするのは難しい模様…。となると、pygame でスクリーンセーバを作るとしたら、PyInstaller 一択のようだなと。
変換したい .py と同じ階層に setup.py を作成。この setup.py の記述で、以下のような指定ができるらしい。
- どのスクリプトを exe化するか。
- 1つのファイルにするか、複数のファイルにするか。
- 動作にはどんなモジュールが必要か。
_setup.py
from distutils.core import setup import py2exe option = { "packages": ["pygame", "tkinter", "win32gui"], "compressed": 1, "optimize": 2, "bundle_files": 1, } setup( options = { 'py2exe': option, }, console = [ {'script': "01_embed_pygamewindow.py"} ], zipfile = None, )
python setup.py py2exe と打てば、distディレクトリ以下にexeファイルが生成される。実行したところ、ちゃんと動作してくれた。なんとなくだけど、PyInstaller と比べると起動は若干速いような気がした。
ただ、今回、"bundle_files": 1, を指定して、1つのexeファイルにするように指定したはずが、そうはなってくれなかった。
- distディレクトリ内には、pygame の動作に必要になるSDL関連dllファイルが9ファイルほどコピーされた。
- dist/libディレクトリも作成されて、その中には tkinter の動作に必要なのであろう tcl と tk というフォルダがあり、中には1000ファイルほど入っていた。
> dir ... 2022/08/08 05:16 <DIR> . 2022/08/08 05:16 <DIR> .. 2022/08/08 05:16 9,367,824 01_embed_pygamewindow.exe 2022/08/08 05:16 <DIR> lib 2021/05/03 11:54 3,406,016 libcrypto-1_1.dll 2021/05/03 11:54 32,792 libffi-7.dll 2021/05/03 11:54 690,368 libssl-1_1.dll 2022/08/08 01:48 300,544 SDL.dll 2022/08/08 01:48 2,326,016 SDL_image.dll 2022/08/08 01:48 165,888 SDL_mixer.dll 2022/08/08 01:48 623,616 SDL_ttf.dll 2021/05/03 11:54 1,705,120 tcl86t.dll 2021/05/03 11:54 1,468,064 tk86t.dll 10 個のファイル 20,086,248 バイト 3 個のディレクトリ 88,550,535,168 バイトの空き領域 > dir lib ... 2022/08/08 05:16 <DIR> . 2022/08/08 05:16 <DIR> .. 2021/05/08 06:29 <DIR> tcl 2021/05/08 06:29 <DIR> tk 0 個のファイル 0 バイト 4 個のディレクトリ 88,550,535,168 バイトの空き領域
どうやら、py2exe を使って1つのexeファイルにするのは難しい模様…。となると、pygame でスクリーンセーバを作るとしたら、PyInstaller 一択のようだなと。
[ ツッコむ ]
以上です。