Pack an Enthought Traits app inside a .exe using py2exe (ETS 4.0 Edit)

In October 2010, I published a small tutorial on how to build a .exe Windows executable for ETS (Enthought Tool Suite) 3.5.0 -based applications.

Today, I present the edited version of the setup script, to match the new Enthought Namespace !!

Goal:

Pack an ETS-based application in a executable .exe file for windows

Keys to remember:

  1. To get things to work, I had to manually import all packages used, surely the Wx backend.
  2. It’s not possible, currently, to bundle and to compress. [if you find out ‘how-to’, please let me know !]
  3. I add the /images folders manually using the data_files variable.

My Configuration:

  • Windows 7 x64
  • Python 2.7.1 (running 32bits, I think)
  • numpy 1.5.1
  • scipy 0.9.0
  • py2exe 0.6.10dev
  • ETS 4.0 from GitHub

So, here is a typical example :

from enthought.traits.api import *

class Person(HasTraits):
    name = Str
    age = CInt
    weight = CFloat

if __name__ == "__main__":
    joe = Person()
    joe.configure_traits()
    print joe.name, joe.age, joe.weight

You’ll see below, the ETS from GitHub is downloaded in “c:\ETSgit”

The setup.py script looks like :

from distutils.core import setup
import py2exe
import os
import glob

includes = []
includes.append('numpy')
includes.append('numpy.core')
includes.append('configobj')
includes.append('reportlab')
includes.append('reportlab.pdfbase')
includes.append('reportlab.pdfbase.*')
includes.append('scipy')
includes.append('xml')
includes.append('xml.etree')
includes.append('xml.etree.*')

includes.append('wx')
includes.append('wx.*')

includes.append('traits')
includes.append('traitsui')
includes.append('traitsui.editors')
includes.append('traitsui.editors.*')
includes.append('traitsui.extras')
includes.append('traitsui.extras.*')

includes.append('traitsui.wx')
includes.append('traitsui.wx.*')

includes.append('kiva')

includes.append('pyface')
includes.append('pyface.*')
includes.append('pyface.wx')

includes.append('pyface.ui.wx')
includes.append('pyface.ui.wx.init')
includes.append('pyface.ui.wx.*')
includes.append('pyface.ui.wx.grid.*')
includes.append('pyface.ui.wx.action.*')
includes.append('pyface.ui.wx.timer.*')
includes.append('pyface.ui.wx.wizard.*')
includes.append('pyface.ui.wx.workbench.*')

includes.append('enable')
includes.append('enable.drawing')
includes.append('enable.tools')
includes.append('enable.wx')
includes.append('enable.wx.*')

includes.append('enable.savage')
includes.append('enable.savage.*')
includes.append('enable.savage.svg')
includes.append('enable.savage.svg.*')
includes.append('enable.savage.svg.backends')
includes.append('enable.savage.svg.backends.wx')
includes.append('enable.savage.svg.backends.wx.*')
includes.append('enable.savage.svg.css')
includes.append('enable.savage.compliance')
includes.append('enable.savage.trait_defs')
includes.append('enable.savage.trait_defs.*')
includes.append('enable.savage.trait_defs.ui')
includes.append('enable.savage.trait_defs.ui.*')
includes.append('enable.savage.trait_defs.ui.wx')
includes.append('enable.savage.trait_defs.ui.wx.*')

packages = []

data_folders = []

# Traited apps:
ETS_folder = r'C:\ETSgit'

data_folders.append( ( os.path.join(ETS_folder,r'enable\enable\images')                   , r'enable/images') )
data_folders.append( ( os.path.join(ETS_folder,r'pyface\pyface\images')    , r'pyface\images') )
data_folders.append( ( os.path.join(ETS_folder,r'pyface\pyface\ui\wx\images')    , r'pyface\ui\wx\images') )
data_folders.append( ( os.path.join(ETS_folder,r'pyface\pyface\ui\wx\grid\images')    , r'pyface\ui\wx\grid\images') )

data_folders.append( ( os.path.join(ETS_folder,r'traitsui\traitsui\wx\images')    , r'traitsui\wx\images') )

data_folders.append( ( os.path.join(ETS_folder,r'traitsui\traitsui\image\library')      , r'traitsui\image\library') )

data_folders.append( ( os.path.join(ETS_folder,r'enable\enable\savage\trait_defs\ui\wx\data')      , r'enable\savage\trait_defs\ui\wx\data') )

# Matplotlib
import matplotlib as mpl
data_files = mpl.get_py2exe_datafiles()

# Parsing folders and building the data_files table
for folder, relative_path in data_folders:
    for file in os.listdir(folder):
        f1 = os.path.join(folder,file)
        if os.path.isfile(f1): # skip directories
            f2 = relative_path, [f1]
            data_files.append(f2)

data_files.append((r'enable',[os.path.join(ETS_folder,r'enable\enable\images.zip')]))

setup(windows=['example.py'],
    author="Geophysique.be",
    version = "1.1",
    description = "Geophysique.be ETS4.0/Py2exe example",
    name = "DP - ROB Data Processing Tool",
    options = {"py2exe": {    "optimize": 0,
                              "packages": packages,
                              "includes": includes,
                              "dist_dir": 'dist',
                              "bundle_files":2,
                              "xref": False,
                              "skip_archive": True,
                              "ascii": False,
                              "custom_boot_script": '',
                              "compressed":False,
                             },},
    data_files=data_files)

 

then, to launch the packing, just do:
$ python setup.py py2exe

The build and packing process takes some time, but it works. The /dist directory contains a lot of libraries (~73 MB), there must be a way to cut down the imports, but I have to admit, now that I have a successful build, I really don’t care if it’s 110 MB or 80 MB ! I left the lines to include the matplotlib data files, although they are not needed for this example.

to launch the script, just hit “example.exe” in the /dist folder. Note, I set the “console” value to “example.py”, because I wanted to have a python console running behind my GUI. If you replace “console” by “windows”, double-clicking “example.exe” will just load your GUI.

FYI, I successfully packed our new seismic waveform data processing application using the script above !!

Cheeeeeeeeeeeeeeeeeeeers !

 

Thom

6 thoughts on “Pack an Enthought Traits app inside a .exe using py2exe (ETS 4.0 Edit)”

  1. hi,Thomas
    I have tried your emaxple, I installed pythonxy instead of ETS4.0, So the ETS_folder = r’C:\Python27\Lib\site-packages’, In this folder,It have ‘enable’,’pyface’,’traitsui’ subfolders. I have modified the setup.py. However, When I ‘python setup.py py2exe’,And run the example.exe, The error are as follows:

    Traceback (most recent call last):
    File “Traits_app_py2exe.py”, line 1, in
    ImportError: No module named enthought.traits.api
    Traceback (most recent call last):
    File “Traits_app_py2exe.py”, line 1, in
    ImportError: No module named enthought.traits.api

    Could you help me how to deal with it??

  2. My example is wrong in that, now, the enthought namespace has disappeared, you have to import traits.api and traitsui.api without the enthought prefix !
    Obviously, before py2exe a code, you should be sure it runs on your normal python install… Does it ? Let me know how it goes !

  3. Hi,Thomas
    your example of traitsui demo can run well on my normal python install.
    and follow your advice,I change ” from enthought.traits.api import * ” to ” from traits.api import * ”
    also works!
    But after I py2exe the demo ,the errors has changed ,Here it is:

    Traceback (most recent call last):
    File “Traits_app_py2exe.py”, line 10, in
    File “traits\has_traits.pyc”, line 2563, in configure_traits

    File “traitsui\wx\toolkit.pyc”, line 219, in view_application

    File “traitsui\wx\view_application.pyc”, line 80, in view_application

    File “traitsui\wx\view_application.pyc”, line 123, in __init__

    File “wx\_core.pyc”, line 8081, in __init__

    File “wx\_core.pyc”, line 7981, in __init__

    File “wx\_core.pyc”, line 7555, in _BootstrapApp
           e ”
    File “traitsui\wx\view_application.pyc”, line 140, in OnInit

    File “traitsui\view.pyc”, line 433, in ui

    File “traitsui\ui.pyc”, line 218, in ui

    File “traitsui\wx\toolkit.pyc”, line 140, in ui_live

    File “traitsui\wx\ui_live.pyc”, line 68, in ui_live

    File “traitsui\wx\ui_live.pyc”, line 99, in ui_dialog

    File “traitsui\wx\ui_live.pyc”, line 194, in init

    File “traitsui\wx\ui_base.pyc”, line 142, in set_icon

    File “pyface\ui\wx\image_resource.pyc”, line 67, in create_icon

    File “pyface\i_image_resource.pyc”, line 126, in _get_image_not_found_image

    File “pyface\i_image_resource.pyc”, line 103, in create_image

    File “pyface\i_image_resource.pyc”, line 129, in _get_image_not_found_image

    ValueError: cannot locate the file for ‘image_not_found’

    I don’t know why?

  4. It’s most probably an issue of data (image, static file) missing in the pyface module. In the setup script, there are a few lines about those images, to list them and “move” them to the right folder. I’ll try to rerun this example with the latest git clone and I’ll update it asap!

Leave a Reply

Your email address will not be published. Required fields are marked *

*