You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5.2 KiB

Packaging Gooey as a Stand Alone Application

⚠️ Packaging Gooey is an ongoing science. Gooey currently runs on all the major platforms, can be installed in a bajillion different ways, and has several active versions in wide usage. In short, edge cases abound. If you run into any problems, [this issue0(https://github.com/chriskiehl/Gooey/issues/259) is a great place to dump whatever errors you're running into. Just be sure to give as many details about your setup, what you've tried, and the errors you're seeing!

Instructions tested against Gooey 1.0.3 and pyinstaller 3.5 on Mojave 10.14.6

This is all made possible by the work from @john174586, @jrjhealey's, and the research of numerous others.

Pull requests with additional steps and packaging information are highly welcome.

Packing Gooey into a standalone executable is super straight forward thanks to PyInstaller. It is the only dependency you'll need and can be installed via the following.

pip install pyinstaller

Setting up the build instructions:

PyInstaller uses spec files to determine how to bundle the project. These are a bit like setup.py files, but contain rules for how PyInstaller should bundle your whole application as a stand alone executable.

Note: if you're still rocking Python 2.7, see the Python2.7 section at the bottom for a few additional steps.

These provide a list of directives on how to build the package, setup hooks to run, and resources to include.

This file should be placed in the root of your project.

e.g.

MyProject/
   - src/
   - build.spec  # <-- goes here!
   - LICENCE.txt
   - README.md

You can grab a pre-built spec file here, or create a build.spec by hand in the root directory of your project and paste in the following:

# -*- mode: python ; coding: utf-8 -*-

import os
import platform
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')

from PyInstaller.building.api import EXE, PYZ, COLLECT
from PyInstaller.building.build_main import Analysis
from PyInstaller.building.datastruct import Tree
from PyInstaller.building.osx import BUNDLE

block_cipher = None

a = Analysis(['APPNAME.py'],  # replace me with the app name 
          pathex=['/path/to/APP.py'],  # replace me with the appropriate path
          hiddenimports=[],
          hookspath=None,
          runtime_hooks=None,
          )
pyz = PYZ(a.pure)

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


exe = EXE(pyz,
       a.scripts,
       a.binaries,
       a.zipfiles,
       a.datas,
       options,
       gooey_languages,
       gooey_images,
       name='APPNAME',  # FIXME
       debug=False,
       strip=None,
       upx=True,
       console=False,
       icon=os.path.join(gooey_root, 'images', 'program_icon.ico'))

if platform.system() == 'Darwin':
 info_plist = {'addition_prop': 'additional_value'}
 app = BUNDLE(exe,
              name='APPNAME.app',
              bundle_identifier=None,
              info_plist=info_plist
             )

This is the standard build.spec generated by Pyinstaller, but with a few additions to ensure that all of Gooey's assets get pulled in correctly during bundling.

From this spec, you'll need to make two tweaks.

  1. replace APPNAME in the Analysis() section with the name of your application
  2. replace the pathex value in the Analysis() section with the path to your application's root

note: If you use additional data resources (e.g. images, data, etc..) you'll also need to explicitly add them to the EXE section. See [packaging custom images] for more info.

Windows Notes

OSX Notes:

Python 2.7

If you're on Python 2.7, you'll need to make one addition to your source file to ensure smooth output for Gooey. By default, the python interpreter will aggressively buffer stdout which means that rather than Gooey being able to read and display the output of your program in real time, it receives it in awkward 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

Troubleshooting

Exception: This program needs access to the screen. Please run with a Framework build of python, and only when you are logged in on the main display of your Mac.

This happens on OSX when you neglect the --windowed flag during your build step.

wrong:

pyinstaller build.spec  ## oops! forgot the required flags    

Correct:

pyinstaller --windowed build.spec 

Checkout the Pyinstaller Manual for more details.