Tag: py2exe

How to get XP Styles on wxPython apps made with py2exe

Let me begin with this: wxPython is fantastic! I could write a moderately simple app in ~17 hours which automates a task that is normally done manually loading data into a web form. The app consists on custom wizard classes, access to web resources (requesting and parsing) and session management with cookies. I’m impressed that I could finish the application, with no previous knowledge on any of the aforementioned subjects, in such a short time span.

Publicity aside, I faced a little problem. I packed the python application into an executable that didn’t require a full python install using the py2exe extension, after reading the guide on how to get py2exe to work with wxPython. My first problem was some silly issue with some MSCV*.dll files:

*** finding dlls needed ***
error: MSVCP90.dll: No such file or directory

After a quick read on several places, I found a simple solution for this problem, ignoring the dll file when building  the executables.

With that solved, I could successfully build and launch my application. The less obvious problem was, that even though the application launched it looked ugly! Here’s a screenshot on what I was getting when running the generated exe vs running the .py file directly:

normal_theme-vs-xp_theme

Hideous indeed. The problem was that for some reason the application was not using XP Themes when drawing the application’s widgets, and after some reading I discovered that there are some serious issues regarding Python2.6 (the version I’m using), wxPython and py2exe when it comes to manifests. Long story short, I had to associate a correct manifest to my exe file, and after lurking around my Python installs, I found a manifest that wxPython creates for Python that served my needs:

<?xml version=’1.0′ encoding=’UTF-8′ standalone=’yes’?>
<assembly xmlns=’urn:schemas-microsoft-com:asm.v1′ manifestVersion=’1.0′>
<trustInfo xmlns=”urn:schemas-microsoft-com:asm.v3″>
<security>
<requestedPrivileges>
<requestedExecutionLevel level=’asInvoker’ uiAccess=’false’ />
</requestedPrivileges>
</security>
</trustInfo>
<dependency>
<dependentAssembly>
<assemblyIdentity
type=’win32′
name=’Microsoft.VC90.CRT’
version=’9.0.21022.8′
processorArchitecture=’*’
publicKeyToken=’1fc8b3b9a1e18e3b’ />
</dependentAssembly>
</dependency>
<dependency>
<dependentAssembly>
<assemblyIdentity
type=”win32″
name=”Microsoft.Windows.Common-Controls”
version=”6.0.0.0″
processorArchitecture=”*”
publicKeyToken=”6595b64144ccf1df”
language=”*” />
</dependentAssembly>
</dependency>
</assembly>
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel level='asInvoker' uiAccess='false' />
      </requestedPrivileges>
    </security>
  </trustInfo>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity
     type='win32'
     name='Microsoft.VC90.CRT'
     version='9.0.21022.8'
     processorArchitecture='*'
     publicKeyToken='1fc8b3b9a1e18e3b' />
    </dependentAssembly>
  </dependency>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity
         type="win32"
         name="Microsoft.Windows.Common-Controls"
         version="6.0.0.0"
         processorArchitecture="*"
         publicKeyToken="6595b64144ccf1df"
         language="*" />
    </dependentAssembly>
  </dependency>
</assembly>

Both the “Microsoft.VC90.CRT” and “Microsoft.Windows.Common-Controls” assemblies are important. The first one is mandatory for Python >= 2.6 is built with Visual Studio 2008, and the second one is the one that links our app to the dlls that provide the nice XP Theme widgets.

Just loaded that text into a string (called manifest in my case) in my setup.py file and passed it as an  argument to the setup function like this:

setup(windows=[{
               'script':"app.py",
               'other_resources' : [(24, 1, manifest)]
               }],
        name = "My App",
        version="0.1"
    )

Easy as pie :D.

Hope you find this useful!