Create a standalone Windows installer for your Python application

I am developing a scientific application in Python with a graphical user interface in Qt. Some end-users use OS X or Linux, but most of them are Windows users who are not familiar with Python or with a command-line interface. It is notoriously difficult to distribute Python applications to end-users who are not programmers, and it's a common criticism that is made against Python.

I thought for a long time that there was nothing that could be done about that. But I recently wanted to find a solution. A complicated installation process can really be a barrier to entry for regular Windows users. So I've been looking for days for the right way to create a full standalone installer that installs everything (Python, external packages, and the software) along with icons on the desktop and the Start menu. I tried different approaches and none was satisfying at first. In particular, I tried to combine multiple installers for Python and the numerous external packages (NumPy, Matplotlib, PyQt, etc., coming from Chris Gohlke's webpage) into a single installer. It looked like something difficult to do, especially when the installers are .exe files and not .msi files. I tried to use some script to launch all installers successively and have the Next buttons pressed automatically, but it was not working very well and it was a bit of a mess.

I finally found a solution that I find quite satisfying. I haven't found this solution clearly explained anywhere on the Web, so I'm sharing it here. The goal is to distribute any Python application (possibly with a GUI) as a single-file .exe installer. No dependencies, no prerequisites, just a regular setup.exe file that installs everything, including a full standalone, isolated Python distribution, into C:\Program Files\MyApp\ like a regular program. The end-user does not even need to know that the software is written in Python.

This solution can be summarized in two words: WinPython and Inno Setup.

WinPython is a free scientific Python distribution for Windows that is portable, meaning that it is completely isolated from the rest of the system. WinPython can be installed in a folder, which can be moved anywhere, even on an USB stick, remaining entirely functional. WinPython is bundled with a lot of existing Python packages, but the huge advantage is that it includes graphical and command-line utilities to install/uninstall any Python package easily (from a zip or Windows binary installer generated by distutils).

Inno Setup is a free software for creating Windows installers. Building an installer requires to write a plain text script that contains the instructions about what to install and where. It has a lot of features, including desktop and start menu shortcuts, post-install scripts, etc. A wizard allows one to get started quickly and easily.

Here is a summary of what you need to do to create an installer for your Python application. First, create a folder with a WinPython distribution including all your Python dependencies, and your own Python package as well. Second, create an installer that will just copy this folder somewhere on the end-user hard drive, along with shortcuts to run your application. Then, you can customize the installation process as you wish.

Step 1: customize your WinPython distribution

  • Create a folder named MyApplication somewhere on your development machine. This folder will be copied to C:\Program Files\MyApplication by the installer.
  • Download and install WinPython into, say, MyApplication\WinPython-64bit-2.7.5.0.
  • WinPython contains a GUI that lets you install/uninstall Python packages. There are a lot of packages built in, but you can remove those that you don't need for your application. You can also add new Python packages by dragging zip or exe installers (created with distutils) into the GUI.

Step 2: create the installer

  • Install Inno Setup.
  • Use the wizard to create a new installer file (it is just a .iss text file).
  • Tell the wizard to copy your folder MyApplication to C:\Program Files\MyApplication. The ISS code for this looks like this:

    [Files]
    Source: "D:\Dev\MyApplication"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
    

    Note the {app} variable which contains the user application path. See the list of Inno Setup constants here.

Step 3: create the shortcuts

Create some shortcuts. Here is the ISS code to create a shorcut in the Start menu launching a Python GUI application:

[Icons]
Name: "{group}\Application"; Filename: "{app}\WinPython-64bit-2.7.5.0\python-2.7.5.amd64\pythonw.exe"; WorkingDir: "{app}"; Parameters: """{app}\WinPython-64bit-2.7.5.0\python-2.7.5.amd64\Lib\site-packages\myapplication\scripts\runmyapp.py"""; IconFilename: "{app}\favicon.ico"

This code snippet assumes that the Python script running your application is in myapplication\scripts\runmyapp.py. You could also have this script somewhere directly in your application folder and not necessarily in your Python package. You can also specify an icon as a .ico file.

You can create shortcuts on the desktop too. See more details here.

Step 4: customize the installation process

Inno Setup allows you to customize the installation process as you wish. Together with Python scripts, you can really achieve anything. For instance, here is how you can run a Python script at the end of the installation process.

[Run]
Filename: "{app}\WinPython-64bit-2.7.5.0\python-2.7.5.amd64\python.exe"; WorkingDir: "{app}"; Parameters: """{app}\postinstall.py"""; Flags: runhidden

Here are a few directives that you can use to customize some aspects of the installation wizard (icon, images, colors...):

[Setup]
SetupIconFile=D:\Dev\myapp\favicon.ico
WizardImageFile=wizard.bmp
WizardImageStretch=no
WizardSmallImageFile=wizard-small.bmp
WizardImageBackColor=$ffffff

Python code to install packages

You can create Python scripts that the user runs by clicking on an icon (see the [Icons] section above). You can for instance create a small utility that updates automatically your application by checking the last installer version on a server, and downloading and executing it automatically. Here is some Python code snippet that installs or updates a Python package in the user's WinPython distribution, from a zip or a Windows package created with distutils.

import sys
from winpython import wppm, utils

try:
    dist = wppm.Distribution(sys.prefix)
    package = wppm.Package(pathtoexefile)
    dist.install(package)
except:
    raise Exception("Unable to install the package.")

Conclusion

This solution may not be adapted to everyone. But I think it is best for regular Windows users who are used to do everything with the mouse and who are scared by command line interfaces. It allows any Python developer to create and distribute a graphical application as easily as for a more standard C++ program. I imagine that games for Windows could be written with Python and be easily distributed like this. Kivy, a Python library used in media applications and games, uses a similar technique as far as I know.

Finally, do take the time to browse Inno Setup's documentation. It is clear and well organized. And take a look to WinPython, it is nice and powerful, even if the documentation could be better. Actually I may start using it as my day-to-day Python distribution.