Deploy PyQt Applications on Mac OS X with PyInstaller!

The interweb seem to incline on py2app when it come to deploying applications on mac. I’ve tried to make a single deployable .app file for my application for a long time trying to follow these instructions from ars technica. I’m not a hacker and just want to produce a deployable usable application for others to use. And it seems py2app from MacPorts wasn’t able to surmount the Snow Leopard’s 64-bit compatibility issue.

And then, I was slacking off while studying for my final and out of nowhere I found PyInstaller‘s explicit support for PyQt and its recent support for the mac. And after trying, almost everything works out without much of a kink. Credit goes to ChrisWayg who produced an amazingly complete and up-to-date set of instructions to follow. I’m merely telling how my application did using his instructions (April 2010) and hopefully doing my part to draw more attention to the excellent PyInstaller.

My Application

A PyQt application to help photographers apply custom watermarks in a manual but in an assisted way.

Uses:

  • PIL 1.1.7
  • Qt4 4.6.2
  • PyQt4 4.7.2
  • SIP 4.10.1

All from MacPorts on Python 2.6.5, OSX 10.6.3.

Prepare PyInstaller

PyInstaller is kinda special in that it’s not installed anywhere. It’s just a bunch of Python scripts that work on stuff in place. While you’re at it, read the manual.

svn co http://svn.pyinstaller.org/trunk ~/PyInstaller

Or put it somewhere you want.

Then you need to build the bootloaders (no longer necessary with the newest version!). If you’re making PyQt applications, you probably have XCode already. Anyway, you need it.

cd source/linux
python Make.py
make
cd ..
python Configure.py

Making your Application

So far, PyInstaller has a problem of not showing your application at the front when you run it. You can fix it in your own code by adding

form.raise_()

right after

form.show()

with “form” being whatever you called your instance of MainWindow().

Like with py2app, you gotta first make a “spec file”.

python Makespec.py --onefile /path/to/YourApplication.py

This will make a YourApplication/YourApplication.spec file in your PyInstaller folder.
To make it work on mac, add

import sys
if sys.platform.startswith("darwin"):
    app = BUNDLE(exe,
                 appname='WhateverYouWantToCallIt',
                 version=1)

at the bottom.

For my application, it made 70 files inside the .app file on default. –onefile makes it more neat and 3 times smaller in size! but it does make it a bit slower. If you care to make it as fast as possible, leave out that option but you have to fix a bug yourself.

Finally, build the application!

python Build.py YourApplication/YourApplication.spec

This will make your application and leave a YourApplication.app in your PyInstaller folder.

Fix the Application

Put your own nice icon on the application. Copy your graphic, right-click your application and choose Get Info, click the icon and paste the graphic.

Open the content of your application, open Contents/Info.plist, uncheck “Application is background only” and save. If you’re using a plain text editor, change LSBackgroundOnly to false.

There is only one last problem to deal with on mac. You have to copy the qt_menu.nib file.

locate qt_menu.nib

to find it. If you use MacPorts, it’s probably somewhere like /opt/local/libexec/qt4-mac/lib/QtGui.framework/Versions/4/Resources/qt_menu.nib

Copy it to your YourApplication.app/Contents/Resources

Boom, you can double click your application and it will run!

Ok, one last problem, I swear. When you run, it shows 2 icons on the dock… pretty ugly. This hack can fix it, but it’s not perfect as it leaves junk on your computer. Hopefully PyInstaller can address it soon.

Conclusion

The program is beautiful! You don’t have to worry about your Python plug-ins, your 64-bitness, anything (mostly). It just works. I have yet to figure out how to make 32-bit binaries on 64-bit systems and how to make upx work properly to reduce executable size. I’m currently running at 32mb for an application that really should be 1mb. But then again, it’s packaging a whole new GUI framework that doesn’t exist on the target computer with the application. Consider Skype (yes, it’s infinitely more complex than my thing), it is still 43mb.