Como criar um executável com Instalador MSI em Python

Esse post é o primeiro da série Distribuir Programas em Python para Desktops Windows. Esse é o mais básico de todos, e esse assunto já foi bem discutido em diversos sites, mas a minha ideia aqui é apresentar como criei o programa de exemplo, mostrar como posso gerar um instalador bem simples já com as bibliotecas que o programa precisa pra rodar em outros Windows.

Toda vez que você gera um executável em Python, o windows precisa das DLLs de Runtime do C++, o tal do Microsoft Visual C++ Redistributable, você vai encontrar como MSVCR. A versão que será necessária depende de qual versão do Python você está usando.

Nós temos três coisas a fazer:

  1. Criar o script do programa
  2. Criar um arquivo de setup para gerar o executável.
  3. Gerar o Instalador MSI

Vou criar um script besta em python só pra ilustrar esses posts, isso é de uma brincadeira idiota que fiz com um amigo meu. Toda vez que o usuário apertar Print Screen o aplicativo vai abrir uma foto de uma boneca, como da foto abaixo:

baby-looking-like-his-doll

 


#boneca.py
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), ####Essa Linha Pega a tecla "PRINT SCREEN"
    2 : (win32con.VK_F4, win32con.MOD_WIN)
}

def handle_win_f3 ():
    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_win_f3,
    2 : handle_win_f4
}


# Registrando as chaves sem dar o print pra ficar escondido na tela.
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


# Executando as funções e tirando o registro das chaves depois do encerramento do programa.
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)

Como referência pra esse código eu usei o post do Tim Golden[1].

O código é simples, ele cria dois atalhos no windows, um é o Print Screen que quando pressionado roda a função handle_win_f3 que faz a chamada da foto da boneca.jpg

Já quando ele pressionar o atalho Win + F4 será executada a função handle_win_f4 que apenas encerra o programa. Ele não tem interface gráfica, o que faz sentido pra essa brincadeira.

Beleza, até aí só um script python fácil, agora nós precisamos fazer o Frozen dessa aplicação. Gerar um Frozen (um módulo congelado) é compilar o script e gerar um executável que contenha o interpretador python, suas bibliotecas, seu script e seus arquivos tudo em um lugar só e com um executável (ou mais executáveis). Para isso você precisa criar um arquivo de setup e escolher uma ferramenta para fazer o frozen, os mais usados são py2exe, py2app, cx_freeze, e o pyinstaller. Nesse caso eu vou usar o cx_freeze. Esse arquivo por convenção é chamado de setup.py e


#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")]
    )

O script também é simples, ele segue a mesma lógica do disutils , só quero chamar atenção para o include_msvcr que vai colocar as DLLs do Microsoft Visual C++ Redistributable no seu executável, dessa forma ele vai copiar as DLLs que já existem no seu sistema operacional. Esse é o único jeito do seu programa funcionar em outros windows, pois o Python precisa dessas dlls para ser executado. Você também pode baixar o próprio instalador do MSVCR da Microsoft e incorporar no seu instalador usando o Inno Setup, por exemplo, vou mostrar isso em um dos próximos posts da série.

Agora vamos gerar um MSI, ou seja, um instalador para windows. Só rodar na linha de comando:

python setup.py bdist_msi

Beleza, agora ele criou uma pasta chamada dist com um arquivo boneca-1.0.0-win32.msi (ou boneca-1.0.0-amd64.msi se seu sistema for 64) agora já dá pra instalar e usar. Então aqui mostrei Como criar um executável com Instalador MSI em Python e com Cx_freeze.

Os scripts estão no meu Github: https://github.com/ffreitasalves/boneca

[1]: http://timgolden.me.uk/python/win32_how_do_i/catch_system_wide_hotkeys.html