PyInstaller Package Python Applications (Windows, Mac and Linux)

PyInstaller is a program used to convert Python scripts into standalone applications. PyInstaller allows you to run applications written in Python on a computer without requiring the user to install Python. It is an excellent option when you need to distribute a program to the end user as a standalone application. PyInstaller currently only works with Python 2.3 up to 2.7.

PyInstaller claims to be compatible with a lot of third party libraries or packages out of the box. PyQt, Django and matplotlib are fully supported.

To start, download and extract PyInstaller from the official site.

PyInstaller is an application, not a package. So there is no need to install it in your computer. To get started, open a Command Prompt (Windows) or a Terminal (Mac and Linux) and go to the PyInstaller Folder.

cd pyinstaller

Now suppose you want to package myscript.py, I saved it into the pyinstaller folder:

# myscript.py
print('Hello World!')

Then, in order to create the executable just run python pyinstaller.py myscript.py and you will see a lot of output and a folder called myscript will be created, with two folders and a file inside. The build folder is used by PyInstaller as a temporary folder to create the files needed for the executable. The dist folder stores the executable and all of the files needed in order to run that executable. It is safe to delete the build folder. The file called myscript.spec is useful to customize the way PyInstaller packs your application.

Now test if your executable works:

Windows

cd myscript/dist/myscript
myscript

Mac and Linux

cd myscript/dist/myscript
./myscript

You should see now a “Hello World!” printed on the screen.

Remember that running python pyinstaller.py myscript.py assumes that you have Python in your path environment variable. If that is not the case, just use C:\Python27\python.exe pyinstaller.py myscript.py in Windows. Most of the time on Linux and Mac OS X, Python will be in your path environment variable.

GUI Applications

Now it is time to create a GUI application. In this example we will be using Tkinter:

# tkexample.py
'''A very basic Tkinter example. '''
import Tkinter
from Tkinter import *
root = Tk()
root.title('A Tk Application')
Label(text='I am a label').pack(pady=15)
root.mainloop()

To pack it, it is mandatory to use the --windowed flag, otherwise the application will not start:

python pyinstaller.py --windowed tkexample.py

In this moment you can navigate to the dist folder and run the application by double clicking it.

Note for Mac OS X users: the above example using Tkinter works fine if you use the pre-installed Python version, if you installed or updated Python by yourself, you can find some problems running the packaged application.

Using External Modules

The previous examples were importing modules from the Python Standard Library. PyInstaller includes the Standard Library Modules by default. However if we installed a third party library, PyInstaller is likely not to include it. In most of the cases we need to create “hooks” to tell PyInstaller to include these modules. An example of this is an application using the ReportLab library to make PDF files:

# invoice.py
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter
from reportlab.lib.units import mm
 
if __name__ == '__main__':
    name = u'Mr. John Doe'
    city = 'Pereira'
    address = 'ELM Street'
    phone = '555-7241'
    c = canvas.Canvas(filename='invoice.pdf', pagesize= (letter[0], letter[1]/2))
    c.setFont('Helvetica', 10)
    # Print Customer Data
    c.drawString(107*mm, 120*mm, name)
    c.drawString(107*mm, 111*mm, city)
    c.drawString(107*mm, 106*mm, address)
    c.drawString(107*mm, 101*mm, phone)
    c.showPage()
    c.save()

A “hook” module is a Python file with a special name, used to tell PyInstaller to include a specific module. Doing a Google search i found the needed hooks to pack a ReportLab application here and placed them in a folder named “hooks”:

+- hooks/
|- hook-reportlab.pdfbase._fontdata.py
|- hook-reportlab.pdfbase.py
|- hook-reportlab.py

hook-reportlab.py and hook-reportlab.pdfbase.py are empty files, hook-reportlab.pdfbase._fontdata.py contains:

# hook-reportlab.pdfbase._fontdata.py
hiddenimports = [
    '_fontdata_enc_macexpert',
    '_fontdata_enc_macroman',
    '_fontdata_enc_pdfdoc',
    '_fontdata_enc_standard',
    '_fontdata_enc_symbol',
    '_fontdata_enc_winansi',
    '_fontdata_enc_zapfdingbats',
    '_fontdata_widths_courier',
    '_fontdata_widths_courierbold',
    '_fontdata_widths_courierboldoblique',
    '_fontdata_widths_courieroblique',
    '_fontdata_widths_helvetica',
    '_fontdata_widths_helveticabold',
    '_fontdata_widths_helveticaboldoblique',
    '_fontdata_widths_helveticaoblique',
    '_fontdata_widths_symbol',
    '_fontdata_widths_timesbold',
    '_fontdata_widths_timesbolditalic',
    '_fontdata_widths_timesitalic',
    '_fontdata_widths_timesroman',
    '_fontdata_widths_zapfdingbats'
]

Now in order to pack the executable, we have to run python pyinstaller.py --additional-hooks-dir=hooks/ invoice.py. The additional-hooks-dir flag tells PyInstaller to search for hooks in the specified directory.

Conclusion

Pyinstaller works great if your script only imports modules from The Python Standard Library, or a module included in the official Supported Packages list. Using these supported packages make packaging applications pretty straightforward, but when wee need to use a third-party not supported module, it can be tricky to make it work.

Leave a Reply

Your email address will not be published. Required fields are marked *