Questions like “What GUI should I use with Python?” and “What’s the best way to make a standalone Python app?” come up on Reddit and Stack Overflow pretty frequently. Having put together a standalone Python application a couple of months ago, I thought I’d post something about my experiences.
PODBrowser is a simple desktop application I put together as part of a larger project to revamp a handbook of Probability Of Detection (POD) curves. The hows and whys of POD don’t really concern us here, all that matters is that I’m writing a program that lets the user easily preview and plot technical data from 500+ Excel files. The files are organized by folders so that the root data folder has sub-folders for every technique used; inside each technique folder is one or more sub-folders for every sample that was inspected by that technique.
PODBrowser searches through its data folder and organizes data by technique and sample. When the user selects an Excel file to preview, a new window pops up with the Excel file in question previewing the first sheet in the file that isn’t a chart (PODBrowser skips the “Chart1” sheets in these data files). The user can select any of the sheets in the spreadsheet to preview, and plot the POD curve for the given sheet. The end goal was an application that could be distributed as a CD in the back of the hardcopy POD Handbook we were contracted to update, or as a promotional USB flash drive giveaway at conferences and trade shows.
I used three third party Python libs to write PODBrowser: wxPython as the UI toolkit, xlrd to read the Excel files, and matplotlib to produce the plots. All three work great with PyInstaller, my Python standalone creator of choice. I went with PyInstaller over py2exe because PyInstaller handles the whole DLL mess that otherwise crops up in bundling Python 2.6+ applications, and because it knows how to handle matplotlib without any tinkering on my part.
Once you’ve got PyInstaller installed, the next step is to create the spec file for your application. PyInstaller’s docs are pretty good for getting going on this step; the only options I used were
-w to keep the cmd console hidden and
--icon to specify the icon I’d made for the final product. Once you get the spec file working there’s not much need to ever visit it again unless your project changes or you want to change the final product; I got in the habit of thinking of it as a
Makefile of sorts and
python Build.py specfile
as my compilation. I actually had more trouble putting together a decent icon for PODBrowser under Windows than I ever did building the standalone with PyInstaller.
I went with wxPython over say PyQt or Tkinter mainly because I’m more familiar with it, and because I like that wxWidgets uses the underlying native toolkit where possible. You can substitute your own UI toolkit of choice over wxPython, PyInstaller works with most of them. Here’s what the final product looked like on a couple of platforms:
(Those parentheses are actually part of the original Excel filenames – I have no idea what the reasoning was behind that.)
The final tally in distribution size was around 45MB, including some basic documentation but not including the Excel data files (which was another 130MB, give or take). Of that, about 8MB was for matplotlib, another 4MB for matplotlib fonts and toolbar icons, 10-15MB for wxPython, and only about 200kB or so for xlrd. Ballpark, if you weren’t using matplotlib and xlrd, you’re looking at around a 30MB distributable. You can use UPX to bring that final size down, but for this application I really only needed it small enough to fit on a CD and/or thumb drive handed out at conferences so 45MB was small enough in this case. I also put together a setup.msi Windows Installer package with Advanced Installer; the final tally there including some brief installation instructions was 43MB.
If you’d like to see the final result in action, here’s a link to an archive of the source code. Also included in there is a copy of the PyInstaller spec file I used to make it a standalone application under Windows.