pythonのexe化 ~PySimpleGUIの実行ファイルにiconを設定~

exe化,python,python

pyinstaller使って実行ファイル(exeファイル)にすることで、より使い勝手のいい形で作ったツールを配布したいなと思ったこのごろ。

デフォルトのままだとiconにオリジナリティがないので変更方法を調べてみました。

テストコード

いろいろ試していくベースとして以下のようなテストコードを用意しました。

もちろんご自身で作成いただいたソースで試していただいていかまいません。

# coding: utf -8
import PySimpleGUI as sg

# テーマの設定
sg.theme("SystemDefault ")

L1 = [[sg.Button("Buttan 1", border_width=3 ,    size =(11,1),    key="-BTN1-")]]
L2 = [[sg.Button("Buttan 2", border_width=3 ,     size =(11,1),    key="-BTN2-")]]
L3 = [[sg.Multiline(default_text="", border_width=1,    size=(29,2),    key="-TEXT-")]]
L  = [[sg.Frame("Buttan 1",L1),sg.Frame("Buttan 2",L2)],
     [sg.Frame("text",L3)]]

# ウィンドウ作成
window = sg.Window ("ICON test",L )
values = ""

def main():
    global values
    # イベントループ
    while True:
        # イベントの読み取り(イベント待ち)
        event , values = window.read()
        if event == "-BTN1-":
            window["-TEXT-"].Update("-----------Buttan 1 pressed-----------")
            print("Buttan 1 pressed")
        elif event == "-BTN2-":
            window["-TEXT-"].Update("-----------Buttan 2 pressed-----------")
            print("Buttan 2 pressed")
        elif event == None:
            break

if __name__ == '__main__':
    main()

押したボタンに応じて画面表示が変わります。

pyinstallerによるexe化の基本

まずはpythonのexe化に必要なpyinstallerの簡単な使い方から。

ライブラリインストール

以下で必要なライブラリをインストール

pip install -U pyinstaller

exe化

適当なテストコードを用意します。

以下コマンドを実施(app.pyは任意のファイル名にしてください)

pyinstaller --onefile app.py

諸々ファイルが出力されるので、「dist」フォルダを確認

「dist」フォルダに出力されたexeを実行すると、GUIとコンソールが立ち上がる

print文やエラー発生時のエラー表示などはコンソール画面に表示される

オプション設定

pyinstallerによる実行ファイル生成にあたり様々なオプションが設定で行きます。

例えば以下のように–noconsoleを使用すれば、exe実行時に前述のコンソール画面が立ち上がらなくなります。

pyinstaller --onefile --noconsole app.py

ちなみに–onefileを使わないと関連ファイルを一つにまとめてくれないので、結局exeだけで実行が出来なくなってしまいます。

その他いくつか役立ちそうなオプションを紹介します。

オプション概要
onefile1 つのファイルまとめて実行ファイル(exeファイル)を作成する。
name生成する実行ファイルの名前を指定。
(デフォルトだと指定したpythonファイルと同名の実行ファイルが生成される)
noconsoleコンソールを非表示にする。
iconアイコンを実行ファイルへ適用させる。
(デフォルトだとPyInstaller のアイコンとなる)

その他詳細については公式等参照してください。

iconを変えてみる

いよいよ本題のiconを目当てのものへ変えていきます。

変更箇所の確認

iconの変更箇所は以下の3つ。(コンソールは出力しない前提)

① 実行ファイルのアプリアイコン

② PySimpleGUI実行中のアプリアイコン

③ PySimpleGUI画面左上のアプリアイコン

iconファイルの準備

まずはiconファイルを準備しましょう。

本題ではないので詳しく解説はしませんが、事前に準備したjpgなどを以下でiconへ変換できます。

iconの設定その1

① 実行ファイルのアプリアイコン」については、pyinstallerのオプションである「–icon」を使用することで簡単に変えることが可能です。

pyinstallerのオプション「–icon」によるicon設定

exe化対象pythonソースと同じフォルダにiconファイルを置く

オプション「–icon」を指定してpyinstallerを実行

pyinstaller --onefile --icon=icon.ico --noconsole app.py

出力されるexeファイルにiconが反映されている

(エラーが出ていないにも関わらず反映されない場合は、前述の注意点を実行してみる)

注意点

PC上のキャッシュ状態によってはiconの変更内容が反映された状態で表示されない可能性があります。

おかしいなと思ったら以下試してください。

「windows」+「r」ボタンを押す

「ie4uinit.exe -show」を入力しOK

iconの設定その2

② 実行中アプリのアプリアイコン」「③ ツール画面左上のアプリアイコン」はPySimpleGUIの実装に依存するiconです。

通常は以下のようにiconファイルが格納されているパスを指定することで、iconの適用が出来ます。

window = sg.Window ("ICON test",L ,icon="icon.ico")

しかしながら、常にexeファイルと同じフォルダにiconファイルを置いてくださいだとかっこが悪い。

なのでiconファイルもexeファイルに含めて動かす手順を説明します。

本手順には以下2段階が必要になります。

  • PySimpleGUIによるicon指定
  • pyinstallerによるiconファイルの実行ファイルへの取り込み

PySimpleGUIによるicon指定

実行ファイルに取り込まれるiconファイルを、実装にて指定する必要があります。

早速ですが修正後のソースは以下になります。(追加関数の置き場所などはわかりやすさ優先)

# coding: utf -8
import PySimpleGUI as sg
import sys # import追加
import os # import追加

# テーマの設定
sg.theme("SystemDefault ")

L1 = [[sg.Button("Buttan 1", border_width=3 ,    size =(11,1),    key="-BTN1-")]]
L2 = [[sg.Button("Buttan 2", border_width=3 ,     size =(11,1),    key="-BTN2-")]]
L3 = [[sg.Multiline(default_text="", border_width=1,    size=(29,2),    key="-TEXT-")]]
L  = [[sg.Frame("Buttan 1",L1),sg.Frame("Buttan 2",L2)],
     [sg.Frame("text",L3)]]

def resource_path(relative): # 関数追加
    if hasattr(sys, '_MEIPASS'):
        return os.path.join(sys._MEIPASS, relative)
    return os.path.join(os.path.abspath('.'), relative)

# ウィンドウ作成
icon_path = resource_path("icon.ico") # パス取得追加
window = sg.Window ("ICON test",L,icon=icon_path) # パス指定追加
values = ""

def main():
    global values
    # イベントループ
    while True:
        # イベントの読み取り(イベント待ち)
        event , values = window.read()
        if event == "-BTN1-":
            window["-TEXT-"].Update("-----------Buttan 1 pressed-----------")
            print("Buttan 1 pressed")
        elif event == "-BTN2-":
            window["-TEXT-"].Update("-----------Buttan 2 pressed-----------")
            print("Buttan 2 pressed")
        elif event == None:
            break

if __name__ == '__main__':
    main()

変更箇所だけを抜き出すと以下の通りで、Tempフォルダに展開される一時ファイルから、必要なiconファイルを引っ張ってきているらしいです。

import sys # import追加
import os # import追加

def resource_path(relative): # 関数追加
    if hasattr(sys, '_MEIPASS'):
        return os.path.join(sys._MEIPASS, relative)
    return os.path.join(os.path.abspath('.'), relative)

# ウィンドウ作成
icon_path = resource_path("icon.ico") # パス取得追加
window = sg.Window ("ICON test",L,icon=icon_path) # パス指定追加

詳細については以下参照してください。

pyinstallerによるiconファイルの実行ファイルへの取り込み

pyinstallerによる実行ファイル生成

前述のicon指定オプションとともにpyinstallerを実行します。

pyinstaller --onefile --icon=icon.ico --noconsole app.py

生成されたapp.specファイルを開き、「a.datas += [(“icon.ico", “icon.ico", “Data")]」を挿入

以下の23行目

コマンド「pyinstaller app.spec」を実行、「dist」フォルダの実行ファイルが更新される

pyinstaller app.spec

更新された実行ファイルを動かしてみるとiconが変わっています。

その他困ったときは

私もよくつまずくのですが、使用しているライブラリのバージョン依存によってはよくわからないエラーが出てきます。

以下は今回の私の環境になりますので、困ったときは参考にしてみてください。

  • Python 3.9.4
  • pyinstaller 5.1
  • PySimpleGUI 4.60.1

以下はpyinstallerを最新の5.8にした際、「pyinstaller app.spec」実行時に出たエラーです。

pyinstallerを実績のある5.1へダウングレードし、実行ファイル生成からやり直したら解決しました。

136 INFO: PyInstaller: 5.8.0
137 INFO: Python: 3.9.4
137 INFO: Platform: Windows-10-10.0.19041-SP0        
139 INFO: UPX is not available.
140 INFO: Extending PYTHONPATH with paths
['C:\\work\\01_ブログ\\01_python\\28_icon\\someip_s']
844 INFO: checking Analysis
858 INFO: Building because inputs changed        
858 INFO: Initializing module dependency graph...
861 INFO: Caching module graph hooks...
875 INFO: Analyzing base_library.zip ...
2645 INFO: Loading module hook 'hook-encodings.py' from 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks'...
3804 INFO: Loading module hook 'hook-pickle.py' from 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks'...4326 INFO: Loading module hook 'hook-heapq.py' from 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks'...
4757 INFO: Caching module dependency graph...
4871 INFO: running Analysis Analysis-00.toc
4885 INFO: Adding Microsoft.Windows.Common-Controls to dependent assemblies of final executable
  required by c:\users\h1110\appdata\local\programs\python\python39\python.exe
5040 INFO: Analyzing C:\work\01_ブログ\01_python\28_icon\someip_s\app.py
5757 INFO: Loading module hook 'hook-platform.py' from 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks'...
5911 INFO: Loading module hook 'hook-difflib.py' from 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks'...
6095 INFO: Loading module hook 'hook-sysconfig.py' from 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks'...
6477 INFO: Loading module hook 'hook-PIL.py' from 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks'...
6525 INFO: Loading module hook 'hook-PIL.Image.py' from 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks'...
7154 INFO: Loading module hook 'hook-xml.py' from 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks'...
7235 INFO: Loading module hook 'hook-xml.etree.cElementTree.py' from 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks'...
7473 INFO: Loading module hook 'hook-pycparser.py' from 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\_pyinstaller_hooks_contrib\\hooks\\stdhooks'...
7806 INFO: Processing pre-find module path hook distutils from 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks\\pre_find_module_path\\hook-distutils.py'.
7882 INFO: Loading module hook 'hook-distutils.py' from 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks'...
7924 INFO: Loading module hook 'hook-distutils.util.py' from 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks'...
8151 INFO: Loading module hook 'hook-setuptools.py' from 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks'...
c:\users\h1110\appdata\local\programs\python\python39\lib\site-packages\setuptools\distutils_patch.py:25: UserWarning: Distutils was imported before Setuptools. This usage is discouraged and may exhibit undesirable behaviors or errors. Please use Setuptools' objects directly or at least import Setuptools first.    
  warnings.warn(
9280 INFO: Loading module hook 'hook-pkg_resources.py' from 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks'...
10739 INFO: Processing pre-safe import module hook setuptools.extern.six.moves from 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks\\pre_safe_import_module\\hook-setuptools.extern.six.moves.py'.
11899 INFO: Loading module hook 'hook-multiprocessing.util.py' from 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks'...
12522 INFO: Loading module hook 'hook-PIL.ImageFilter.py' from 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks'...
12541 INFO: Processing module hooks...
12684 INFO: Loading module hook 'hook-PIL.SpiderImagePlugin.py' from 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks'...
12799 INFO: Processing pre-safe import module hook win32com from 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\_pyinstaller_hooks_contrib\\hooks\\pre_safe_import_module\\hook-win32com.py'.
12952 INFO: Loading module hook 'hook-win32com.py' from 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\_pyinstaller_hooks_contrib\\hooks\\stdhooks'...
12953 INFO: Loading module hook 'hook-pythoncom.py' from 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\_pyinstaller_hooks_contrib\\hooks\\stdhooks'...
13395 INFO: Loading module hook 'hook-pywintypes.py' from 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\_pyinstaller_hooks_contrib\\hooks\\stdhooks'...
14229 INFO: Loading module hook 'hook-packaging.py' from 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks'...
14420 INFO: Loading module hook 'hook-setuptools.msvc.py' from 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks'...
14722 INFO: Loading module hook 'hook-_tkinter.py' from 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks'...
14723 INFO: checking Tree
14775 INFO: checking Tree
14786 INFO: checking Tree
15503 INFO: Looking for ctypes DLLs
15639 INFO: Analyzing run-time hooks ...
15642 INFO: Including run-time hook 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_pkgutil.py'
15644 INFO: Including run-time hook 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_multiprocessing.py'
15645 INFO: Including run-time hook 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_setuptools.py'
15647 INFO: Including run-time hook 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_pkgres.py'
15651 INFO: Including run-time hook 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_win32comgenpy.py'
15653 INFO: Including run-time hook 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\_pyinstaller_hooks_contrib\\hooks\\rthooks\\pyi_rth_pywintypes.py'
15654 INFO: Including run-time hook 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\_pyinstaller_hooks_contrib\\hooks\\rthooks\\pyi_rth_pythoncom.py'
15655 INFO: Including run-time hook 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_inspect.py'
15658 INFO: Including run-time hook 'c:\\users\\h1110\\appdata\\local\\programs\\python\\python39\\lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth__tkinter.py'
15682 INFO: Looking for dynamic libraries
c:\users\h1110\appdata\local\programs\python\python39\lib\site-packages\setuptools\distutils_patch.py:25: UserWarning: Distutils was imported before Setuptools. This usage is discouraged and may exhibit undesirable behaviors or errors. Please use Setuptools' objects directly or at least import Setuptools first.    
  warnings.warn(
211 INFO: Extra DLL search directories (AddDllDirectory): []
212 INFO: Extra DLL search directories (PATH): ['C:\\Program Files\\Amazon Corretto\\jdk18.0.2_9\\bin', 'C:\\Program Files (x86)\\Intel\\Intel(R) Management Engine Components\\iCLS\\', 'C:\\Program Files\\Intel\\Intel(R) Management Engine Components\\iCLS\\', 'C:\\Windows\\system32', 'C:\\Windows', 'C:\\Windows\\System32\\Wbem', 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\', 'C:\\Windows\\System32\\OpenSSH\\', 'C:\\Program Files (x86)\\Intel\\Intel(R) Management Engine Components\\DAL', 'C:\\Program Files\\Intel\\Intel(R) Management Engine Components\\DAL', 'C:\\Program Files (x86)\\Intel\\Intel(R) Management Engine Components\\IPT', 'C:\\Program Files\\Intel\\Intel(R) Management Engine Components\\IPT', 'C:\\Program Files (x86)\\NVIDIA Corporation\\PhysX\\Common', 'C:\\Program Files\\NVIDIA Corporation\\NVIDIA NvDLISR', 'C:\\Program Files\\dotnet\\', 'C:\\Program Files\\nodejs\\', 'C:\\WINDOWS\\system32', 'C:\\WINDOWS', 'C:\\WINDOWS\\System32\\Wbem', 'C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\', 'C:\\WINDOWS\\System32\\OpenSSH\\', 'C:\\Program Files\\TortoiseSVN\\bin', 'C:\\Program Files\\Git\\cmd', 'C:\\Program Files\\TortoiseGit\\bin', 'C:\\Program Files\\Tesseract-OCR', 'C:\\Program Files\\OpenSSL-Win64\\bin', 'C:\\Users\\h1110\\AppData\\Local\\Programs\\Python\\Python39\\Scripts\\', 'C:\\Users\\h1110\\AppData\\Local\\Programs\\Python\\Python39\\', 'C:\\Users\\h1110\\AppData\\Local\\Microsoft\\WindowsApps', 'C:\\Users\\h1110\\AppData\\Roaming\\npm', 'C:\\Users\\h1110\\AppData\\Local\\Programs\\Microsoft VS Code\\bin', 'C:\\Users\\h1110\\AppData\\Local\\Google\\Cloud SDK\\google-cloud-sdk\\bin', 'C:\\Users\\h1110\\AppData\\Local\\GitHubDesktop\\bin']
16745 INFO: Looking for eggs
16746 INFO: Using Python library c:\users\h1110\appdata\local\programs\python\python39\python39.dll
16746 INFO: Found binding redirects:
[]
16750 INFO: Warnings written to C:\work\01_ブログ\01_python\28_icon\someip_s\build\app\warn-app.txt
16805 INFO: Graph cross-reference written to C:\work\01_ブログ\01_python\28_icon\someip_s\build\app\xref-app.html
16857 INFO: checking PYZ
16861 INFO: Building because name changed
16861 INFO: Building PYZ (ZlibArchive) C:\work\01_ブログ\01_python\28_icon\someip_s\build\app\PYZ-00.pyz
17563 INFO: Building PYZ (ZlibArchive) C:\work\01_ブログ\01_python\28_icon\someip_s\build\app\PYZ-00.pyz completed successfully.
17575 INFO: checking PKG
17584 INFO: Building because name changed
17586 INFO: Building PKG (CArchive) app.pkg
Traceback (most recent call last):
  File "c:\users\h1110\appdata\local\programs\python\python39\lib\runpy.py", line 197, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "c:\users\h1110\appdata\local\programs\python\python39\lib\runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "C:\Users\h1110\AppData\Local\Programs\Python\Python39\Scripts\pyinstaller.exe\__main__.py", line 7, in <module>
  File "c:\users\h1110\appdata\local\programs\python\python39\lib\site-packages\PyInstaller\__main__.py", line 194, in _console_script_run
    run()
  File "c:\users\h1110\appdata\local\programs\python\python39\lib\site-packages\PyInstaller\__main__.py", line 180, in run
    run_build(pyi_config, spec_file, **vars(args))
  File "c:\users\h1110\appdata\local\programs\python\python39\lib\site-packages\PyInstaller\__main__.py", line 61, in run_build
    PyInstaller.building.build_main.main(pyi_config, spec_file, **kwargs)
  File "c:\users\h1110\appdata\local\programs\python\python39\lib\site-packages\PyInstaller\building\build_main.py", line 977, in main
    build(specfile, distpath, workpath, clean_build)
  File "c:\users\h1110\appdata\local\programs\python\python39\lib\site-packages\PyInstaller\building\build_main.py", line 899, in build
    exec(code, spec_namespace)
  File "app.spec", line 25, in <module>
    exe = EXE(
  File "c:\users\h1110\appdata\local\programs\python\python39\lib\site-packages\PyInstaller\building\api.py", line 576, in __init__
    self.pkg = PKG(
  File "c:\users\h1110\appdata\local\programs\python\python39\lib\site-packages\PyInstaller\building\api.py", line 233, in __init__
    self.__postinit__()
  File "c:\users\h1110\appdata\local\programs\python\python39\lib\site-packages\PyInstaller\building\datastruct.py", line 173, in __postinit__
    self.assemble()
  File "c:\users\h1110\appdata\local\programs\python\python39\lib\site-packages\PyInstaller\building\api.py", line 304, in assemble
    archive_toc.append((dest_name, src_name, self.cdict.get(typecode, False), self.xformdict[typecode]))
KeyError: 'Data'