In this tutorial we’ll be using py2app to create a standalone OSX application from a Python source code with a simple Tkinter user interface.
"py2app is a Python setuptools command which will allow you to make standalone application bundles and plugins from Python scripts. py2app is similar in purpose and design to py2exe for Windows."
Relevant links about py2app:
- Docs
- Source on BitBucket (last commit 2015-05-05)
- Issue Tracker
- Mailing List
This guide is loosely based on the official tutorial.
Based on a Python file called Sandwich.py
, we’ll create an application called Sandwich.app
.
Prerequisites
Create a custom directory and create a virtualenv:
# Create a custom directory$ mkdir SandwichApp
$ cd SandwichApp
# Use virtualenv to create an isolated environment$ virtualenv venv
$ . venv/bin/activate
Now create a very simple Tkinter app with the filename Sandwich.py
:
fromTkinterimport*root=Tk()root.title("Sandwich")Button(root,text="Make me a Sandwich").pack()mainloop()
Install py2app
The original version of py2app has a bug due to a newer version of ModuleGraph. I made a fork of the project and fixed this bug on Github. Install it with pip like this:
$ pip install -U git+https://github.com/metachris/py2app.git@master
Create a setup.py
file
py2app includes py2applet
, a helper which generates a setup.py file for you:
$ py2applet --make-setup Sandwich.py
Wrote setup.py
This setup.py
is a basic definition of the app:
fromsetuptoolsimportsetupAPP=['Sandwich.py']DATA_FILES=[]OPTIONS={'argv_emulation':True}setup(app=APP,data_files=DATA_FILES,options={'py2app':OPTIONS},setup_requires=['py2app'],)
If your application uses some data files, like a JSON, text files or images, you should include them in DATA_FILES. For example:
DATA_FILES=['testdata.json','picture.png']
Build the app for development and testing
py2app builds the standalone application based on the definition in setup.py
.
For testing and development, py2app provides an “alias mode”, which builds an app with symbolic links to the development files:
$ python setup.py py2app -A
This creates the following files and directories:
.
├── 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
This is not a standalone application, and the applications built in alias mode are not portable to other machines!
The app built with alias mode simply references the original code files, so any changes you make to the original Sandwich.py
file are instantly available on the next app start.
The resulting development app in dist/Sandwich.app
can be opened just like any other .app with the Finder
or the open command ($ open dist/Sandwich.app
). To run your application directly from the Terminal
you can just run:
$ ./dist/Sandwich.app/Contents/MacOS/Sandwich
Building for deployment
When everything is tested you can produce a build for deployment with a calling python setup.py py2app
. Make sure that any old build
and dist
directories are removed:
$ rm -rf build dist
$ python setup.py py2app
This will assemble your application as dist/Sandwich.app
. Since this application is self-contained, you will have to run the py2app command again any time you change any source code, data files, options, etc.
The original py2app has a bug which would display “AttributeError: 'ModuleGraph' object has no attribute 'scan_code'
” or load_module
. If you encounter this error, take
a look at this StackOverflow thread or use my fork of py2app.
The easiest way to wrap your application up for distribution at this point is simply to right-click the application from Finder and choose “Create Archive”.
Adding an icon
Simply add "iconfile": "youricon.icns"
to the OPTIONS
dict:
fromsetuptoolsimportsetupAPP=['Sandwich.py']DATA_FILES=[]OPTIONS={'argv_emulation':True,'iconfile':'app.icns'}setup(app=APP,data_files=DATA_FILES,options={'py2app':OPTIONS},setup_requires=['py2app'],)
Advanced app settings
You can tweak the application information and behaviour with modifications
to the Info.plist
. The most complete reference for the keys available is Apple’s Runtime Configuration Guidelines.
Here is an example with more modifications:
# -*- coding: utf-8 -*-fromsetuptoolsimportsetupAPP=['Sandwich.py']APP_NAME="SuperSandwich"DATA_FILES=[]OPTIONS={'argv_emulation':True,'iconfile':'app.icns','plist':{'CFBundleDisplayName':APP_NAME,'CFBundleGetInfoString':"Making Sandwiches",'CFBundleIdentifier':"com.metachris.osx.sandwich",'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'],)
References
If you have suggestions, feedback or ideas, please reach out to me @metachris.