Dockerコンテナをアイコン付きでそのままexe化する方法
やりたいこと
Ubuntu上でDockerコンテナを立てて開発したPythonコードを、Windowsアプリとしてアイコン付きでexe化したい!
サマリー
- windowsでPyinstallerを実行するコンテナ、docker-pyinstallerを使用して実行ファイルを作成(exe化)
- specファイルを修正して実行ファイルにアイコン付与
ディレクトリ構成
ディレクトリ構成です。
iconフォルダにはアプリのアイコン画像を格納します。
main.pyはアプリのソースコード。
requirements.txth、必要なライブラリを記載したテキストファイル。
.
├── icon
│ └── icon.ico
├── main.py
└── requirements.txt
①exe化するコード “main.py" を作成
例としてtkinterを使ってGUIを作成し、画像を表示するアプリを作成する。
DALL-E3で作成した2匹のタヌキを表示するアプリです。
実行するとtkinterのGUI上に画像が表示されます。
# main.py
import tkinter as tk
from PIL import ImageTk
class Application(tk.Frame):
def __init__(self, master = None):
super().__init__(master)
self.master.title("画像の表示") # ウィンドウタイトル
self.master.geometry("500x500") # ウィンドウサイズ(幅x高さ)
# Canvasの作成
self.canvas = tk.Canvas(self.master)
# Canvasを配置
self.canvas.pack(expand = True, fill = tk.BOTH)
# 画像ファイルを開く
self.photo_image = ImageTk.PhotoImage(file = "tanukis.png")
# キャンバスのサイズを取得
self.update() # Canvasのサイズを取得するため更新しておく
canvas_width = self.canvas.winfo_width()
canvas_height = self.canvas.winfo_height()
# 画像の描画
self.canvas.create_image(
canvas_width / 2, # 画像表示位置(Canvasの中心)
canvas_height / 2,
image=self.photo_image # 表示画像データ
)
if __name__ == "__main__":
root = tk.Tk()
app = Application(master = root)
app.mainloop()
②必要なライブラリを記載した " requirements.txt " を作成
上記のmain.py と同じ階層に requirements.txt を作成する。
# requirements.txt
pillow
③specファイルの作成
以下のコマンドを実行するとmani.pyを元にしたexeファイルとspecファイルが作成される。
docker run --rm -v "$(pwd):/src/" cdrx/pyinstaller-windows -c \
"pip install -r requirements.txt && \
pyinstaller main.py --onedir --onefile --noconsole --icon ./icon.ico && \
mv dist/main.exe main.exe "
ここで作成されるmain.exeは問題なく動くが、起動時のウィンドウにアイコンが表示されないのでspecファイルを修正して再度作り直す必要がある。(④で説明)
コードの解説
docker run --rm -v "$(pwd):/src/" cdrx/pyinstaller-windows
cdrx/pyinstaller-windowsイメージから使い捨てのコンテナを立て、カレントディレクトリをそのコンテナにマウントする。
pip install -r requirements.txt
pipで依存関係をインストールする。
pyinstaller main.py --onedir --onefile --noconsole --icon ./icon.ico
Pyinstallerでexe化する。
–onedir:出力を1ディレクトリにまとめる
–onefile:出力を1ファイルにまとめる
–noconsole:実行ファイル起動時にコンソール(コマンドプロンプト)を表示しない
–icon:アイコンファイルのパスを指定する
mv dist/main.exe main.exe
exeファイルの場所を移動する。
④specファイルの修正(main.specの修正)
作成されたspecファイル内のexe=EXE()に Tree('./icon’, prefix=’icon_path’) を追加する。
exe = EXE(pyz,
Tree('./icon', prefix='icon_path'), # <- 追加
a.scripts,
b.binaries
a.zipfiles,
a.datas,
[],
name='main',
debug=False,
bootloades_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=Flase,
icon='icon\\icon.ico')
⑤Pythonスクリプト(main.py)の修正
Pythonスクリプト(main.py)を修正して、実行ファイル起動時のウィンドウにもアイコンが埋め込まれるようにする。
・リソースフォルダにアクセスする関数 resourcePath を作成(コピペでOK)
def resourcePath(filename):
if hasattr(sys, "_MEIPASS"):
return os.path.join(sys._MEIPASS, filename)
return os.path.join(filename)
・if __name__ == “__main__": 以降に下記コードを追加(ファイル名"icon”は適宜修正)※ linuxだとico拡張子でエラー出るのでpng作成して埋め込む
filename = resourcePath( 'icon_path/icon.ico')
img = Image.open(filename)
img.save(os.path.splitext(filename)[0]+".png")
root.tk.call('wm', 'iconphoto', root._W,
tk.PhotoImage(file=resourcePath('icon_path/icon.png')))
コード全体は以下のように書き換わる。
# main.py
import tkinter as tk
from PIL import ImageTk
import sys
import os
def resourcePath(filename):
if hasattr(sys, "_MEIPASS"):
return os.path.join(sys._MEIPASS, filename)
return os.path.join(filename)
class Application(tk.Frame):
def __init__(self, master = None):
super().__init__(master)
self.master.title("画像の表示") # ウィンドウタイトル
self.master.geometry("500x500") # ウィンドウサイズ(幅x高さ)
# Canvasの作成
self.canvas = tk.Canvas(self.master)
# Canvasを配置
self.canvas.pack(expand = True, fill = tk.BOTH)
# 画像ファイルを開く
self.photo_image = ImageTk.PhotoImage(file = "tanukis.png")
# キャンバスのサイズを取得
self.update() # Canvasのサイズを取得するため更新しておく
canvas_width = self.canvas.winfo_width()
canvas_height = self.canvas.winfo_height()
# 画像の描画
self.canvas.create_image(
canvas_width / 2, # 画像表示位置(Canvasの中心)
canvas_height / 2,
image=self.photo_image # 表示画像データ
)
if __name__ == "__main__":
filename = resourcePath( 'icon_path/icon.ico')
img = Image.open(filename)
img.save(os.path.splitext(filename)[0]+".png")
root.tk.call('wm', 'iconphoto', root._W,
tk.PhotoImage(file=resourcePath('icon_path/icon.png')))
root = tk.Tk()
app = Application(master = root)
app.mainloop()
⑥実行ファイル"main.py"を再度作成
以下のコマンドを実行し、specファイルからdist/main.exeを作成して終了。
docker run --rm -v "$(pwd):/src/" cdrx/pyinstaller-windows -c \
"pip install -r requirements.txt && \
pyinstaller main.spec"
完成
作成したmain.exeを実行すると、
画像が表示されます。左上にはアイコン画像がついています。
ディスカッション
コメント一覧
まだ、コメントがありません