Python
macos
PyInstaller

pyinstallerの*.specファイル作成法。

More than 1 year has passed since last update.

作成されたpython scriptをpyinstallerを用いてMacOSのアプリを作れます。

最初僕はpy2appを使用しましたが結果ファイルサイズが以下のように違いました。

- Py2appの結果:35MB

- Pyinstallerの結果:25MB

Pyinstallerを使わないわけがないでしょう。

インストールはPIPで可能になりまして。(昨年はできませんでした)


pip install pyinstaller


それで完了

pyinstallerの駆動は*.specファイルを通じてするのがお勧めです。

specファイルの基本構造は下のようです。

# -*- mode: python -*-

# filename = Main.spec

a = Analysis(['Main.py'],
pathex=['/Users/.../ProgramNameDir'],
hiddenimports=[],
hookspath=None,
runtime_hooks=None)
pyz = PYZ(a.pure)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
name='Main',
debug=False,
strip=None,
upx=True,
console=False , icon='mainIcon.ico')
app = BUNDLE(exe,
name='Main.app',
icon='mainIcon.ico')

この場合はmain.pyを読んでMain.appを作ります。iconはmainIcon.icoをかぶります。

Main.pyは ”/Users/.../ProgramNameDir”に存在し、その下の "./lib"のlibファイルを参考しています。なので *.specファイルが"./lib"を認識できるように少し修正します。

# -*- mode: python -*-

# filename = Main.spec

### 個人の./libsを認識するためのコード ###
import sys
myLibPath = './libs'
sys.path.append(myLibPath)
############################

a = Analysis(['Main.py'],
pathex=['/Users/.../ProgramNameDir'],
hiddenimports=[],
hookspath=None,
runtime_hooks=None)
pyz = PYZ(a.pure)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
name='Main',
debug=False,
strip=None,
upx=True,
console=False , icon='mainIcon.ico')
app = BUNDLE(exe,
name='Main.app',
icon='mainIcon.ico')

もう一つの問題があります。Main.pyでは /Users/.../ProgramNameDir/img内のイメージ・ファイルをつかいます。これはsys.path.appendで追加しても最終のアプリでは読めません。イメージ・ファイルをハンドリング仕組みが違うからです。

pyinstallerで作られたアプリが駆動する際、必要なファイル(イメージ・ファイルなど)は臨時フォルダ(temp folder)内「_MEIPASS」という場所でコピーします。だから*.pyコード内に「_MEIPASS」を認識させる必要があります。

Main.pyでこの関数を追加します。

def resource_path(relative):

if hasattr(sys, "_MEIPASS"):
return os.path.join(sys._MEIPASS, relative)
return os.path.join(relative)

そしてコード内のイメージ参照こどを変換します。

例え:

"./img/Title.png" --> resource_path("./img/Title.png")

*.specファイルにはTree()を追加します。

# -*- mode: python -*-

#filename = Main.spec
import sys
myLibPath = './libs'
sys.path.append(myLibPath)
a = Analysis(['Main.py'],
pathex=['/Users/.../ProgramNameDir'],
hiddenimports=[],
hookspath=None,
runtime_hooks=None)
pyz = PYZ(a.pure)
exe = EXE(pyz,
Tree('img',prefix='img'), #<--追加するイメージがあるフォルダ名
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
name='Main',
debug=False,
strip=None,
upx=True,
console=False , icon='mainIcon.ico')
app = BUNDLE(exe,
name='Main.app',
icon='mainIcon.ico')

僕はMacBook proを使っています。即ちRetina Displayを使っています。pyinstallerで作られたアプリをそのまま作るとRetina Displayの上にはメッセージ・ボックスとかダイアログが滲んで見えるので醜いです。

それを解決するためBUNDLEにinfo_plistを追加します。

# -*- mode: python -*-

#filename = Main.spec
import sys
myLibPath = './libs'
sys.path.append(myLibPath)
a = Analysis(['Main.py'],
pathex=['/Users/.../ProgramNameDir'],
hiddenimports=[],
hookspath=None,
runtime_hooks=None)
pyz = PYZ(a.pure)
exe = EXE(pyz,
Tree('img',prefix='img'),
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
name='Main',
debug=False,
strip=None,
upx=True,
console=False , icon='mainIcon.ico')
app = BUNDLE(exe,
name='Main.app',
       info_plist={ 'NSHighResolutionCapable': 'True'}, #<-- Option for High Resolution
icon='mainIcon.ico')

これで大体におわりです。

pyinstaller Main.spec

でアプリができます。

py2exeとかpy2appの場合は事例がずいぶんあるけどpyinstallerの場合はあんまりないので整理のため記録しました。