Introduction

The following will explain how to freeze a Python/wxPython application using the py2exe tool.

What Objects are Involved

You will need a working development environment including

* Python

* wxPython

* py2exe

* Gui2Exe - by Andrea Gavana

Process Overview

A tiny sample wxPython "Hello world" application will be used to demonstrate the process. I created the tiny application using Boa Constructor but you could use any other IDE you use for your wxPython development. The creation of the setup.py file I did using Gui2Exe, but again one could create this by hand.

Special Concerns

Python 2.5x

Python 2.6x

The setup.py

Save the following code in your working folder as a file called 'setup.py'.

Toggle line numbers
   1 # ======================================================#
   2 # File automagically generated by GUI2Exe version 0.3
   3 # Andrea Gavana, 01 April 2007
   4 # ======================================================#
   5 
   6 # Let's start with some default (for me) imports...
   7 
   8 from distutils.core import setup
   9 import py2exe
  10 import glob
  11 import os
  12 import zlib
  13 import shutil
  14 
  15 # Remove the build folder
  16 shutil.rmtree("build", ignore_errors=True)
  17 
  18 print 'is this shown'
  19 
  20 class Target(object):
  21     """ A simple class that holds information on our executable file. """
  22     def __init__(self, **kw):
  23         """ Default class constructor. Update as you need. """
  24         self.__dict__.update(kw)
  25         
  26 
  27 # Ok, let's explain why I am doing that.
  28 # Often, data_files, excludes and dll_excludes (but also resources)
  29 # can be very long list of things, and this will clutter too much
  30 # the setup call at the end of this file. So, I put all the big lists
  31 # here and I wrap them using the textwrap module.
  32 
  33 data_files = []
  34 
  35 includes = []
  36 excludes = ['_gtkagg', '_tkagg', 'bsddb', 'curses', 'email', 'pywin.debugger',
  37             'pywin.debugger.dbgcon', 'pywin.dialogs', 'tcl',
  38             'Tkconstants', 'Tkinter']
  39 packages = []
  40 dll_excludes = ['libgdk-win32-2.0-0.dll', 'libgobject-2.0-0.dll', 'tcl84.dll',
  41                 'tk84.dll']
  42 icon_resources = []
  43 bitmap_resources = []
  44 other_resources = []
  45 
  46 
  47 # This is a place where the user custom code may go. You can do almost
  48 # whatever you want, even modify the data_files, includes and friends
  49 # here as long as they have the same variable name that the setup call
  50 # below is expecting.
  51 
  52 baseFolder, progFolder = os.path.split(os.getcwd())
  53 
  54 #
  55 # The following will copy the MSVC run time dll's
  56 # (msvcm90.dll, msvcp90.dll and msvcr90.dll) and
  57 # the Microsoft.VC90.CRT.manifest which I keep in the
  58 # "Py26MSdlls" folder to the dist folder
  59 #
  60 # depending on wx widgets you use, you might need to add
  61 # gdiplus.dll to the above collection
  62 
  63 py26MSdll = glob.glob(r"c:\dev\Py26MSdlls\*.*")
  64 
  65 data_files += [("", py26MSdll),
  66                           ]
  67 
  68 
  69 # Ok, now we are going to build our target class.
  70 # I chose this building strategy as it works perfectly for me :-D
  71 
  72 
  73 GUI2Exe_Target_1 = Target(
  74     # what to build
  75     script = "simplewx.py",
  76     icon_resources = icon_resources,
  77     bitmap_resources = bitmap_resources,
  78     other_resources = other_resources,
  79     dest_base = "simplewx",    
  80     version = "0.1",
  81     company_name = "No Company",
  82     copyright = "No Copyrights",
  83     name = "Py2Exe Sample File"
  84     )
  85 
  86 
  87 
  88 # That's serious now: we have all (or almost all) the options py2exe
  89 # supports. I put them all even if some of them are usually defaulted
  90 # and not used. Some of them I didn't even know about.
  91 
  92 setup(
  93 
  94     data_files = data_files,
  95 
  96     options = {"py2exe": {"compressed": 2, 
  97                           "optimize": 2,
  98                           "includes": includes,
  99                           "excludes": excludes,
 100                           "packages": packages,
 101                           "dll_excludes": dll_excludes,
 102                           "bundle_files": 3,
 103                           "dist_dir": "dist",
 104                           "xref": False,
 105                           "skip_archive": False,
 106                           "ascii": False,
 107                           "custom_boot_script": '',
 108                          }
 109               },
 110 
 111     zipfile = r'lib/library.zip',
 112     console = [],
 113     windows = [GUI2Exe_Target_1]
 114     )
 115 
 116 
 117 # And we are done. That's a setup script :-D
 118 
 119 # Run setup standalone...
 120 if __name__ == "__main__":
 121     print 'just running setup'
 122     setup()
 123     print 'setup done'
 124 
 125 print 'clean up'
 126 
 127 # This is a place where any post-compile code may go.
 128 # You can add as much code as you want, which can be used, for example,
 129 # to clean up your folders or to do some particular post-compilation
 130 # actions.
 131 
 132 # for some reason some files remain sometimes in dist, but they
 133 # are already present in dist/lib, so lets remove them
 134 # 
 135 
 136 cwdFolder = os.getcwd()
 137 libFile = os.path.join(cwdFolder, 'dist\\library.zip')
 138 if os.path.exists(libFile):
 139         print 'deleting duplicate files'
 140         os.remove(libFile)
 141         os.remove(os.path.join(cwdFolder, 'dist\\bz2.pyd'))
 142         os.remove(os.path.join(cwdFolder, 'dist\\select.pyd'))
 143         os.remove(os.path.join(cwdFolder, 'dist\\unicodedata.pyd'))

The MS manifest

Following is the content of the Microsoft manifest file, note that the content of "version" and "publicKeyToken" are specific to the version of the dll files. Installing Python 2.6 on Windows 7rc1 with the option "for this user only" this manifest file is created in the Python26 folder. Note the Py26 installer does not offer the "for this user only" option on Vista.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- Copyright (c) Microsoft Corporation.  All rights reserved. -->
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <noInheritable/>
    <assemblyIdentity
        type="win32"
        name="Microsoft.VC90.CRT"
        version="9.0.21022.8"
        processorArchitecture="x86"
        publicKeyToken="1fc8b3b9a1e18e3b"
    />
    <file name="msvcr90.dll" /> <file name="msvcp90.dll" /> <file name="msvcm90.dll" />
</assembly>

Sample wxPython application

Save the following code in your working folder as a file called 'simplewx.py'.

Toggle line numbers
   1 #Boa:Frame:Frame1
   2 
   3 import wx
   4 
   5 def create(parent):
   6     return Frame1(parent)
   7 
   8 [wxID_FRAME1, wxID_FRAME1PANEL1, wxID_FRAME1STATICTEXT1, 
   9 ] = [wx.NewId() for _init_ctrls in range(3)]
  10 
  11 class Frame1(wx.Frame):
  12     def _init_coll_bsPanel_Items(self, parent):
  13         # generated method, don't edit
  14 
  15         parent.AddWindow(self.staticText1, 0, border=10,
  16               flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER_HORIZONTAL | wx.ALL | wx.EXPAND)
  17 
  18     def _init_coll_bsFrame_Items(self, parent):
  19         # generated method, don't edit
  20 
  21         parent.AddWindow(self.panel1, 1, border=2,
  22               flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER_HORIZONTAL)
  23 
  24     def _init_sizers(self):
  25         # generated method, don't edit
  26         self.bsPanel = wx.BoxSizer(orient=wx.VERTICAL)
  27 
  28         self.bsFrame = wx.BoxSizer(orient=wx.VERTICAL)
  29 
  30         self._init_coll_bsPanel_Items(self.bsPanel)
  31         self._init_coll_bsFrame_Items(self.bsFrame)
  32 
  33         self.panel1.SetSizer(self.bsPanel)
  34         self.SetSizer(self.bsFrame)
  35 
  36     def _init_ctrls(self, prnt):
  37         # generated method, don't edit
  38         wx.Frame.__init__(self, id=wxID_FRAME1, name='', parent=prnt,
  39               pos=wx.Point(642, 279), size=wx.Size(124, 86),
  40               style=wx.DEFAULT_FRAME_STYLE, title='Frame1')
  41         self.SetClientSize(wx.Size(108, 50))
  42 
  43         self.panel1 = wx.Panel(id=wxID_FRAME1PANEL1, name='panel1', parent=self,
  44               pos=wx.Point(2, 2), size=wx.Size(104, 46),
  45               style=wx.TAB_TRAVERSAL)
  46 
  47         self.staticText1 = wx.StaticText(id=wxID_FRAME1STATICTEXT1,
  48               label=u'Hello world!', name='staticText1', parent=self.panel1,
  49               pos=wx.Point(10, 10), size=wx.Size(84, 13),
  50               style=wx.ALIGN_CENTRE)
  51 
  52         self._init_sizers()
  53 
  54     def __init__(self, parent):
  55         self._init_ctrls(parent)
  56         
  57 
  58 if __name__ == '__main__':
  59     app = wx.PySimpleApp()
  60     frame = create(None)
  61     frame.Show()
  62 
  63     app.MainLoop()

Sample wxPython application

Save the following code in your working folder as a file called 'simplewx.py'.

Toggle line numbers
   1 #Boa:Frame:Frame1
   2 
   3 import wx
   4 
   5 def create(parent):
   6     return Frame1(parent)
   7 
   8 [wxID_FRAME1, wxID_FRAME1PANEL1, wxID_FRAME1STATICTEXT1, 
   9 ] = [wx.NewId() for _init_ctrls in range(3)]
  10 
  11 class Frame1(wx.Frame):
  12     def _init_coll_bsPanel_Items(self, parent):
  13         # generated method, don't edit
  14 
  15         parent.AddWindow(self.staticText1, 0, border=10,
  16               flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER_HORIZONTAL | wx.ALL | wx.EXPAND)
  17 
  18     def _init_coll_bsFrame_Items(self, parent):
  19         # generated method, don't edit
  20 
  21         parent.AddWindow(self.panel1, 1, border=2,
  22               flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER_HORIZONTAL)
  23 
  24     def _init_sizers(self):
  25         # generated method, don't edit
  26         self.bsPanel = wx.BoxSizer(orient=wx.VERTICAL)
  27 
  28         self.bsFrame = wx.BoxSizer(orient=wx.VERTICAL)
  29 
  30         self._init_coll_bsPanel_Items(self.bsPanel)
  31         self._init_coll_bsFrame_Items(self.bsFrame)
  32 
  33         self.panel1.SetSizer(self.bsPanel)
  34         self.SetSizer(self.bsFrame)
  35 
  36     def _init_ctrls(self, prnt):
  37         # generated method, don't edit
  38         wx.Frame.__init__(self, id=wxID_FRAME1, name='', parent=prnt,
  39               pos=wx.Point(642, 279), size=wx.Size(124, 86),
  40               style=wx.DEFAULT_FRAME_STYLE, title='Frame1')
  41         self.SetClientSize(wx.Size(108, 50))
  42 
  43         self.panel1 = wx.Panel(id=wxID_FRAME1PANEL1, name='panel1', parent=self,
  44               pos=wx.Point(2, 2), size=wx.Size(104, 46),
  45               style=wx.TAB_TRAVERSAL)
  46 
  47         self.staticText1 = wx.StaticText(id=wxID_FRAME1STATICTEXT1,
  48               label=u'Hello world!', name='staticText1', parent=self.panel1,
  49               pos=wx.Point(10, 10), size=wx.Size(84, 13),
  50               style=wx.ALIGN_CENTRE)
  51 
  52         self._init_sizers()
  53 
  54     def __init__(self, parent):
  55         self._init_ctrls(parent)
  56         
  57 
  58 if __name__ == '__main__':
  59     app = wx.PySimpleApp()
  60     frame = create(None)
  61     frame.Show()
  62 
  63     app.MainLoop()

Comments

Please feel free to provide any feedback on this page either here, on the wxPython-user list or to werner.bruhin at free.fr.

NOTE: To edit pages in this wiki you must be a member of the TrustedEditorsGroup.