原文:Creating standalone Mac OS X applications with Python and py2app
說明:在翻譯之前,譯者已根據(jù)原文成功創(chuàng)建了一個(gè)應(yīng)用(Mac OS X 10.11.1, Python3.5),如讀者在操作中遇到問題,可在本文下方留言,共同探討。
PS: 據(jù)網(wǎng)友提醒,使用自帶 Python 將導(dǎo)致生成的應(yīng)用不能在其他 mac 上運(yùn)行。
源碼下載:SandwichAPP
本教程中,我們將會(huì)使用 Python2 或 Python3 的Tkinter接口,結(jié)合py2app創(chuàng)建一個(gè)獨(dú)立的 OSX 應(yīng)用。
“py2app 是一個(gè)讓你可以通過 Python 腳本創(chuàng)建獨(dú)立應(yīng)用包或插件的 Python 安裝包命令。py2app 的設(shè)計(jì)與目的與 Windows 的 py2exe相似?!?/p>
與 py2app 相關(guān)的鏈接:
- 文檔
- BitBucket 上的源碼(最后提交時(shí)間:2015-05-05)
- 問題跟蹤,郵件列表
本指南沒有嚴(yán)格基于官方教程。我們基于一個(gè) Sandwich.py 的 Python 文件創(chuàng)建了一個(gè)名為 Sandwich.app 的應(yīng)用。

前提
創(chuàng)建一個(gè)普通文件夾,并創(chuàng)建一個(gè)virtualenv環(huán)境:
# Create a custom directory
$ mkdir SandwichApp
$ cd SandwichApp
# Use virtualenv to create an isolated environment
$ virtualenv venv
$ . venv/bin/activate
現(xiàn)在,創(chuàng)建一個(gè)簡(jiǎn)單的Tkinter應(yīng)用,并其命名為 Sandwich.py:
import sys
if sys.version_info < (3, 0):
# Python 2
import Tkinter as tk
else:
# Python 3
import tkinter as tk
root = tk.Tk()
root.title("Sandwich")
tk.Button(root, text="Make me a Sandwich").pack()
tk.mainloop()
這個(gè)小應(yīng)用是這個(gè)樣子:

安裝 py2app
原版的 py2app 由于 ModuleGraph 更新了版本產(chǎn)生了一個(gè) bug。我 fork 了該工程,解決了 bug,然后放到了 Github。使用 pip 安裝 py2app:
$ pip install -U git+https://github.com/metachris/py2app.git@master
創(chuàng)建 setup.py 文件
py2app 包含了 py2applet 工具, 此工具可以幫你創(chuàng)建 setup.py 文件:
$ py2applet --make-setup Sandwich.py
Wrote setup.py
setup.py 文件是對(duì)應(yīng)用的基本定義:
from setuptools import setup
APP = ['Sandwich.py']
DATA_FILES = []
OPTIONS = {'argv_emulation': True}
setup(
app=APP,
data_files=DATA_FILES,
options={'py2app': OPTIONS},
setup_requires=['py2app'],
)
如果你的應(yīng)用使用的其他文件,比如 JSON 文件,文本文件,圖片等,你應(yīng)該將他們包含在 DATA_FILES 中。 例如:
DATA_FILES = ['testdata.json', 'picture.png']
創(chuàng)建開發(fā)版和測(cè)試版的應(yīng)用
py2app 基于在 setup.py 文件的定義創(chuàng)建獨(dú)立應(yīng)用。
為了方法測(cè)試和開發(fā),py2app 提供了“別名模式”,該模式通過與開發(fā)文件象征性的鏈接構(gòu)建應(yīng)用。
$ python setup.py py2app -A
此命令創(chuàng)建了如下文件和文件夾:
.
├── build
│ └── bdist.macosx-10.10-x86_64
│ └── python2.7-standalone
│ └── app
│ ├── Frameworks
│ ├── collect
│ ├── lib-dynload
│ └── temp
├── Sandwich.py
├── dist
│ └── Sandwich.app
│ └── Contents
│ ├── Info.plist
│ ├── MacOS
│ │ ├── Sandwich
│ │ └── python -> /Users/chris/Projects/chris/python-gui/tkinter/env/bin/../bin/python
│ ├── PkgInfo
│ └── Resources
│ ├── __boot__.py
│ ├── __error__.sh
│ ├── lib
│ │ └── python2.7
│ │ ├── config -> /Users/chris/Projects/chris/python-gui/tkinter/env/bin/../lib/python2.7/config
│ │ └── site.pyc -> ../../site.pyc
│ ├── site.py
│ └── site.pyc
└── setup.py
這并不是一個(gè)獨(dú)立的應(yīng)用,并且通過別名模式構(gòu)建的應(yīng)用不適用于其他機(jī)器。
別名模式下構(gòu)建的應(yīng)用直接引用了源碼文件,所以任何對(duì) Sandwich.py 文件作的修改在應(yīng)用下次啟動(dòng)時(shí)會(huì)立刻生效。
位于 dist/Sandwich.app 的開發(fā)應(yīng)用可以和其他 .app 應(yīng)用一樣,在 Finder 中或通過 open 命令($ open dist/Sandwich.app)啟動(dòng)。你可以在終端執(zhí)行如下命令啟動(dòng)你的應(yīng)用:
$ ./dist/Sandwich.app/Contents/MacOS/Sandwich
構(gòu)建發(fā)布版應(yīng)用
當(dāng)測(cè)試通過后,你可以通過調(diào)用 python setup.py py2app 來生成發(fā)布版。確保舊的 build 和 dist 文件類都被刪除了:
$ rm -rf build dist
$ python setup.py py2app
此命令會(huì)將你的應(yīng)用打包為 dist/Sandwich.app。由于該應(yīng)用是自包含的,在任意時(shí)刻,如果你修改了代碼,數(shù)據(jù)文件,選項(xiàng)等,你都可以再次運(yùn)行 py2app 命令重新構(gòu)建。
原版的 py2app 有一個(gè) bug,會(huì)出現(xiàn) “AttributeError: 'ModuleGraph' object has no attribute 'scan_code'” 或者 load_module。如果你遇到此錯(cuò)誤,請(qǐng)參考 StackOverflow 或者使用我的 py2app fork。
此時(shí)此刻,最簡(jiǎn)單的打包并發(fā)布應(yīng)用的方法是在 Finder 中右擊該應(yīng)用選擇“創(chuàng)建歸檔”。
添加一個(gè)圖標(biāo):
在 OPTIONS 字典中添加 "iconfile": "youricon.icns" 即可:
from setuptools import setup
APP = ['Sandwich.py']
DATA_FILES = []
OPTIONS = {
'argv_emulation': True,
'iconfile': 'app.icns'
}
setup(
app=APP,
data_files=DATA_FILES,
options={'py2app': OPTIONS},
setup_requires=['py2app'],
)
你可以在網(wǎng)上找到 icns 格式的圖標(biāo)(例如:IconFinder或者freepik)。
應(yīng)用高級(jí)設(shè)置
你可以通過修改 Info.plist 來調(diào)用應(yīng)用的信息各行為。最完整的對(duì)可用的鍵的引用是Apple's Runtime Configuratin Guidelines。
下面是一個(gè)有更多修改的例子:
# -*- coding: utf-8 -*-
from setuptools import setup
APP = ['Sandwich.py']
APP_NAME = "SuperSandwich"
DATA_FILES = []
OPTIONS = {
'argv_emulation': True,
'iconfile': 'app.icns',
'plist': {
'CFBundleName': APP_NAME,
'CFBundleDisplayName': APP_NAME,
'CFBundleGetInfoString': "Making Sandwiches",
'CFBundleIdentifier': "com.metachris.osx.sandwich",
'CFBundleVersion': "0.1.0",
'CFBundleShortVersionString': "0.1.0",
'NSHumanReadableCopyright': u"Copyright ? 2015, Chris Hager, All Rights Reserved"
}
}
setup(
name=APP_NAME,
app=APP,
data_files=DATA_FILES,
options={'py2app': OPTIONS},
setup_requires=['py2app'],
)
通過設(shè)置,應(yīng)用會(huì)擁有下面的信息:

參考鏈接
其他鏈接
- 向 App Store 提交 Python 應(yīng)用(dafoster.net)
- PyInstaller - 另一個(gè)創(chuàng)建跨平臺(tái)獨(dú)立應(yīng)用的工具(全面支持 PyQt, Django 和 matplotlib)
- rumps - 簡(jiǎn)單到極致的 Mac OS X 上的 Python 狀態(tài)欄應(yīng)用
- py2exe - Windows 平臺(tái)的 py2app
- cx_Freeze - 另一個(gè)創(chuàng)建 Windows 上 .exe 的工具包
- 在 Hacker News 上討論此話題