Featured Post

Introduction to Python Tales

Hello my friends, my name is Nelson Carrasquel, and perhaps as you I have the philosophy of coding for life, since 2013 when I started programming with Python (I have started before in 2008 with Delphi and some Visual Basic, but that is my dark past) my value as a Developer really increased due to the things that I found that could be possible with Python, now Python (with some Javascript) are the core languages for almost every challenge that I face in the programming world. I am a Chemical Engineer graduated from the UCV (Central University of Venezuela) in 2015, was working as an engineer for almost two years for an Automation and Control Systems Company, programming with LabVIEW almost all the time, but also I was tutoring as a part time job, helping students from all around the world (Not all the world but all the America) in their Python and Javascript assignments and projects; I found out that I really enjoy teaching and tutoring programming languages, and from that on I was kin…

Python to Exe to Windows Installer

Hello my friends, for this entry we are going to talk about a topic very often asked in the social media, how can we compile Python to a Windows Executable? Well in short answer, you cannot, because Python is not a compiled language, instead it is an interpreted language, but what we can do is to trick the Python Interpreter to run only a specific script as a windows executable, and after that we can create a Python Binary Installer, pretty cool right? Well follow me in the next lines to explain you how you can turn a Python GUI program into a Windows Executable.




Base Script

First let's talk about our base script, for the purpose of example for this tutorial; we are going to use the next script
import string

from Tkinter import *

""" The function takes in a string, makes all characters
lowercase, and removes all punctuation including space.
This function should be called by vig_encrypt before encryption. """
def preprocess(s):

     s = s.replace(" """).rstrip()

     result = ""

     for ch in s:

          if not ch in string.punctuation:

               result += ch
              
     return result.lower()
    
""" Given the character 'ch' and an integer k,
    representing the number of positions to shift this character,
    return the result of shifting 'ch' by 'k' positions"""
def shift(ch,k):
     return chr(ord('a')+(ord(ch)-ord('a')+k)%26)

""" Given a plain text character and a key character, it calls
    the shift function to generated the encrypted character"""
def char_encrypt(plaintextchar,keychar):

     alphabet = string.ascii_lowercase
     index = alphabet.index(keychar)

     return shift(plaintextchar, index)

""" Given a plain text character and a key character, it calls
the shift function to generated the decrypted character """
def char_decrypt(ciphertextchar,keychar):

     alphabet = string.ascii_lowercase
     index = alphabet.index(keychar)

     return shift(ciphertextchar-index)

""" Encrypt with Vigenere cipher """
def vig_encrypt(plaintext,key):

     processed = preprocess(plaintext)
     result = ""
    
     for index, ch in enumerate(processed):

          if index >= len(key):

               index = index % len(key)
              
          result += char_encrypt(ch, key[index])

     return result
  
""" Decrypt with Vigenere cipher """
def vig_decrypt(ciphertext,key):

     result = ""

     for index, ch in enumerate(ciphertext):

          if index >= len(key):

               index = index % len(key)

          result += char_decrypt(ch, key[index])

     return result
        

############################################################################
This code detects which button the user clicked on.
If the user clicked on the Encrypt button, it will calle the function
# 'vig_encrpt' to encrypt the text. Otherwise, if the user clicked on the Decrypt
button, it will call the function 'vig_decrypt' to decrypt
def button_handler(event):
    window.update()
    w=event.widget
    if w==encrypt_button:
        plaintext=the_text.get(0.0,END)
        plaintext=plaintext[:len(plaintext)-1#strip newline character
        key = the_key.get()
        ciphertext=vig_encrypt(plaintext,key)
        the_text.delete(0.0,END)
        the_text.insert(0.0,ciphertext)
        the_text.configure(bg="indian red")
    elif w==decrypt_button:
        ciphertext=the_text.get(0.0,END)
        ciphertext=ciphertext[:len(ciphertext)-1]#strip newline character
        key = the_key.get()
        plaintext=vig_decrypt(ciphertext,key)
        the_text.delete(0.0,END)
        the_text.insert(0.0,plaintext)
        the_text.configure(bg="light green")

This code creates the GUI
window = Tk()
creat the textbox for the user to enter the text (to encrypt/decrpt)
textlabel=Label(window,text='Plaintext/Ciphertext', font = ("comic sans ms"16"bold"))
textlabel.grid(row=0,column=0,columnspan=3)
the_text=Text(window,width=40,height=20,bg = "light green", font = ("comic sans ms"14"bold"))
the_text.grid(row=1,column=0,columnspan=3)
# create the textbox for user to enter the key
keylabel=Label(window,text='Key', font = ("comic sans ms"14"bold"))
keylabel.grid(row=2,column=0)
the_key=Entry(window, bg="goldenrod", text="<Enter Key>")
the_key.grid(row=3,column=0)
# create button to allow user to encrypt text
encrypt_button=Button(window,text='Encrypt')
encrypt_button.grid(row=4,column=0)
encrypt_button.bind('<Button-1>',button_handler)
# create button to allow user to decrypt text
decrypt_button=Button(window,text='Decrypt')
decrypt_button.grid(row=4,column=1)
decrypt_button.bind('<Button-1>',button_handler)
window.mainloop()

If we execute this script we are going to have the next GUI program

 With a very simple graphic user interface it holds two text inputs and two buttons with some labels, the purpose of this program is to encrypt/decrypt a text with the Vigenere Cypher using a string Key, we can another some other screenshots.

Pretty cool right? Well… maybe not, but the purpose of this tutorial is not GUI Programming with Python, we can cover that in another entry, for now we just need a basic GUI script so we can turn it into a Windows Executable.

Using Pyinstaller

Pyinstaller is going to be the library that we are going to use to convert a script into an executable, this doesn't actually compile Python code, but instead builds a smaller Python distribution for only to run a specified Python script, this process will generate a bunch of files with a main .exe file, with will have the same name as the Python script, for instance encryption.py will generate encryption.exe.
We can use setuptools to install pyinstaller from the internet by running this command in a terminal
Microsoft Windows [Version 6.1.7600]
Copyright (c) 2009 Microsoft Corporation. All rights reserved.

C:\Users\My User>pip install pyinstaller

Next thing to do is to move to the directory that we are holding our python script by using the cd command; once we are in the correct directory we can just execute the next command.
Microsoft Windows [Version 6.1.7600]
Copyright (c) 2009 Microsoft Corporation. All rights reserved.

C:\Users\My User\Script Directory>pyinstaller --noconsole encryption.py

It is very important to feed --noconsole option to the command, so we can hide the Python console during the GUI Program execution. Another thing to point out is that you might need Administrative Privileges for executing this command, and also some antivirus can affect this process by removing the .exe files generated, you might need to disable the antivirus for a few moments.
An optional option could be the use of --onefile, this will create a single .exe file with no other program files, this can be usefull but for very large programs you will have really heavy .exe files and could be problem later in the road.
Finally this will generate two directories in the same directory as the Python script, dist and build directories; inside dist you will find a directory with the same name as the script, for our example will be the directory encryption, which will have this content.

These are the program files, for this basic program It takes roughly 12.2 MB, portable ready to use with a pen drive, If you double click on the encryption.exe, you will get the program.

Another thing to add is that the process of using pyinstaller will generate a .spec file, which will hold the specifications of the build and distribution, if take a look at this file we will see some Python code.
# -*- mode: python -*-
 
block_cipher = None
 
 
a = Analysis(['encryption.py'],
             pathex=['K:\\Personal\\Blog\\Blog Entries\\Entry 11'],
             binaries=[],
             datas=[],
             hiddenimports=[],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          exclude_binaries=True,
          name='encryption',
          debug=False,
          strip=False,
          upx=True,
          console=False )
coll = COLLECT(exe,
               a.binaries,
               a.zipfiles,
               a.datas,
               strip=False,
               upx=True,
               name='encryption')

We can make some modifications to fit our requirements, but full details about this you can find it in the pyinstaller documentation, a cool trick that we can do here for this file is to add a custom icon file, we can add a parameter to the EXE Instantiation, using the icon parameters followed by the path directory to the icon.
exe = EXE(pyz,
          a.scripts,
          exclude_binaries=True,
          name='encryption',
          debug=False,
          strip=False,
          upx=True,
          console=False,
          icon='C:\\Users\\Path\\To\\Icon\\logo.ico')

Creating the Windows Installer

Now that we have all the program files we can create our windows installer, how are going to do this? Well we can use some software for this purpose but I strongly recommend the use of NSIS, which is an open source tool and easy to use, this is its main interface

Mainly there are two ways of creating an installer with this tool, one is using a NSI script, but an easier way is to use a zip file, in order to use this option you need to zip your program files first, and then click on "Installer based on Zip file", you get the next interface.

The basics to information to provide is the source Zip file, you can use the Open Dialog by clicking the open… button, next you choose the interface, a Classic interface is that one with a blue screen background used in the past (really ugly), choose Modern instead; then the Default Folder, there are several options in here, the most common is to use the program files default directory of Windows, and finally the output EXE for the installer, I always choose the Desktop, and click on Generate.
You will get the next final information, and you can click on close.


Installing our Program

If you go to your desktop you will find your installer, if you double click it you will launch the installer wizard.

You can click on install and finally after the process is done you can click on close. Unfortunately this will only install the files in the Program Files with no Start Menu or Desktop Icon, for this you need to go further in the NSIS documentation, but once you have master it, you will create cool installers of your own.
We have come to the end of this entry; I hope you have found this entry helpful to you. So thanks for coming by my new entry and I hope you have enjoyed this as much as I enjoyed writing it, stop by the comments if you want to discuss about this, share in your social media and subscribe. Cheers my friends.