Design thinking + open source = freedom for mere mortals.

How to make standalone OS X application bundles from PyQt apps using py2app

Historical content: I wrote this article over 5 years ago on 2008-12-01. You are viewing an archived post from my old WordPress blog. The archive contains over 1,500 articles written over a ten year period. The formatting and contents of the posts may not display perfectly.

How to make standalone OS X application bundles from PyQt apps using py2app

You can build cross-platform GUI applications in Python using a library called PyQt, a set of Python bindings for Nokia's Qt application framework. Using PyQt, you can create Python applications that look and feel like native applications regardless of whether they happen to be running on Linux, OS X, or Windows.

The application files themselves, however, do not look like native executables; they are simply Python files and require Python, PyQt, and other dependencies to be installed to run. Needless to say, you cannot distribute your applications in this manner unless your target audience is entirely comprised of alpha geeks. To provide a usable user experience that is consistent with native applications, you can bundle PyQt applications into native executables for each of the platforms you're supporting. When deploying on OS X, for example, you can bundle your PyQt application into a standalone OS X application bundle. This process, which involves the use of the excellent py2app application, comes with several gotchas that I hadn't bargained on.

Specifically, you cannot build standalone application bundles with the system python in OS X using py2app. If you want to build standalone PyQt apps, I recommend setting up Python, PyQt, etc., under MacPorts.

If you're comfortable with Terminal and don't need to be spoon fed, here's a quick overview of how to get your system in shape to compile standalone OS X application bundles from PyQt apps using py2app on Leopard 10.5.5 (which happens to be the only operating system version I've tested this on; this may work on other versions of OS X but YMMV).

Note that I am installing the -devel version of py25-py2app, as well as py25-macholib-devel on purpose. I couldn't get it to work with the stable versions. You may have to deactivate py25-macholib if it gets installed automatically as a dependency for py25-py2app-devel (this is what I did, but I'm assuming that installing py25-macholib-devel before py25-py2app-devel should mean that you won't have to).

Deploying a PyQt application as a Mac OS X application bundle

Once you've successfully installed everything under MacPorts, change your environment to use the version of Python in Macports via python_select:

sudo python_select python25

Once you're done, create a setup.py file for py2app. Something along the lines of the following example:

Note that the includes array contains sip and PyQt4._qt. Those two entries are essential if you're bundling a PyQt app.

Finally, issue the following command to create a standalone OS X application bundle from your application:

python setup.py py2app

That's it. Double click on your app, which you can find in the dist folder and it should run.

If you run into any problems, see the My Macports setup section, below, for an exact dump of my Macports portfiles that you can use to compare (obtained using port installed).

Read on if you want to the full story and details of how I got my system up and running with MacPorts to deploy OS X app bundles using PyQt and py2app. I'm including this with as much information (error messages, etc., as possible so that others may find this post if they're similarly stuck.) Or, if you're not interested, you can jump to the Reference section.

The gory details

My initial attempt at creating a standalone PyQt app with py2app resulted in the app crashing on launch.

The error message:

Fatal Python error: Interpreter not initialized (version mismatch?)

Judging by the error message, and emboldened by a forum exchange between Derrick Hendricks and Christopher Baker I guessed that the conflict was due to my having two versions of Python installed (the System python and a 2.5 through MacPorts). So I removed MacPorts completely, ran py2app again and the app launched but with a different error:

ImportError: '/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/lib-dynload/PyQt4/QtCore.so' not found

Searching for a solution, I stumbled upon a thread where someone who ran into the same issue suggested adding the following line to __boot__.py in the Contents/Resources folder of the application bundle:

It was only when I actually read the output from py2app that I noticed that it appeared to be creating a semi-standalone app instead of the standalone app that I asked it to create. In retrospect, I should have realized this from the very first error since I should not have gotten a version mismatch error on the Python interpreter if it had been correctly embedded in the app bundle.

First of all, if you want to build bundle .app's that are redistributable, it's a good idea not to use the system Python, especially if you want to support Tiger (10.4). There are things in Apple's Python (i.e. DTrace support) that I imagine won't play nicely on an older OS.

At this point, I knew that I had to install a separate Python and decided to use MacPorts. It did take a while to install everything but that approach paid off in the end.

Reference

My Macports setup

Here's a full list of my MacPorts setup (which I set up specifically to compile PyQt apps with py2app, so there aren't any superfluous packages in there) which you can compare to yours if something isn't working:

Note that at one point during the PyQt installation, MacPorts aborted with:

Target org.macports.activate returned: Image error: /opt/local/bin/a2p is being used by the active perl5.8 port. Please deactivate this port first, or use the -f flag to force the activation.

It appears that there were two versions of PERL installed. I simply restarted the installation (sudo port install py25-pyqt) and, after waiting a long while, and having to restart it once more, it completed successfully in installing Qt and PyQt.

(Note that the PyQt installation will take seemingly forever so it might be an idea to (a) start it in verbose mode, as shown above, so you know it's still doing something and hasn't hung, and (b) set up a growlnotify message to let you know when the process is complete.)

Comments

Well that looks intuitive!

by steveballmer on 2008-12-10 02:14:54

Thanks, this helped a lot. I was getting stuck in the version mismatch error and this post had everything I needed to get thing

by Tico on 2008-12-09 07:20:52

Thank you so much for that, you saved many hours of debugging and googling. My project http://astmontray.sf.net thanks you too :-)

by caio1982 on 2009-01-17 13:42:08

I've build a binary with py2app, but PyQt4._qt is pulling redundan't Qt components like QtDesigner, xml, dbus, network etc. which app doesn't need. only QtCore and QtGui. If I remove PyQt4._qt from the list then the binary is small, but it doesn't want to work, as it doesn't find PyQt4._qt :)
Is there a way to exclude unneeded PyQt4/Qt4 components without breaking the build?

by riklaunim on 2009-06-03 10:35:30

Thanks for this guide, however…!
1) Building Qt & PyQt via MacPorts is currently broken, but it's quite easy to workaround.
2) I have the same problem as riklaunim. Before I followed your guide, I had installed everything by myself (Qt .dmg bundle from QtSoftware), compiled sip and PyQt… And it was enough to import individual PyQt4.s in my app. Then py2app picked up only the frameworks needed.
But with your guide, it doesn't pick them up unless I import PyQt4._qt. I don't get it. When I try specify the frameworks by hand in setup.py, the frameworks cannot be found. Eh.

by Lukáš 'Spike' Polívka on 2009-07-14 04:40:02

thank you aral.
do you happen to have _any_ solution to the plugins dependency problem (i.e. the fact that plugins do not correctly get linked, and for instance using webkit to display pages will result in images not showing)?

by roberto on 2009-09-26 20:11:42

BTW, if you are trying this now, you'll see an error trying to install py25-macholib-devel, but I found that if install revision 46161 of serf after python25, then py25-macholib-devel will build and install OK.

by Stephen Hartley on 2009-11-11 14:07:11

After fixing (downgrading) serf, you may still see this error when using py2applet:
/usr/bin/strip: for architecture ppc64 object: .../Contents/Frameworks/Python.framework/Versions/2.5/Python malformed object (load command 3 cmdsize not a multiple of 8)
To fix this, install python25 with +universal, e.g.
sudo port install python25 +universal

by Stephen Hartley on 2009-11-11 14:49:33

[...] are some ressources around about deploying Python and PyQt applications on Mac OS X, e.g. here, here, and here, and if you read them, you maybe will see, that it isn’t without hitches. You have [...]

by How to make standalone OS X application bundles from PyQt apps using py2app « 那些日子,花开花落,云卷云舒… on 2010-08-12 21:57:48

[...] basic steps for using MacPorts to get a PyQt environment were published by Aral Balkan in his py2app tutorial. The order in which he lists the steps did not work for me because his instructions tell you switch [...]