This is the first part of Distributing Python Apps for Windows Desktops. This is the most basic part and this matter was discussed in a lot of websites, but my idea here is to present how I’ve created the sample program and show how to generate a simple MSI installer with the necessary DLLs to run on Windows.
Every Python executable needs C++ Runtime DLLs to run on windows. You must have heard of it as Microsoft Visual C++ Redistributable or you can find it as MSVCR. The version you will need depends on which Python version you are using.
- Python 2.4 or Python 2.5 – Microsoft Visual C++ 2005 Redistributable Package
- Python 2.6, 2.7 or 3.x – Microsoft Visual C++ 2008 Redistributable Package
So, what we’re going to do:
- Our program in python files
- Write a setup file for the executable
- Generate the MSI installer
Alright, I’m creating a stupid script that I’ve used to troll one of my friends. This will replace the function of Print Screen keys and every time the key is press a picture of a Doll appears. I called it Boneca (That’s Portuguese for doll).
#boneca.py (it's portuguese for doll)
import os
import sys
import ctypes
from ctypes import wintypes
import win32con
byref = ctypes.byref
user32 = ctypes.windll.user32
HOTKEYS = {
1 : (win32con.VK_SNAPSHOT, 0), # "PRINT SCREEN"
2 : (win32con.VK_F4, win32con.MOD_WIN)
}
def handle_print_screen ():
os.startfile(os.path.join(os.path.realpath(os.path.dirname(sys.argv[0])),"boneca.jpg"))
def handle_win_f4 ():
user32.PostQuitMessage (0)
HOTKEY_ACTIONS = {
1 : handle_print_screen,
2 : handle_win_f4
}
# Registering the keys without the print
for id, (vk, modifiers) in HOTKEYS.items ():
#print "Registering id", id, "for key", vk
pass
if not user32.RegisterHotKey (None, id, modifiers, vk):
#print "Unable to register id", id
pass
# Calling the functions and removing from the register when quitting.
try:
msg = wintypes.MSG ()
while user32.GetMessageA (byref (msg), None, 0, 0) != 0:
if msg.message == win32con.WM_HOTKEY:
action_to_take = HOTKEY_ACTIONS.get (msg.wParam)
if action_to_take:
action_to_take ()
user32.TranslateMessage (byref (msg))
user32.DispatchMessageA (byref (msg))
finally:
for id in HOTKEYS.keys ():
user32.UnregisterHotKey (None, id)
As a reference to this code, I’ve used Tim Golden’s post[1].
Basically, this code creates two shortcuts on Windows, one for Print Screen that when pressed it calls the handle_print_screen function which loads the boneca.jpg file. The other shortcut calls handle_win_f4 to quit the program. It doesn’t have a GUI so it makes sense.
So far, so good. It’s a very simple script but now we have to freeze our code which means we will compile the script and generate an executable containing the Python interpreter, the modules, and files, everything in the same place. To do that we’ll need a setup file and chose one tool to freeze our apps such as py2exe, py2app, cx_freeze or Pyinstaller. In this case, we’ll use the cx_freeze.
#setup.py
from cx_Freeze import setup, Executable
setup(
name = "boneca",
version = "1.0.0",
options = {"build_exe": {
'packages': ["os","sys","ctypes","win32con"],
'include_files': ['boneca.jpg'],
'include_msvcr': True,
}},
executables = [Executable("boneca.py",base="Win32GUI")]
)
This is very straightforward and documented as disutils. But look, we’re using include_msvcr that will add the Microsoft Visual C++ Redistributable DLLs into your executable. It will copy the existing DLLs of your OS (if you’re using windows). That’s the only way your program will run on another Windows because it needs those DLLs. Also, you can download MSVCR installer and incorporate on your own installer using Inno Setup, for instance. We’ll do this in the 3rd post.
Now, we’ll generate an MSI using the command line:
python setup.py bdist_msi
Fine, now we have a dist folder with a boneca-1.0.0-win32.msi file inside or boneca-1.0.0-amd64.msi (for x64 OS) and now you can install and use the program.
Scripts on my GitHub: https://github.com/ffreitasalves/boneca
It was originally published in Portuguese.
[1]: http://timgolden.me.uk/python/win32_how_do_i/catch_system_wide_hotkeys.html