We are writing a small PC application at work that needs a GUI, USB device access, and text-to-speech. Since we’re a team of Linux guys, we were strongly opposed to the obvious C# + .NET combo. After a couple of days of research, we settled on:
* Python 2.7
* PyTTSx (text-to-speech)
* win32com (Windows COM interface, required for PyTTSx)
* PyUSB w/ libusb
But one critical requirement is that the end user must not need Python, so everything needs to be bundled. We experimented with Py2Exe first, and it worked, but with limitations. Namely, it can’t bundle into a single file or glib throws a bunch of errors.
I decided to try PyInstaller, which has good reviews. It had the same troubles as Py2Exe at first: it didn’t automatically generate dependencies for PyTTSx and win32com correctly. After a few patches to the hook files, I got it working with everything. And, better than py2exe, it bundles into a single executable file!
Here’s the patch against the official PyInstaller 1.5 release to get PyTTSx and win32com working in Windows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 Index: pyinstaller-1.5/hooks/hook-pyttsx.py =================================================================== — pyinstaller-1.5/hooks/hook-pyttsx.py (revision 0) +++ pyinstaller-1.5/hooks/hook-pyttsx.py (revision 167) @@ -0,0 +1 @@ +hiddenimports = [‘drivers’, 'drivers.sapi5’, 'drivers.nsss’] Index: pyinstaller-1.5/hooks/hook-win32com.py =================================================================== — pyinstaller-1.5/hooks/hook-win32com.py (revision 163) +++ pyinstaller-1.5/hooks/hook-win32com.py (revision 167) @@ -17,6 +17,8 @@ import os +hiddenimports = ['win32com.server.util’] + def hook(mod): pth = str(mod.__path__) if os.path.isdir(pth): Index: pyinstaller-1.5/support/rthooks/win32comgenpy.py =================================================================== — pyinstaller-1.5/support/rthooks/win32comgenpy.py (revision 163) +++ pyinstaller-1.5/support/rthooks/win32comgenpy.py (revision 167) @@ -34,5 +34,6 @@ import win32com win32com.__gen_path__ = genpydir win32com.__path__.insert(0, supportdir) +sys.modules[“win32com.gen_py”].__path__ = [ win32com.__gen_path__ ] # for older Pythons import copy_reg
This creates a hook file for PyTTSx that correctly gets its backend drivers. It also modifies the path of win32com so it uses gen_py correctly, and can then find COM objects dynamically. There is also one extra include for win32com because it was missing the server.util package, and needed it.