create your

import sys
import pygtk
if not sys.platform == 'win32':
import gtk

from mainwindow import MainWindow

if __name__ == '__main__':
    # enable threading

    # create the main window
    myapp = MainWindow()

    # start the program loop

    # cleanup

create your file:

from distutils.core import setup
import py2exe
import os

# Find GTK+ installation path
m = sys.modules['gtk']
gtk_base_path = m.__path__[0]

    name = 'handytool',
    description = 'Some handy tool',
    version = '1.0',

    windows = [
                      'script': '',
                      'icon_resources': [(1, "handytool.ico")],

    options = {
                  'py2exe': {
                      # Optionally omit gio, gtk.keysyms, and/or rsvg if you're not using them
                      'includes': 'cairo, pango, pangocairo, atk, gobject, gio, gtk.keysyms, rsvg',

                   # If using GTK+'s built in SVG support, uncomment these
                   #os.path.join(gtk_base_path, '..', 'runtime', 'bin', 'gdk-pixbuf-query-loaders.exe'),
                   #os.path.join(gtk_base_path, '..', 'runtime', 'bin', 'libxml2-2.dll'),

run py2exe. You'll get a warning:

The following modules appear to be missing
['gdk', 'ltihooks']

Ignore it ;)

Once that's done, you'll need to copy the etc, lib and share directories from your GTK+ install (not the pygtk install) to the dist dir py2exe created. Optionaly, you can clean the share\locale dir to include only the locales you need for GTK+. Same thing for share\themes (I left both Default and MS-Windows).

If you are using GtkBuilder and later versions of GTK+ and PyGTK, you may get the following error message:

Y:\my_handytool\dist\\gtk\ RuntimeWarning: tp_compare didn't return -1 or -2 for exception
ImportError: could not import gio
ImportError: could not import gio
Traceback (most recent call last):
  File "", line 88, in <module>
  File "", line 75, in __init__
AttributeError: 'module' object has no attribute 'Builder'

This issue can be resolved by adding the 'gio' module to the py2exe includes option in the file:

---        2010-11-13 22:35:20.000000000 +0100
+++    2010-11-13 20:26:02.000000000 +0100
@@ -14,7 +14,7 @@
     options = {
         'py2exe': {
-            'includes': 'cairo, pango, pangocairo, atk, gobject',
+            'includes': 'cairo, pango, pangocairo, atk, gobject, gio',

If you are using kiwi, you will also have to copy the contents of the (path to python)/share/kiwi directory (and (path to python)/share/gazpacho, if you used gazpacho) to the dist dir. And you will have to create an empty 'kiwi' directory in the dist/pixmaps directory that you just copied there. (note: I have not tested kiwi with the Innosetup installer, but if you try it and it works edit this page and remove this note :) )

Note you'll want to set the working directory (Start in:) for any shortcut you create to the application directory containing the executable.

(Optional) an Innosetup .iss file to create an installer for handytool:

AppVerName=handytool 1.0
VersionInfoCompany=me inc

Name: {app}; Flags: uninsalwaysuninstall;

Source: dist\*; DestDir: {app}; Flags: ignoreversion recursesubdirs createallsubdirs

Name: {group}\handytool; Filename: {app}\handytool.exe; WorkingDir: {app}

; If you are using GTK's built-in SVG support, uncomment the following line.
;Filename: {cmd}; WorkingDir: "{app}"; Parameters: "/C gdk-pixbuf-query-loaders.exe > lib/gdk-pixbuf-2.0/2.10.0/loaders.cache"; Description: "GDK Pixbuf Loader Cache Update"; Flags: nowait runhidden
Filename: {app}\handytool.exe; Description: {cm:LaunchProgram,handytool}; Flags: nowait postinstall skipifsilent

This Recipe has been tested with: