Differences between revisions 14 and 15
Revision 14 as of 2010-05-12 13:28:16
Size: 11849
Editor: 204
Comment:
Revision 15 as of 2010-05-28 08:10:05
Size: 12100
Editor: 226
Comment:
Deletions are marked like this. Additions are marked like this.
Line 35: Line 35:
  * With 2.6.5 I get an error "msvcp90.dll is not found", the work around is to change py2exe/build_exe.py and add this "excludes_use.append("msvcp90.dll")" around line 1056, or as an alternative you can add "msvcp90.dll" to the "dll_excludes" list.

Using py2exe to freeze your application

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 small 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. I did the initial creation of the setup.py file with Gui2Exe and then keep maintaining it and running it from the IDE (Boa in my case).

Special Concerns

Python 2.5x

  • you will need the MS C run time dll 'msvcr71.dll', included with Python
  • the dll 'gdiplus.dll' might also be needed depending on what wxPython widgets you use
  • the appname.manifest file (needed to get the nice themed widgets on XP+) can be generated by checking the appropriate option in Gui2Exe

Python 2.6x

  • you will need the MS C run time dll's (msvcr90.dll, msvcp90.dll, msvcm90.dll), included with Python (additional information provided below)
  • you will need a copy of the Microsoft.VC90.CRT.manifest file, additional information provided below
  • the dll 'gdiplus.dll' might also be needed depending on what wxPython widgets you use
  • do not check the manifest option in Gui2Exe, that caused a problem when I tried it and anyhow it should no longer be needed - so I need to verify this again.

  • I need to remove dist/UxTheme.dll in order having a correct look and feel under XP. It could be specific to my configuration (wxPython 2.8.10.1, python 2.6.4, xp SP3 FR)
  • With 2.6.5 I get an error "msvcp90.dll is not found", the work around is to change py2exe/build_exe.py and add this "excludes_use.append("msvcp90.dll")" around line 1056, or as an alternative you can add "msvcp90.dll" to the "dll_excludes" list.

The setup.py

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

Note that I use "bundle=3" and use a "lib/library.zip" to reduce the number of files, I found that "bundle=1 or 2" does not always work. Actually with Py2.6 I could not get them to work at all. To deliver a single file to your end-users check out the InnoSetup page.

To "freeze" the application you run the following command from the command line in your working folder.

\python26\python setup.py py2exe

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 # do the save for dist folder
  19 shutil.rmtree("dist", ignore_errors=True)
  20 
  21 class Target(object):
  22     """ A simple class that holds information on our executable file. """
  23     def __init__(self, **kw):
  24         """ Default class constructor. Update as you need. """
  25         self.__dict__.update(kw)
  26         
  27 
  28 # Ok, let's explain why I am doing that.
  29 # Often, data_files, excludes and dll_excludes (but also resources)
  30 # can be very long list of things, and this will clutter too much
  31 # the setup call at the end of this file. So, I put all the big lists
  32 # here and I wrap them using the textwrap module.
  33 
  34 data_files = []
  35 
  36 includes = []
  37 excludes = ['_gtkagg', '_tkagg', 'bsddb', 'curses', 'email', 'pywin.debugger',
  38             'pywin.debugger.dbgcon', 'pywin.dialogs', 'tcl',
  39             'Tkconstants', 'Tkinter']
  40 packages = []
  41 dll_excludes = ['libgdk-win32-2.0-0.dll', 'libgobject-2.0-0.dll', 'tcl84.dll',
  42                 'tk84.dll']
  43 icon_resources = []
  44 bitmap_resources = []
  45 other_resources = []
  46 
  47 
  48 # This is a place where the user custom code may go. You can do almost
  49 # whatever you want, even modify the data_files, includes and friends
  50 # here as long as they have the same variable name that the setup call
  51 # below is expecting.
  52 
  53 baseFolder, progFolder = os.path.split(os.getcwd())
  54 
  55 #
  56 # The following will copy the MSVC run time dll's
  57 # (msvcm90.dll, msvcp90.dll and msvcr90.dll) and
  58 # the Microsoft.VC90.CRT.manifest which I keep in the
  59 # "Py26MSdlls" folder to the dist folder
  60 #
  61 # depending on wx widgets you use, you might need to add
  62 # gdiplus.dll to the above collection
  63 
  64 py26MSdll = glob.glob(r"c:\dev\Py26MSdlls\*.*")
  65 
  66 # following works from Windows XP +
  67 # if you need to deploy to older MS Win versions then I found that on Win2K
  68 # it will also work if the files are put into the application folder without
  69 # using a sub-folder.
  70 data_files += [("Microsoft.VC90.CRT", py26MSdll),
  71                ("lib\Microsoft.VC90.CRT", py26MSdll),
  72               ]
  73 
  74 
  75 # Ok, now we are going to build our target class.
  76 # I chose this building strategy as it works perfectly for me :-D
  77 
  78 
  79 GUI2Exe_Target_1 = Target(
  80     # what to build
  81     script = "simplewx.py",
  82     icon_resources = icon_resources,
  83     bitmap_resources = bitmap_resources,
  84     other_resources = other_resources,
  85     dest_base = "simplewx",    
  86     version = "0.1",
  87     company_name = "No Company",
  88     copyright = "No Copyrights",
  89     name = "Py2Exe Sample File"
  90     )
  91 
  92 
  93 
  94 # That's serious now: we have all (or almost all) the options py2exe
  95 # supports. I put them all even if some of them are usually defaulted
  96 # and not used. Some of them I didn't even know about.
  97 
  98 setup(
  99 
 100     data_files = data_files,
 101 
 102     options = {"py2exe": {"compressed": 2, 
 103                           "optimize": 2,
 104                           "includes": includes,
 105                           "excludes": excludes,
 106                           "packages": packages,
 107                           "dll_excludes": dll_excludes,
 108                           "bundle_files": 3,
 109                           "dist_dir": "dist",
 110                           "xref": False,
 111                           "skip_archive": False,
 112                           "ascii": False,
 113                           "custom_boot_script": '',
 114                          }
 115               },
 116 
 117     zipfile = "lib\library.zip",
 118     console = [],
 119     windows = [GUI2Exe_Target_1]
 120     )
 121 
 122 # This is a place where any post-compile code may go.
 123 # You can add as much code as you want, which can be used, for example,
 124 # to clean up your folders or to do some particular post-compilation
 125 # actions.
 126 
 127 # And we are done. That's a setup script :-D

The MS manifest

Following is the content of the Microsoft manifest file ("Microsoft.VC90.CRT.manifest"), note that the content of "version" and "publicKeyToken" are specific to the version of the dll files.

Installing Python 2.6 on Windows with the option "for this user only" this manifest file is created in the Python26 folder, note that the Py26 installer does not offer the "for this user only" option on Vista.

If you install Python 2.6 for all users then these files are found in "C:\Windows\winsxs", i.e. in "C:\Windows\winsxs\Manifests" you will find guess what the manifest and it would be called "x86_microsoft.vc90.crt_1fc8b3b9a1e18e3b_9.0.21022.8_none_bcb86ed6ac711f91.manifest" and then in "C:\Windows\winsxs\x86_microsoft.vc90.crt_1fc8b3b9a1e18e3b_9.0.21022.8_none_bcb86ed6ac711f91" you will find the dll's.

Note that the folder names contain the version number, so with Python 2.6.2 you should use version 9.0.21022.8 manifest and dll's.

<?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_FRAME1BUTTON1, wxID_FRAME1BUTTON2, wxID_FRAME1PANEL1, 
   9  wxID_FRAME1STATICTEXT1, 
  10 ] = [wx.NewId() for _init_ctrls in range(5)]
  11 
  12 class Frame1(wx.Frame):
  13     def _init_coll_bsPanel_Items(self, parent):
  14         # generated method, don't edit
  15 
  16         parent.AddWindow(self.staticText1, 0, border=10,
  17               flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER_HORIZONTAL | wx.ALL | wx.EXPAND)
  18         parent.AddSizer(self.fgsButtons, 1, border=2, flag=wx.ALL | wx.EXPAND)
  19 
  20     def _init_coll_fgsButtons_Items(self, parent):
  21         # generated method, don't edit
  22 
  23         parent.AddWindow(self.button1, 1, border=2, flag=wx.ALL | wx.EXPAND)
  24         parent.AddWindow(self.button2, 1, border=2, flag=wx.ALL | wx.EXPAND)
  25 
  26     def _init_coll_bsFrame_Items(self, parent):
  27         # generated method, don't edit
  28 
  29         parent.AddWindow(self.panel1, 1, border=2,
  30               flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER_HORIZONTAL)
  31 
  32     def _init_coll_fgsButtons_Growables(self, parent):
  33         # generated method, don't edit
  34 
  35         parent.AddGrowableCol(0)
  36         parent.AddGrowableCol(1)
  37 
  38     def _init_sizers(self):
  39         # generated method, don't edit
  40         self.bsPanel = wx.BoxSizer(orient=wx.VERTICAL)
  41 
  42         self.bsFrame = wx.BoxSizer(orient=wx.VERTICAL)
  43 
  44         self.fgsButtons = wx.FlexGridSizer(cols=2, hgap=0, rows=0, vgap=0)
  45 
  46         self._init_coll_bsPanel_Items(self.bsPanel)
  47         self._init_coll_bsFrame_Items(self.bsFrame)
  48         self._init_coll_fgsButtons_Items(self.fgsButtons)
  49         self._init_coll_fgsButtons_Growables(self.fgsButtons)
  50 
  51         self.SetSizer(self.bsFrame)
  52         self.panel1.SetSizer(self.bsPanel)
  53 
  54     def _init_ctrls(self, prnt):
  55         # generated method, don't edit
  56         wx.Frame.__init__(self, id=wxID_FRAME1, name='', parent=prnt,
  57               pos=wx.Point(642, 279), size=wx.Size(236, 106),
  58               style=wx.DEFAULT_FRAME_STYLE, title='Frame1')
  59         self.SetClientSize(wx.Size(220, 70))
  60 
  61         self.panel1 = wx.Panel(id=wxID_FRAME1PANEL1, name='panel1', parent=self,
  62               pos=wx.Point(2, 2), size=wx.Size(216, 66),
  63               style=wx.TAB_TRAVERSAL)
  64 
  65         self.staticText1 = wx.StaticText(id=wxID_FRAME1STATICTEXT1,
  66               label=u'Hello world!', name='staticText1', parent=self.panel1,
  67               pos=wx.Point(10, 10), size=wx.Size(196, 13),
  68               style=wx.ALIGN_CENTRE)
  69 
  70         self.button1 = wx.Button(id=wxID_FRAME1BUTTON1, label='button1',
  71               name='button1', parent=self.panel1, pos=wx.Point(4, 37),
  72               size=wx.Size(100, 23), style=0)
  73 
  74         self.button2 = wx.Button(id=wxID_FRAME1BUTTON2, label='button2',
  75               name='button2', parent=self.panel1, pos=wx.Point(116, 37),
  76               size=wx.Size(100, 23), style=0)
  77 
  78         self._init_sizers()
  79 
  80     def __init__(self, parent):
  81         self._init_ctrls(parent)
  82         
  83 
  84 if __name__ == '__main__':
  85     app = wx.PySimpleApp()
  86     frame = create(None)
  87     frame.Show()
  88 
  89     app.MainLoop()

Other wiki pages to check

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.

py2exe (last edited 2010-08-21 18:54:09 by c-75-72-71-98)

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