Bundling a GTK Application in OS X
==================================
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.
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 hook-gtk.py.
Correcting Pixbuf
-----------------
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 .
or::
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::
"/usr/local/Cellar/gdk-pixbuf/2.30.8/lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-ani.so"
to::
"libpixbufloader-ani.so"
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.
Correcting Pango
----------------
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 .
or::
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:*
to::
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.
Real 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
following contents::
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
bundled directory.
Spec File
---------
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 'icon=None' to
'icon=MyApp.icns', where MyApp.icns is the name of your icon file.
Conclusion
----------
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.
--
You received this message because you are subscribed to the Google Groups
"PyInstaller" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/pyinstaller.
For more options, visit https://groups.google.com/d/optout.