Distributing Python Apps for Windows Desktops

I’ve started working on a blog post about how to create a Python app auto-update and it turned into three. After these 3 articles, you will be able to create a Python app that fully works on windows and you can distribute it within an installer.

This text was originally written in Portuguese.

  1. How to create a Python .exe with MSI Installer and Cx_freeze
  2. How to create an application with auto-update using Python and Esky
  3. How to create an MSI installer using Inno Setup

It has just 4 Steps:

  • Create a simple project called boneca
  • Build an MSI installer using Cx_freeze
  • Add an Auto-update feature to the project, using Esky
  • Show how to use Inno Setup to build a more powerful and custom installer

In the end will be able to pack and distribute Python apps for windows desktop in an easy way.

Some people still think Python is just a script language or it works only for web development through frameworks, but it’s not. It can be compiled and it can be shipped without source code, turned into a commercial application.

The great example of all time is Dropbox. Dropbox client was written in Python to be portable for Windows, Mac, and Linux. The only difference is the interface. For Windows and Linux, Dropbox uses wxPython and for Mac it uses Python-ObjC. I like this words from Guido Van Rossum about Dropbox:

 

“Python plays an important role in Dropbox’s success: the Dropbox client, which runs on Windows, Mac and Linux (!), is written in Python. This is key to the portability: everything except the UI is cross-platform. (The UI uses a Python-ObjC bridge on Mac, and wxPython on the other platforms.) Performance has never been a problem — understanding that a small number of critical pieces were written in C, including a custom memory allocator used for a certain type of objects whose pattern of allocation involves allocating 100,000s of them and then releasing all but a few. Before you jump in to open up the Dropbox distro and learn all about how it works, beware that the source code is not included and the bytecode is obfuscated. Drew’s no fool. And he laughs at the poor competitors who are using Java.”

From depth and breadth of python

How to create an MSI installer using Inno Setup

Alright, guys, that’s the 3rd and last part of our Distributing Python Apps for Windows Desktops series. In this post, I’ll show how to create an MSI installer using Inno Setup and add MSVCR’s DLLs to make Python work on any Windows computer.

The other two parts are:

In the first part, we’ve learned how to create an MSI with cx_freeze and use the MSVCR from your own OS with the include_msvcr parameter. After that, we have updated our program to include an Auto-Update service.

OK, but now we can’t use the cx_freeze to make an installer anymore. It happens because Esky modifies your program creating an executable that verifies your program updates on FTP if it has some update available, esky downloads it, checks if everything is ok and remove the old files. No problem, let’s solve this with Inno Setup

1st thing, download and install Inno Setup.

Inno Setup generates a script file (.iss) for you to make your own installer. You can write your own script or use the Script Wizard.

inno

First, we’ll use the wizard and the file boneca-1.0.1.win32.zip that we have generated on the previous post (Part II). Unzip this file.

boneca-esky-conteudo

 

Back to Inno Setup click File >> New. The wizard is pretty straight forward. Fill the blanks as you like.

assistente-inno-setup-1

In the next screen, you can choose the folder to install your App. The default is Program Files, but if your code is not signed (using a Code Signing tool) you may have problems with Windows UAC. It will not recognize the authenticity of your code and you can struggle with antivirus, Windows security and it can stop your program from doing the auto-updates. So, at first, you better use another folder. You can type a path or use a Directory Constant.

assistente-inno-setup-2

On the next screen, you’ll add the programs, folders, and files that will be installed. In this case, boneca.exe and python27.dll at the root level and the boneca-1.0.1.win32 folder with its content.

Don’t forget to add boneca.exe as Application main executable file.

assistente-inno-setup-3

Now, go ahead with the standard procedure to windows programs (next, next, next…). At the end, it creates a .iss file. You can compile and it will generate a .msi Installer. But, hold on! We still need to add the MSVCR’s DLLs. So download it according to your python version:

Now update your .iss file, so it can install those DLLs too. I used a solution I’ve found on StackOverFlow and it works fine.

At Files section insert the vc_redist’s path that you’ve just downloaded:

[Files]
Source: "vcredist_x86.exe"; DestDir: {tmp}; Flags: deleteafterinstall

At the end of the Run section, paste it as it is:

[Run]
; add the Parameters, WorkingDir and StatusMsg as you wish, just keep here
; the conditional installation Check
Filename: "{tmp}\vcredist_x86.exe"; Check: VCRedistNeedsInstall

[Code]
#IFDEF UNICODE
 #DEFINE AW "W"
#ELSE
 #DEFINE AW "A"
#ENDIF
type
 INSTALLSTATE = Longint;
const
 INSTALLSTATE_INVALIDARG = -2; // An invalid parameter was passed to the function.
 INSTALLSTATE_UNKNOWN = -1; // The product is neither advertised or installed.
 INSTALLSTATE_ADVERTISED = 1; // The product is advertised but not installed.
 INSTALLSTATE_ABSENT = 2; // The product is installed for a different user.
 INSTALLSTATE_DEFAULT = 5; // The product is installed for the current user.

 VC_2005_REDIST_X86 = '{A49F249F-0C91-497F-86DF-B2585E8E76B7}';
 VC_2005_REDIST_X64 = '{6E8E85E8-CE4B-4FF5-91F7-04999C9FAE6A}';
 VC_2005_REDIST_IA64 = '{03ED71EA-F531-4927-AABD-1C31BCE8E187}';
 VC_2005_SP1_REDIST_X86 = '{7299052B-02A4-4627-81F2-1818DA5D550D}';
 VC_2005_SP1_REDIST_X64 = '{071C9B48-7C32-4621-A0AC-3F809523288F}';
 VC_2005_SP1_REDIST_IA64 = '{0F8FB34E-675E-42ED-850B-29D98C2ECE08}';
 VC_2005_SP1_ATL_SEC_UPD_REDIST_X86 = '{837B34E3-7C30-493C-8F6A-2B0F04E2912C}';
 VC_2005_SP1_ATL_SEC_UPD_REDIST_X64 = '{6CE5BAE9-D3CA-4B99-891A-1DC6C118A5FC}';
 VC_2005_SP1_ATL_SEC_UPD_REDIST_IA64 = '{85025851-A784-46D8-950D-05CB3CA43A13}';

 VC_2008_REDIST_X86 = '{FF66E9F6-83E7-3A3E-AF14-8DE9A809A6A4}';
 VC_2008_REDIST_X64 = '{350AA351-21FA-3270-8B7A-835434E766AD}';
 VC_2008_REDIST_IA64 = '{2B547B43-DB50-3139-9EBE-37D419E0F5FA}';
 VC_2008_SP1_REDIST_X86 = '{9A25302D-30C0-39D9-BD6F-21E6EC160475}';
 VC_2008_SP1_REDIST_X64 = '{8220EEFE-38CD-377E-8595-13398D740ACE}';
 VC_2008_SP1_REDIST_IA64 = '{5827ECE1-AEB0-328E-B813-6FC68622C1F9}';
 VC_2008_SP1_ATL_SEC_UPD_REDIST_X86 = '{1F1C2DFC-2D24-3E06-BCB8-725134ADF989}';
 VC_2008_SP1_ATL_SEC_UPD_REDIST_X64 = '{4B6C7001-C7D6-3710-913E-5BC23FCE91E6}';
 VC_2008_SP1_ATL_SEC_UPD_REDIST_IA64 = '{977AD349-C2A8-39DD-9273-285C08987C7B}';
 VC_2008_SP1_MFC_SEC_UPD_REDIST_X86 = '{9BE518E6-ECC6-35A9-88E4-87755C07200F}';
 VC_2008_SP1_MFC_SEC_UPD_REDIST_X64 = '{5FCE6D76-F5DC-37AB-B2B8-22AB8CEDB1D4}';
 VC_2008_SP1_MFC_SEC_UPD_REDIST_IA64 = '{515643D1-4E9E-342F-A75A-D1F16448DC04}';

 VC_2010_REDIST_X86 = '{196BB40D-1578-3D01-B289-BEFC77A11A1E}';
 VC_2010_REDIST_X64 = '{DA5E371C-6333-3D8A-93A4-6FD5B20BCC6E}';
 VC_2010_REDIST_IA64 = '{C1A35166-4301-38E9-BA67-02823AD72A1B}';
 VC_2010_SP1_REDIST_X86 = '{F0C3E5D1-1ADE-321E-8167-68EF0DE699A5}';
 VC_2010_SP1_REDIST_X64 = '{1D8E6291-B0D5-35EC-8441-6616F567A0F7}';
 VC_2010_SP1_REDIST_IA64 = '{88C73C1C-2DE5-3B01-AFB8-B46EF4AB41CD}';

 // Microsoft Visual C++ 2012 x86 Minimum Runtime - 11.0.61030.0 (Update 4) 
 VC_2012_REDIST_MIN_UPD4_X86 = '{BD95A8CD-1D9F-35AD-981A-3E7925026EBB}';
 VC_2012_REDIST_MIN_UPD4_X64 = '{CF2BEA3C-26EA-32F8-AA9B-331F7E34BA97}';
 // Microsoft Visual C++ 2012 x86 Additional Runtime - 11.0.61030.0 (Update 4) 
 VC_2012_REDIST_ADD_UPD4_X86 = '{B175520C-86A2-35A7-8619-86DC379688B9}';
 VC_2012_REDIST_ADD_UPD4_X64 = '{37B8F9C7-03FB-3253-8781-2517C99D7C00}';

function MsiQueryProductState(szProduct: string): INSTALLSTATE; 
 external 'MsiQueryProductState{#AW}@msi.dll stdcall';

function VCVersionInstalled(const ProductID: string): Boolean;
begin
 Result := MsiQueryProductState(ProductID) = INSTALLSTATE_DEFAULT;
end;

function VCRedistNeedsInstall: Boolean;
begin
 // here the Result must be True when you need to install your VCRedist
 // or False when you don't need to, so now it's upon you how you build
 // this statement, the following won't install your VC redist only when
 // the Visual C++ 2010 Redist (x86) and Visual C++ 2010 SP1 Redist(x86)
 // are installed for the current user
 Result := not (VCVersionInstalled(VC_2010_REDIST_X86) and 
 VCVersionInstalled(VC_2010_SP1_REDIST_X86));
end;


And now compile your file. You have a setup.exe as the Output and this is able to install our boneca.exe and the necessary DLLs to run it on every goddamn Windows.

Conclusion:

If you read the 3 posts you’ve learned how to create an executable using Python with auto-update feature and an MSI installer to distribute it for any Windows version.

Originally published in Portuguese!

How to create an application with auto-update using Python and Esky

This is the 2nd part of  Distributing Python Apps for Windows Desktops series. The 1st part is here: How to create a Python .exe with MSI Installer and CX_freeze

Every time a program has to be updated is a burden. Remember Java! It feels so uncomfortable, even if you’re an IT guy. You don’t like it, nor does your users. So be nice to them and make an auto-update on your program so they don’t have to download new versions.

To show how to create an application with auto-update using Python and Esky I’ll use the boneca app from part 1. The program was written and compiled but it doesn’t have an auto-update yet. You just generated an installer and the clients using that version will never have updates again. So, now we’re creating a new version using Esky:

pip install esky

 

Let’s modify the boneca.py script to import esky and find for updates on the internet when they’re available.

# right after import win32con
import esky

if hasattr(sys,"frozen"):
    app = esky.Esky(sys.executable,"http://teenspirit.com.br/exemplo_boneca/")
    app.auto_update()

When the program initializes it will look up the given URL for some update to download. It does that based on version number. You just have to create a folder and enable it on Apache. I’m using one of my sites: http://teenspirit.com.br/exemplo_boneca/.

Now, instead of using the boneca.jpg I’ll use this image (chuck.jpg):

chuck


#replace boneca.jpg to chuck.jpg
os.startfile(os.path.join(os.path.realpath(os.path.dirname(sys.argv[0])),"chuck.jpg"))

Now, let’s update setup.py to use esky:

#setup.py
import esky.bdist_esky
from esky.bdist_esky import Executable as Executable_Esky
from cx_Freeze import setup, Executable

include_files = ['boneca.jpg','chuck.jpg']

setup(
    name = 'boneca',
    version = '1.0.1',
    options = {
        'build_exe': {
            'packages': ['os','sys','ctypes','win32con'],
            'excludes': ['tkinter','tcl','ttk'],
            'include_files': include_files,
            'include_msvcr': True,
        },
        'bdist_esky': {
            'freezer_module': 'cx_freeze',
        }
    },
    data_files = include_files,
    scripts = [
        Executable_Esky(
            "boneca.py",
            gui_only = True,
            #icon = XPTO  # Use an icon if you want.
            ),
    ],
    executables = [Executable('boneca.py',base='Win32GUI')]
    )

As you can see, I’ve changed the version number to 1.0.1 and from this version, our program will have an auto-update. This file is different from the previous one, so I’ll try to explain everything that is happening here.

1. Importing bdist_esky and an Executable Esky.

import esky.bdist_esky
from esky.bdist_esky import Executable as Executable_Esky

2. Defining options for the new argument bdist_esky:

        'bdist_esky': {
            'freezer_module': 'cx_freeze',
        }

3. Adding  data_files cause Esky uses it to include files on the app’s folder.

      data_files = include_files,

4. Adding scripts so Esky will now which files will be the executables.

    scripts = [
        Executable_Esky(
            "boneca.py",
            gui_only = True,
            #icon = XPTO  # Use an icon if you want,
            ),
    ],

Run the setup.py with bdist_esky version to generate the version.

python setup.py bdist_esky

On your dist folder:

boneca_esky
Inside the zip file you will see this:

boneca-esky-conteudo

So what happened? Esky created a program boneca.exe that will be responsible for the update of your app, including itself. When it’s open it’ll look for new Zip Files with New versions on the URL that I’ve specified. If it’s a new version it will download and change the folder content cleaning the last version.

Esky handles issues such as internet connection problems, energy failure or any other problems while downloading the new version.

So from now on, our app has auto-update which is So Cool, isn’t it??? But unfortunately this version doesn’t have MSVCR support, so in the next and last part of this series, I’ll show how to create your own installer with Inno Setup.

To show how the update works I’ll create one more version (1.0.2) and I’ll change the image again from chuck.jpg to seu-boneco.jpg (it means Mr doll in Portuguese):

os.startfile(os.path.join(os.path.realpath(os.path.dirname(sys.argv[0])),"seu-boneco.jpg"))

Don’t forget to add seu-boneco.jpg in the include_files of setup.py:

include_files = ['boneca.jpg','chuck.jpg', 'seu-boneco.jpg']

Now, let’s generate the new Esky file:

python setup.py bdist_esky

New file on our dist folder boneca-1.0.2.win32.zip. We just have to put this on the URL that we provided and next time someone uses the boneca-1.0.1 it will be auto-updated.

If you want to test this app, download boneca-1.0.1.win32.zip, unzip the file and open boneca.exe

When you press Print Screen it will show the Chuck Image, but, at this point, it will be looking for updates and the next time you open the program it will show “Seu Boneco” image.

seu-boneco

Code on Github.

Wait for the 3rd Part!

 

Originally published in Portuguese!

How to create a Python .exe with MSI Installer and Cx_freeze

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.

So, what we’re going to do:

  1. Our program in python files
  2. Write a setup file for the executable
  3. 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).

baby-looking-like-his-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

Como criar um instalador MSI utilizando o Inno Setup

Essa é a terceira parte da série de Posts Distribuir Programas em Python para Desktops Windows. Nela você vai aprender como criar um instalador MSI utilizando o Inno Setup e colocando as DLLs do MSVCR para o Python funcionar em qualquer máquina windows.

As outras duas partes são:

Bom, nós vimos como criar um instalador MSI com o cx_freeze e utilizando as DLL’s de MSVCR da própria página no primeiro post, depois vimos como modificar o programa para ele ter um Auto-Update. Só que quando alteramos o setup.py pra gerar o programa com o Auto-Update não conseguimos mais fazer um instalador com o cx_freeze. Isso acontece porque o Esky modifica o seu programa, ele cria um executável que verifica se há algum download disponível no FTP, se tiver ele faz o download, faz o update, verifica se deu certo e exclui as bibliotecas antigas.

Primeira coisa: baixar e instalar o Inno Setup.

O Inno Setup cria um arquivo .iss que nada mais é que um script para criar o seu instalador. Abrindo o Inno você tem a opção de escrever esse script diretamente ou usar o Script Wizard (Um assistente):

inno

 

Nós vamos criar um instalador usando o assistente, primeiro nós vamos utilizar aquele arquivo que geramos no post anterior, o boneca-1.0.1.win32.zip. Você precisa ter descompactado esse arquivo:

boneca-esky-conteudo

No Inno Setup clique em File >> New. Você abrirá o Assistente, ele é fácil de usar, preencha esses dados como quiser:

assistente-inno-setup-1

Na próxima tela você pode escolher onde o Instalador vai colocar o seu programa. O Default é o Program Files folder, mas tem um porém: Se o seu código não tiver Assinatura Digital (Code Signing) você vai ter dificuldades com o UAC do Windows, porque ele não vai conferir a autenticidade do seu código e isso implicará em problemas com antivírus, em bloqueio do programa pelo windows e principalmente ele pode impedir que o programa faça o auto-update, por isso use uma outra pasta a princípio. Você pode escrever um caminho ou usar uma Constante das pastas do Windows do próprio Inno Setup. [Recomendável]

assistente-inno-setup-2

Na terceira tela você vai colocar quais os programas, pastas e arquivos serão instalados pelo instalador, no caso o boneca.exe e o python27.dll ficaram na raiz e também a pasta boneca-1.0.1.win32 com o conteúdo dela.

Então coloque boneca.exe no campo de Application main executable file
Em seguida, clique em Add File para colocar o python27.dll e depois em Add Folder para adicionar a pasta boneca-1.0.1.win32:

assistente-inno-setup-3

Agora é só seguir em diante no assistente, no final ele vai criar o arquivo .iss. Você já pode compilar o arquivo e ele vai gerar o instalador .msi mas está faltando agora adicionar as DLLs do MSVCR. Então baixe de acordo com a sua versão do python:

Agora você precisa modificar o código do seu arquivo .iss pra ele instalar as DLLs junto com o seu programa. Isso aqui eu usei essa solução que achei no StackOverFlow e ela funciona muito bem:

Na seção Files coloque o caminho do vc_redist que você baixou:

[Files]
Source: "vcredist_x86.exe"; DestDir: {tmp}; Flags: deleteafterinstall

No final da seção Run adicione esse código:

[Run]
; add the Parameters, WorkingDir and StatusMsg as you wish, just keep here
; the conditional installation Check
Filename: "{tmp}\vcredist_x86.exe"; Check: VCRedistNeedsInstall

[Code]
#IFDEF UNICODE
 #DEFINE AW "W"
#ELSE
 #DEFINE AW "A"
#ENDIF
type
 INSTALLSTATE = Longint;
const
 INSTALLSTATE_INVALIDARG = -2; // An invalid parameter was passed to the function.
 INSTALLSTATE_UNKNOWN = -1; // The product is neither advertised or installed.
 INSTALLSTATE_ADVERTISED = 1; // The product is advertised but not installed.
 INSTALLSTATE_ABSENT = 2; // The product is installed for a different user.
 INSTALLSTATE_DEFAULT = 5; // The product is installed for the current user.

 VC_2005_REDIST_X86 = '{A49F249F-0C91-497F-86DF-B2585E8E76B7}';
 VC_2005_REDIST_X64 = '{6E8E85E8-CE4B-4FF5-91F7-04999C9FAE6A}';
 VC_2005_REDIST_IA64 = '{03ED71EA-F531-4927-AABD-1C31BCE8E187}';
 VC_2005_SP1_REDIST_X86 = '{7299052B-02A4-4627-81F2-1818DA5D550D}';
 VC_2005_SP1_REDIST_X64 = '{071C9B48-7C32-4621-A0AC-3F809523288F}';
 VC_2005_SP1_REDIST_IA64 = '{0F8FB34E-675E-42ED-850B-29D98C2ECE08}';
 VC_2005_SP1_ATL_SEC_UPD_REDIST_X86 = '{837B34E3-7C30-493C-8F6A-2B0F04E2912C}';
 VC_2005_SP1_ATL_SEC_UPD_REDIST_X64 = '{6CE5BAE9-D3CA-4B99-891A-1DC6C118A5FC}';
 VC_2005_SP1_ATL_SEC_UPD_REDIST_IA64 = '{85025851-A784-46D8-950D-05CB3CA43A13}';

 VC_2008_REDIST_X86 = '{FF66E9F6-83E7-3A3E-AF14-8DE9A809A6A4}';
 VC_2008_REDIST_X64 = '{350AA351-21FA-3270-8B7A-835434E766AD}';
 VC_2008_REDIST_IA64 = '{2B547B43-DB50-3139-9EBE-37D419E0F5FA}';
 VC_2008_SP1_REDIST_X86 = '{9A25302D-30C0-39D9-BD6F-21E6EC160475}';
 VC_2008_SP1_REDIST_X64 = '{8220EEFE-38CD-377E-8595-13398D740ACE}';
 VC_2008_SP1_REDIST_IA64 = '{5827ECE1-AEB0-328E-B813-6FC68622C1F9}';
 VC_2008_SP1_ATL_SEC_UPD_REDIST_X86 = '{1F1C2DFC-2D24-3E06-BCB8-725134ADF989}';
 VC_2008_SP1_ATL_SEC_UPD_REDIST_X64 = '{4B6C7001-C7D6-3710-913E-5BC23FCE91E6}';
 VC_2008_SP1_ATL_SEC_UPD_REDIST_IA64 = '{977AD349-C2A8-39DD-9273-285C08987C7B}';
 VC_2008_SP1_MFC_SEC_UPD_REDIST_X86 = '{9BE518E6-ECC6-35A9-88E4-87755C07200F}';
 VC_2008_SP1_MFC_SEC_UPD_REDIST_X64 = '{5FCE6D76-F5DC-37AB-B2B8-22AB8CEDB1D4}';
 VC_2008_SP1_MFC_SEC_UPD_REDIST_IA64 = '{515643D1-4E9E-342F-A75A-D1F16448DC04}';

 VC_2010_REDIST_X86 = '{196BB40D-1578-3D01-B289-BEFC77A11A1E}';
 VC_2010_REDIST_X64 = '{DA5E371C-6333-3D8A-93A4-6FD5B20BCC6E}';
 VC_2010_REDIST_IA64 = '{C1A35166-4301-38E9-BA67-02823AD72A1B}';
 VC_2010_SP1_REDIST_X86 = '{F0C3E5D1-1ADE-321E-8167-68EF0DE699A5}';
 VC_2010_SP1_REDIST_X64 = '{1D8E6291-B0D5-35EC-8441-6616F567A0F7}';
 VC_2010_SP1_REDIST_IA64 = '{88C73C1C-2DE5-3B01-AFB8-B46EF4AB41CD}';

 // Microsoft Visual C++ 2012 x86 Minimum Runtime - 11.0.61030.0 (Update 4) 
 VC_2012_REDIST_MIN_UPD4_X86 = '{BD95A8CD-1D9F-35AD-981A-3E7925026EBB}';
 VC_2012_REDIST_MIN_UPD4_X64 = '{CF2BEA3C-26EA-32F8-AA9B-331F7E34BA97}';
 // Microsoft Visual C++ 2012 x86 Additional Runtime - 11.0.61030.0 (Update 4) 
 VC_2012_REDIST_ADD_UPD4_X86 = '{B175520C-86A2-35A7-8619-86DC379688B9}';
 VC_2012_REDIST_ADD_UPD4_X64 = '{37B8F9C7-03FB-3253-8781-2517C99D7C00}';

function MsiQueryProductState(szProduct: string): INSTALLSTATE; 
 external 'MsiQueryProductState{#AW}@msi.dll stdcall';

function VCVersionInstalled(const ProductID: string): Boolean;
begin
 Result := MsiQueryProductState(ProductID) = INSTALLSTATE_DEFAULT;
end;

function VCRedistNeedsInstall: Boolean;
begin
 // here the Result must be True when you need to install your VCRedist
 // or False when you don't need to, so now it's upon you how you build
 // this statement, the following won't install your VC redist only when
 // the Visual C++ 2010 Redist (x86) and Visual C++ 2010 SP1 Redist(x86)
 // are installed for the current user
 Result := not (VCVersionInstalled(VC_2010_REDIST_X86) and 
 VCVersionInstalled(VC_2010_SP1_REDIST_X86));
end;


Agora salve e compile seu arquivo. Você terá um arquivo setup.exe como saída na pasta Output (ou o nome que você colocou) e ele vai instalar o seu programa da boneca com as DLLs necessárias pra rodar em qualquer Windows.

Conclusão:

Se você acompanhou os 3 posts você conseguiu criar um programa em python com auto-update e distribuí-lo para qualquer computador windows.

Observações Final sobre Code Signing:

Fiz uma pesquisa sobre code signing no ano passado(2014) , pode ser interessante para alguém:

Autoridade Certificadora Link Preço / ano
Global Sign https://www.globalsign.com/code-signing/ 229 usd
Thawte http://www.thawte.com/code-signing/index.html?tid=a_box_buycs 299 usd
Symantec Verisign http://www.symantec.com/code-signing 499 usd
Godaddy Code Signing http://br.godaddy.com/ssl/code-signing-certificate.aspx 479,99 reais / 199.99 usd
Ksoftware (Comodo partner) http://codesigning.ksoftware.net/ 95 usd
Digicert http://www.digicert.com/code-signing/ev-code-signing-compared.htm 223 usd
Obs:
Comparativo interessante: https://www.sslshopper.com/cheap-code-signing-certificates.html

Guia Rápido para Instalar e rodar o Cordova / Phonegap no Windows

Algumas pessoas que eu conheço tiveram problemas na hora de instalar o Phonegap no Windows e resolvi fazer esse guia rápido de como instalar e rodar o phonegap sem problemas.

1. Instale o Node.js
Vá na página http://nodejs.org/ e clique no botão install que você vai baixar um arquivo de instalação para windows .msi

2. Instale o Git

Vá em http://git-scm.com/downloads e baixe o Git Installer

Depois configure as variáveis globais do git, então abra o cmd e coloque essas duas linhas:

$ git config --global user.name "Seu Nome"
$ git config --global user.email [email protected]

3. Instale o  Phonegap

Abra o cmd e digite:

npm install -g phonegap

4. Faça o Download do Java JDK

Faça Download do Java JDK desse endereço: http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html

5. Faça o Download do ANT

Dessa página aqui: http://ant.apache.org/bindownload.cgi depois descompacte os arquivos em algum lugar

6. Faça Download do ADT Bundle

Dessa página http://developer.android.com/sdk/index.html#download e descompacte os arquivos em algum lugar.

7. Configure o Path do Windows

Do desktop, clique com o botão direito em Meu Computador e depois em Propriedades
Clique em Configurações Avançadas na coluna da esquerda
Na janela de Propriedades de Sistema clique no botão Variáveis de Ambiente.
Selecione a variável PATH da seção de Variáveis do Sistema
Clique em Editar.
Dentro da pasta do ADT Bundle que você descompactou pegue o caminho completo até a pasta \sdk\tools e adicione no final da variável PATH.
Copie o caminho para \sdk\platform-tools para a variável PATH também

8. Configure o JAVA_HOME

Crie uma nova variável de ambiente chamada JAVA_HOME e coloque o caminho para o JDK nessa variável. Fica alguma coisa como “C:\Program Files\Java\jdk1.8.0_05

9. Configure o ANT_HOME

Crie uma nova variável de ambiente chamada ANT_HOME e coloque o caminho da pasta ANT que você descompactou.

10. Crie e Lance um Hello World

Usando o cmd escreva:

phonegap create hello
cd hello 
phonegap run android

Dentro da pasta do projeto hello, crie um arquivo index.html e coloque na pasta www.
Conteúdo de index.html:

<html><body>Hello World</body></html>

Depois use o cmd novamente :

phonegap run android

E veja o Hello World na tela do seu telefone

 

Como criar um programa com Auto-Update utilizando Python e Esky

Essa é a segunda parte da série de Posts Distribuir Programas em Python para Desktops Windows. Se quiser, veja o primeiro post: Como criar um executável com Instalador MSI em Python

Sempre que um programa precisa ser atualizado parece um parto. Lembre-se de todas as vezes você teve que baixar a versão atualizada do Java, lembra bem dessa raiva que você sentiu ao ter que fazer isso e imagine que seu usuário sinta a mesma raiva que você, então seja legal e faça um auto-update no seu programa para que ninguém precise baixar as versões novas. Para aprender como criar um programa com Auto-Update utilizando Python e Esky eu vou usar o aplicativo boneca criado na parte 1.

Até aqui tudo normal, o programa está feito, foi compilado mas não tem auto-update, você simplesmente gerou um instalador da boneca e nunca mais vai atualizar esse programa no cliente que o instalou, então você precisa fazer alterações e gerar um novo executável.

Bom, vamos ter que instalar um package chamado Esky:

pip install esky

Agora vamos fazer alterações no código, primeiro no script da Boneca.py precisamos importar o esky e pedir pra ele procurar por atualizações na internet quando estiverem disponíveis.

#Coloque abaixo de import win32con
import esky

if hasattr(sys,"frozen"):
    app = esky.Esky(sys.executable,"http://teenspirit.com.br/exemplo_boneca/")
    app.auto_update()

Toda vez que você iniciar o programa ele vai procurar no site se tem alguma atualização pra baixar. Ele conseguirá reconhecer pelo número da versão. Eu simplesmente deixei esse diretório (http://teenspirit.com.br/exemplo_boneca/) pra fazer listagem no Apache.

Agora vamos fazer alterações no programa, eu vou colocar outra imagem no lugar da boneca, nesse caso, usarei o chuck.jpg

chuck


#Troque o texto de boneca.jpg para chuck.jpg
os.startfile(os.path.join(os.path.realpath(os.path.dirname(sys.argv[0])),"chuck.jpg"))

 

Agora, vamos alterar o setup.py:

#setup.py
import esky.bdist_esky
from esky.bdist_esky import Executable as Executable_Esky
from cx_Freeze import setup, Executable

include_files = ['boneca.jpg','chuck.jpg']

setup(
    name = 'boneca',
    version = '1.0.1',
    options = {
        'build_exe': {
            'packages': ['os','sys','ctypes','win32con'],
            'excludes': ['tkinter','tcl','ttk'],
            'include_files': include_files,
            'include_msvcr': True,
        },
        'bdist_esky': {
            'freezer_module': 'cx_freeze',
        }
    },
    data_files = include_files,
    scripts = [
        Executable_Esky(
            "boneca.py",
            gui_only = True,
            #icon = XPTO #Coloque um icone aqui se quiser ,
            ),
    ],
    executables = [Executable('boneca.py',base='Win32GUI')]
    )

Reparem que eu mudei o número da Versão para 1.0.1, e a partir dessa versão o programa terá o auto-update. Esse arquivo ficou bem diferente, vou tentar explicar tudo que aconteceu nele:

1. Importei a opção de setup bdist_esky e um tipo de executável do próprio Esky

import esky.bdist_esky
from esky.bdist_esky import Executable as Executable_Esky

2. Coloquei as opções para o novo argumento bdist_esky:

        'bdist_esky': {
            'freezer_module': 'cx_freeze',
        }

3. Adicionei o data_files, porquê é isso que o Esky usa para ver quais arquivos também precisam ir para a pasta do programa

      data_files = include_files,

4. Coloquei o scripts porque é daí que o Esky vê quais scripts se tornaram os executáveis.

    scripts = [
        Executable_Esky(
            "boneca.py",
            gui_only = True,
            #icon = XPTO #Coloque um icone aqui se quiser ,
            ),
    ],

Agora para executar você precisa usar o setup.py com o argumento bdist_esky

python setup.py bdist_esky

Ele vai gerar um arquivo compactado que vai ficar na sua pasta dist:

boneca_esky
E dentro desse arquivo você tem isso aqui:

boneca-esky-conteudo

 

O que aconteceu? O Esky criou um programa, boneca.exe que vai ser responsável pela atualização do seu programa, inclusive dele mesmo. Quando ele for aberto ele vai buscar no endereço que eu coloquei (http://teenspirit.com.br/exemplo_boneca/) e se achar um outro arquivo compactado com uma versão mais recente ele vai baixar o arquivo e trocar o conteúdo, então na próxima vez que ele for aberto ele abrirá com a versão mais nova e limpará a versão mais antiga.

O bom é que se der algum problema no pacote, ou na internet ou na energia o Esky gerencia isso para que o programa sempre funcione. (só não pode subir uma versão bichada).

Então, quem de agora em diante for baixar o seu programa terá que baixar essa versão do Esky, que tem o arquivo zipado, aquele que geramos na Parte 1 não tem auto-update. E é por isso que vou mostrar na Parte 3 como fazer uma distribuição bem mais legal com o instalador do Inno Setup. Outra coisa que não é muito legal é que essa versão que criamos não tem o suporte ao MSVCR, mais um motivo para você ter um instalador customizado que vai incluir isso pra você.

Pra testar a atualização eu vou criar mais uma nova versão, que vou chamar de 1.0.2, nela vou trocar a foto, para isso mudo o boneca.py:

os.startfile(os.path.join(os.path.realpath(os.path.dirname(sys.argv[0])),"seu-boneco.jpg"))

e vou adicionar esse novo arquivo seu-boneco.jpg no include_files do setup.py:

include_files = ['boneca.jpg','chuck.jpg', 'seu-boneco.jpg']

Agora é só gerar o novo arquivo do Esky:

python setup.py bdist_esky

Na sua pasta dist vai aparecer o arquivo boneca-1.0.2.win32.zip. É só colocá-lo no link informado e na próxima vez que seus usuários abrirem o programa, ele será atualizado.

Se você quiser testar esse programa, faça o seguinte: baixe o boneca-1.0.1.win32.zip, descompacte o arquivo em uma pasta e abra o boneca.exe .

Quando você apertar o Print Screen ele vai mostrar a foto do Chuck, mas perceba que ele também já estará fazendo a atualização para a versão 1.0.2 e depois de fechado, na próxima vez que o programa rodar ele exibirá a foto do Seu Boneco.

seu-boneco

 

O código continua no Github.

E segura que em breve posto a Parte 3.

Distribuir Programas em Python para Desktops Windows

Comecei a escrever um post sobre como criar um programa com auto-update em Python e como vi que tinha muito assunto pra falar eu resolvi escrever 3 posts, em cada um vou tratar de um assunto mas o objetivo final é conseguir criar um executável em Python com auto-update e que tenha um instalador.

Então os posts estão divididos em 3 partes:

  1. Como criar um executável com Instalador MSI com Python e Cx_freeze
  2. Como criar um programa com Auto-Update utilizando Python e Esky
  3. Como criar um instalador MSI utilizando o Inno Setup

Durante esses 3 posts vou criar um programa bem simples (chamado boneca), mostrar como eu posso criar o instalador pelo próprio Cx_freeze, depois mostrar como posso criar o auto-update utilizando o Esky e por fim mostrar como posso usar o Inno Setup pra criar um instalador mais robusto e mais personalizado.

No final você poderá distribuir programas em python para desktops windows sem nenhum problema.

Muitas pessoas pensam que o Python serve só para fazer scripts e que não pode ser compilado e shipado. Mas não é bem assim, python também pode ser compilado e você pode distribuir software python sem o código-fonte.

Um grande exemplo disso é o Dropbox. O cliente do Dropbox foi escrito em Python para ser portável para Windows, Mac e Linux e a única diferença é que para Windows e Linux ele roda o wxPython no UI e no Mac ele usa Python-ObjC. As próprias palavras do Guido Van Rossum são muito legais falando sobre isso:

 

“Python plays an important role in Dropbox’s success: the Dropbox client, which runs on Windows, Mac and Linux (!), is written in Python. This is key to the portability: everything except the UI is cross-platform. (The UI uses a Python-ObjC bridge on Mac, and wxPython on the other platforms.) Performance has never been a problem — understanding that a small number of critical pieces were written in C, including a custom memory allocator used for a certain type of objects whose pattern of allocation involves allocating 100,000s of them and then releasing all but a few. Before you jump in to open up the Dropbox distro and learn all about how it works, beware that the source code is not included and the bytecode is obfuscated. Drew’s no fool. And he laughs at the poor competitors who are using Java.”

O texto original dele é esse aqui!

Quick Guide To Install Phonegap and get it running on windows

Some people I know had trouble to install Phonegap on Windows, so I made this quick guide.

1. Install Node.js
Go to http://nodejs.org/ and click the Install button to download a msi file

2. Install Git

Go to http://git-scm.com/downloads and download a git installer

Set Git Config GlobalOpen a bash and add this two lines:

$ git config --global user.name "John Doe"
$ git config --global user.email [email protected]

3. Install Phonegap

npm install -g phonegap

4. Download Java JDK

Download a Java JDK from http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html

5. Download ANT

From http://ant.apache.org/bindownload.cgi and unzip it somewhere

6. Download the ADT Bundle

From http://developer.android.com/sdk/index.html#download and unzip it somewhere

7. Configure Path

From the Desktop, right-click My Computer and click Properties.
Click Advanced System Settings link in the left column.
In the System Properties window click the Environment Variables button.
Select the PATH variable from the System variables section.
Select the Edit button.
Inside the ADT Bundle folder that you unzipped, copy the path for \sdk\tools and add to the PATH variable.
Copy the path for \sdk\platform-tools to the PATH variable too.

8. Configure JAVA_HOME

Create a new Environment Variable named JAVA_HOME and add the path to your JDK to this variable. It is something like that “C:\Program Files\Java\jdk1.8.0_05”

9. Configure ANT_HOME

Create a new Environment Variable named JAVA_HOME and add the path to the ANT folder that you unzipped

10. Create and Launch a Hello World

Using the shell type:

phonegap create hello
cd hello 
phonegap run android

Inside your hello project directory,  create an index.html file into www folder.
Content of index.html file:

<html><body>Hello World</body></html>

then use the shell again :

phonegap run android

And see the Hello World on your phone.

 

Tutorial Virtualenv para iniciantes (windows)

Esse tutorial de Virtualenv para Iniciantes ensina como instalar e utilizar o Virtualenv no windows.

Primeiramente, instale o VirtualEnv usando o pip ou o easy_install:

c:\Python27\Scripts\pip.exe install virtualenv

 

Depois crie um diretório onde estarão seus ambientes virtuais

C:\Users\Fernando>mkdir virtualenv

Agora crie o seu ambiente virtual:

C:\Users\Fernando>c:\Python27\Scripts\virtualenv.exe virtualenv\virtual_1

Neste caso, você criou um ambiente virtual com python 2.7 e todos os pacotes que você já tem no seu sistema (que tenham sido instalados com pip ou com easy_install)

Se você está criando um novo ambiente virtual pode ser mais inteligente não usar todos os seus pacotes e colocar somente aqueles que você precisa utilizar por algum motivo, como por exemplo utilizar um sistema criado em versões anteriores de pacotes que você já atualizou

Então, vamos remover o virtualenv que criamos e fazer um novo com a opção –no-site-packages:

C:\Users\Fernando>del virtualenv\virtual_1
C:\Users\Fernando\virtualenv\virtual_1\*, Você tem certeza (S/N)? S
C:\Users\Fernando>c:\Python27\Scripts\virtualenv.exe virtualenv\virtual_1 --no-site-packages

OK, agora você tem um virtual env zerado e pronto pra colocar os pacotes que você quer.

Para ativar e usar este virtualenv é bem simples:

C:\Users\Fernando>virtualenv\virtual_1\Scripts\activate

Agora, antes da linha de comando, aparecerá um flag (virtual_1) dizendo que você está usando o virtual env “virtual_1”.

(virtual_1) C:\Users\Fernando>

para instalar alguns pacotes você deve usar o pip.

Imagine que você precisa usar o Django1.2 para acessar um projeto antigo e não quer zuar a sua instalação do Django1.6, você pode baixar esta versão dentro de um virtualenv utilizando pip install Django==1.2

Muito simples, né? Agora vou clonar um projeto bem antigo e colocá-lo pra funcionar dentro do virtual_1

(virtual_1) C:\Users\Fernando>git clone https://github.com/ffreitasalves/controle-de-ponto-biometrico.git
(virtual_1) C:\Users\Fernando\controle-de-ponto-biometrico>pip install -r requirements.txt

Agora eu posso criar minha base com python manage.py syncdb e colocar o servidor de desenvolvimento pra funcionar com python manage.py runserver e voilá, está funcionando!