Packaging Gooey as a Distributable Application

For packaging, Gooey supports two options: CxFreeze, and PyInstaller.  We're going to focus on PyInstaller as it's my preferred wrapper due to its ability to make end user friendly single file executables on Windows, OSX, and Linux. Meaning, this: 

Instead of this:

Prerequisites 

If you don't already have it, download the latest PyInstaller version and install it via setuptools. 


Step 1: Preparing your script for packaging. 

By default, the python interpreter buffers stdout. In short, this means that rather than Gooey being able to read the output of your program in real time, it receives it in very coarsely grained chunks.

Normally, passing the -u flag to the Python interpreter would be enough to prevent stdout buffering. However, due to an apparent quirk in PyInstaller, it seems that the -u flag never makes its way up to the interpreter, and thus the output is still highly buffered. Luckily, getting around this pretty easy. 

Add these two lines to your source anywhere before your main() (or equivalent) method. 


  nonbuffered_stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
  sys.stdout = nonbuffered_stdout

This will reopen Python's `stdout` in write mode with buffer size of 0 ( which forces it to constantly flush). This let's Gooey read from your program in realtime as it's generating output. 

Step 2: Creating a PyInstaller buildspec

PyInstaller uses spec files to determine how to bundle the project. These provide a list of directives on how to build the package, setup hooks to run, and resources to include.  

You can download a pre-built spec file here, or create a file called `build.spec` in the directory of your project and paste the following: 



import gooey
gooey_root = os.path.dirname(gooey.__file__)
gooey_languages = Tree(os.path.join(gooey_root, 'languages'), prefix = 'gooey/languages')
gooey_images = Tree(os.path.join(gooey_root, 'images'), prefix = 'gooey/images')

a = Analysis(['YOUR_APP.py'],
             pathex=['c:\\Python27\\Scripts'],
             hiddenimports=[],
             hookspath=None,
             runtime_hooks=None,
             )
pyz = PYZ(a.pure)

options = [('u', None, 'OPTION')]

exe = EXE(pyz,
          a.scripts,
          a.binaries,
          a.zipfiles,
          a.datas,
          options,
          gooey_languages, # Add them in to collected files
          gooey_images, # Same here.
          name='CHANGE_ME',
          debug=False,
          strip=None,
          upx=True,
          console=False,
          icon=os.path.join(gooey_root, 'images', 'program_icon.ico'))

For the most part, this is the default spec file which PyInstaller generates. However, there are a few modification to instruct PyInstaller to pull in all of the non-code resources used by Gooey. Credit goes to roshgar for figuring this all out!

With this spec, you'll just need to make two changes:

  1. Update the application path in the Analysis constructor to point at your Python script
  2. update the name argument in the EXE constructor to the name of your program.

Step 3: Build your project

The final step is to feed the spec file to PyInstaller and let it do its magic. 

Open a terminal in the directory with your spec file and enter

pyinstaller build.spec

Done!

PyInstaller will stick your packaged executable in a directory called dist. Open that up and you'll find your shiney new app all ready to distribute.