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.

Reply via email to