Recipe Gtk Application
Clone this wiki locally
Bundling a GTK Application (in OS X)
Note: The current version of pyinstaller should have the appropriate runtime hooks builtin, so that these steps are unnecessary when building a GTK3 app that uses PyGObject.
(Published by Charles S. Sharman on the Mailinglist.)
This recipe documents the steps necessary to get a working bundled OS X gtk application. I used python 2.7.7 and pyinstaller 2.1 under homebrew. You must have a working application from source before bundling.
Editor's note: The basics of this recipe should be the same for other platforms.
Begin by changing to your application directory and letting pyinstaller create its own spec file:
pyinstaller -w MyApp.py
where MyApp.py is the name of my application. The -w option makes pyinstaller create a .app bundle.
For a light gtk application, you may be able to change to the
dist/MyApp folder and run MyApp. However, if you port it to another
computer, you'll be in trouble. gtk requires external helper files
that aren't copied by the current pyinstaller
gtk uses a
loaders.cache file to associate pixbuf files with certain
libraries. You'll need a local copy in your directory:
cp /usr/local/lib/gdk-pixbuf-2.0/210.0/loaders.cache .
gdk-pixbuf-query-loaders > loaders.cache
If you use the first command, find the correct path to loaders.cache. If you use the second command, it should create a local loaders.cache file, provided gdk-pixbuf-query-loaders is present on your machine. Now, edit the file replacing all the long paths with nothing. For example, I changed
and so on.
You must add a run-time hook to alert gtk of your loaders.cache location, and you must add loaders.cache and the loaders themselves to your .spec file, but I'll show that later.
Like Pixbuf, Pango expects two special file called pango.modules and pangox.aliases. You'll need a copy in your directory:
cp /usr/local/etc/pango/pango.modules .
pango-querymodules > pango.modules
If you use the first command, find the correct path to pango.modules. If you use the second command it should create a local pango.modules file, provided pango-querymodules is present on your machine. Now, edit the file replacing all the long paths with nothing. For example, I changed:
/usr/local/Cellar/pango/1.36.3/lib/pango/1.8.0/modules/pango-arabic-lang.so ArabicScriptEngineLang PangoEngineLang PangoRenderNone arabic:*
pango-arabic-lang.so ArabicScriptEngineLang PangoEngineLang PangoRenderNone arabic:*
pangox.aliases does not need to be edited. It simply can be copied:
cp /usr/local/etc/pango/pangox.aliases .
Make sure the path is correct on your system.
Now, pango cannot be alerted of all changes with environment variables like pixbuf could. Instead, pango can be alerted with a pangorc file. In the same directory, create a pangorc file with these contents:
[Pango] ModuleFiles = ./pango.modules [PangoX] AliasFiles = ./pangox.aliases
You must adjust your run-time hook to alert gtk of your pangorc file, and you must add the files and the libraries to your .spec file.
Run Time Hook
You can redirect gtk to your local loaders.cache, pango.modules, and
pangorc through environment variables set in a real-time hook.
Create a pyinstaller real-time hook called
osx_rthook.py with the
import os, sys os.environ['GDK_PIXBUF_MODULE_FILE'] = sys._MEIPASS + '/loaders.cache' os.environ['PANGO_LIBDIR'] = sys._MEIPASS os.environ['PANGO_RC_FILE'] = sys._MEIPASS + '/pangorc'
sys._MEIPASS is a pyinstaller-created string that points to your
Now it's time to update your spec file to include the real-time hook, the data files, and the binary files. Here's how it ought to look. Lines with '# Changed' or '# Added' appended are changed or added lines:
# -*- mode: python -*- a = Analysis(['MyApp.py'], pathex=['/Users/apple/Downloads/MyApp'], hiddenimports=None, hookspath=None, runtime_hooks=['osx_rthook.py']) # Changed pyz = PYZ(a.pure) exe = EXE(pyz, a.scripts, exclude_binaries=True, name='MyApp', debug=False, strip=None, upx=True, console=False ) base_dir = '.' # Added gtks = ['loaders.cache', 'pangorc', 'pango.modules', 'pangox.aliases'] # Added data_files = [(x, os.path.join(base_dir, x), 'DATA') for x in gtks] # Added more_binaries =  # Added pixbuf_dir = '/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders' # Added for pixbuf_type in os.listdir(pixbuf_dir): # Added if pixbuf_type.endswith('.so'): # Added more_binaries.append((pixbuf_type, os.path.join(pixbuf_dir, pixbuf_type), 'BINARY')) # Added pango_dir = '/usr/local/lib/pango/1.8.0/modules' # Added for pango_type in os.listdir(pango_dir): # Added if pango_type.endswith('.so'): # Added more_binaries.append((os.path.join('pango/1.8.0/modules', pango_type), os.path.join(pango_dir, pango_type), 'BINARY')) # Added coll = COLLECT(exe, data_files, # Changed a.binaries + more_binaries, # Changed a.zipfiles, a.datas, strip=None, upx=True, name='MyApp') app = BUNDLE(coll, name='MyApp.app', icon=None)
To add an icon to your app, change the last line
MyApp.icns is the name of your icon file.
These steps allowed me to get a working PyInstaller bundle for an OS X gtk application. I suspect there may be more gotchas if your application extends gtk's abilities beyond MyApp. Additionally, MacPorts or pkgsrc may require alternate adjustments.
If it proves to be a universal OS X recipe, it ought to be coded into pyinstaller itself.