はじめに
Pythonを使ってちょっとしたGUIアプリを作るときに,よくPyQtで書いてそれをPyInstallerでexeにパッケージングしています.
その時にお気に入りのアプリはアイコンもつけておきたいので,その方法についてまとめます.
対応したいアイコン表示は以下の二通りです.
- エクスプローラに表示されるexeファイルのアイコン
- タイトルバーやタスクバーに表示されるアイコン
開発環境
今回確認した環境は以下の通りです.
- Windows10 Pro
- Python 3.6.4
- PyInstaller 3.3.1
- PyQt5 5.10.1
インストール
PyInstallerとPyQtはpip
からインストールができます.
> pip install pyinstaller pyqt5
exeファイルのアイコン
PyInstallerでパッケージングするときにアイコンをつけることができます.
> pyinstaller foo.py --onefile --noconsole --icon=bar.ico
--icon
オプションにicoファイルを指定します.
またアイコン表示とは直接関係ありませんが,--onefile
オプションでパッケージングするときに1ファイルにまとめるのと,--noconsole
オプションで起動時にコンソール画面を表示しないように指定しています.
タイトルバーやタスクバーに表示されるアイコン
こちらはPyQtのコードで対応します.QApplicationのsetWindowIcon()
に画像ファイルを渡すとタスクバーにアイコンが表示されます.
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication, QWidget
app = QApplication(sys.argv)
app.setWindowIcon(QIcon('bar.ico')) # pngファイルなどでもOK
w = QWidget()
w.show()
sys.exit(app.exec())
これでPyInstallerで生成したexeファイルと同じパスにbar.icoを置いておくと,タイトルバーやタスクバーにアイコンが表示されます.
ただしこのままでは,exeファイルのほかにicoファイルも同時に配布する必要があるため,もう一工夫します.
画像ファイルの組み込み
PyInstallerでパッケージングするときに,外部ファイルを組み込むことができます.組み込むにはspecファイルに記述します.specファイルは一から記述してもいいですが,上記のようにfoo.py
ファイルを指定してPyInstallerを動かしているとfoo.spec
ファイルが生成されています.これに以下の一行を追記すると簡単です.
a = Analysis(['foo.py'],
pathex=['C:\\work\\Python\\foo'],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
+a.datas += [('bar.ico', '.\\bar.ico', 'Data')]
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
name='foo',
debug=False,
strip=False,
upx=True,
runtime_tmpdir=None,
console=False , icon='bar.ico')
このspecファイルを使用してPyInstallerを実行します.
> pyinstaller foo.spec
これでbar.ico
ファイルが組み込まれてパッケージングされます.
組み込んだファイルの読み込み
PyInstallerでパッケージングしたexeを実行するとTempフォルダに一時ファイルが展開されます.この中に上記で組み込んだ画像ファイルも展開されているので,これを読み込みます.
展開される先のパスはsys._MEIPASS
から取得ができます.以下のようなコードを書いておけば,PyInstallerを使用したときと普通にPythonで実行したときとで両対応できます.
import os
import sys
def resource_path(relative):
if hasattr(sys, '_MEIPASS'):
return os.path.join(sys._MEIPASS, relative)
return path.join(path.abspath('.'), relative)
これを利用して以下のようなコードを書けば,パッケージングされたexeファイルだけでタイトルバーやタスクバーにアイコンを表示することができます.
import os
import sys
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication, QWidget
def resource_path(relative):
if hasattr(sys, '_MEIPASS'):
return os.path.join(sys._MEIPASS, relative)
return path.join(path.abspath('.'), relative)
def main():
app = QApplication(sys.argv)
app.setWindowIcon(QIcon(resource_path('bar.ico')))
w = QWidget()
w.show()
sys.exit(app.exec())
if __name__ == '__main__':
main()