So you have just compiled your Windows Python application. You want to make an installer for your users to easily get the application onto their PCs. You have tried using cx_Freeze's bdist_msi option but it is not advanced enough. Perhaps you want to run a script on installation or on uninstall, or want an easy way of adding an icon to the user's desktop.
Now the easiest tool to use is inno. In this post we're going to take a look at how to use inno's setup wizard to package your application, as well as editing the script file.
So the first thing you are going to want to do is to jump onto inno's download page and download inno setup compiler. Once it's done, open the application and select 'Create a new script file using the Script Wizard'. You will then be greeted with the following window:
If you want to run a script on installation or on uninstall, add to [Run] and add the [UninstallRun] sections as appropriate. For example I made a script that takes the installation location and saves it in a constant location in the AppData folder. This file is then removed by another script on uninstall. This script needs to know the install location, so it is taken as an argument. These scripts are shown at the end. If your argument is a path, it is very important to escape the string otherwise the spaces or backslashes would mess up the path. This is what mine looked like:
When you are done, press the green go button.
You can go to the uninstall application by going to Tools > Add/Remove Programs:
Now the easiest tool to use is inno. In this post we're going to take a look at how to use inno's setup wizard to package your application, as well as editing the script file.
So the first thing you are going to want to do is to jump onto inno's download page and download inno setup compiler. Once it's done, open the application and select 'Create a new script file using the Script Wizard'. You will then be greeted with the following window:
Leave the box unchecked. We're going to use the Wizard to make the base of our install script.
Fill out all the information in the entry boxes. The application version will be very useful later when you want to update your application.
Here you can change where the default install location will be. You can change this with the dropdown box. You can also change the name of this folder, or disable asking the user for a different install location. It's probably a good idea to leave this unchecked, so the user can select their (x86) folder if they want to.
Here you need to select ALL of the files and folders cx_Freeze made in your folder. Remember to include the lib folder as well as the .dlls cx_Freeze made.
Here you can select the shortcuts to ask the user to make on installation. When the user is installing, the make desktop shortcut checkbox is automatically unchecked. We can change this in the script.
Here you can select a license file to show to the user on installation. The installer won't let the user continue the installation until they accept the terms of the install. You can skip this if you don't have a license file.
Here you can select all of the languages that are available to use in the installer. The installer will ask the user for the language they want to use on install. It's probably a good idea to only select the languages that your application supports.
You can change the place where inno will place the finished installer and the name of the installer here. If you want to, you can change the installer's icon. I'd prefer to keep it as the default one so it's easy to see that the file is an installer. If you want to do this, leave this field blank. You can also setup a password if you want to.
I'd leave this boxed checked. It basically sets a bunch of constants in the setup script to use as variables so you only need to change one thing.
The base setup script has been made. If it asks you if you want to compile the script now, select 'No' because there's still a few things that need changing.
We are going to change the line I highlighted to the following:
AppVerName={#MyAppName}
This changes the name of the application in the uninstall applications window to the name of the file without the version number. It is pointless to do this because the version number is shown anyway. The result of this will become clear later.
At the bottom of the [Setup] section you can add an icon to show in the Uninstall application window using UninstallDisplayIcon=. I will demonstrate this later. For example, mine was
UninstallDisplayIcon=C:\Users\Edward\Documents\workingdir\eehph2\build\exe.win-amd64-3.6\Assets\icon.ico
Delete 'Flags: unchecked' here to make the 'make desktop checkbox option' checked by default.
Next:
We are going to change these lines to:
Source: "C:\Users\Edward\Documents\workingdir\eehph2\build\exe.win-amd64-3.6\Assets\*"; DestDir: "{app}\Assets"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "C:\Users\Edward\Documents\workingdir\eehph2\build\exe.win-amd64-3.6\lib\*"; DestDir: "{app}\lib"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "C:\Users\Edward\Documents\workingdir\eehph2\build\exe.win-amd64-3.6\tcl\*"; DestDir: "{app}\tcl"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "C:\Users\Edward\Documents\workingdir\eehph2\build\exe.win-amd64-3.6\tk\*"; DestDir: "{app}\tk"; Flags: ignoreversion recursesubdirs createallsubdirs
Don't copy and paste this in because your files will be different. What you need to do is add a backslash and the name of the folder to all of the folders you selected to include in the DestDir. If you don't do this, inno will place all of the files loose in the installation folder. Python isn't expecting the application to be used in this way, so your program won't work. For example:
DestDir: "{app}";
Would become
DestDir: "{app}\lib";
For the lib folder.
If you want to run a script on installation or on uninstall, add to [Run] and add the [UninstallRun] sections as appropriate. For example I made a script that takes the installation location and saves it in a constant location in the AppData folder. This file is then removed by another script on uninstall. This script needs to know the install location, so it is taken as an argument. These scripts are shown at the end. If your argument is a path, it is very important to escape the string otherwise the spaces or backslashes would mess up the path. This is what mine looked like:
[Run]
Filename: "{app}\on_install_setup.exe"; Parameters: """{app}\"
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
[UninstallRun]
Filename: "{app}\cleanup.exe";
When you are done, press the green go button.
Be aware that the application will automatically install on your PC after it's finished compiling.
You can go to the uninstall application by going to Tools > Add/Remove Programs:
Here you can see how we changed the script earlier helped the program:
Changing AppVerName={#MyAppName} made it so that the version number isn't shown in the Name column, as it is in AutoHotkey and GIMP. Showing the version number is pointless because the version is in the Version column. We can also see that there is an icon there. Without this, it would have the automatic icon like Microsoft Visual C++ does:
Lets have a look at how inno is different to cx_Freeze's bdist_msi:
Here's what cx_Freeze's bdist_msi looks like. You can change the install location... but that's about it. You can add a desktop icon, but the user cannot disable this. There is no way of adding a Start Menu shortcut. Here is the same screen in inno:
I'll let you decide which looks better. Inno also has the following screens:
Licence agreement that won't let you continue until you accept the terms
Option to create a desktop shortcut
It's possible to create a start menu shortcut,
Not to mention the most powerful advantage in my opinion, the setup and uninstall scripts.
To finish off, we're going to look at how to create setup scripts.
Here's the source code for the install script:
1: import sys
2: import os
3:
4: location = sys.argv[1]
5:
6: appdata = os.path.join(os.environ['ALLUSERSPROFILE'], "EEHPH2")
7:
8: if not os.path.exists(appdata):
9: os.mkdir(appdata)
10:
11: file = open(os.path.join(appdata, "location.txt"), "w")
12: file.write(location)
13: file.close()
It uses sys.argv to get the argument that is inserted into it to the script in the inno script. This is explained in this blog post. It writes the install location onto %ALLUSERSPROFILE%\EEHPH2\location.txt. This is so my application knows where the install location is. This is done because when a user clicks on a file and selects open with my app, the current working directory (cwd) is set to the one in which the file is located, not the application folder. This means the .dlls that the program needs to work are not present, so the program does not work. My program calls a small .exe which changes the cwd to the install location (as is known thanks to this file) and runs the main application as a module. After the user closes, it switches back to the original. I have another script that deletes this file when the user uninstalls:
1: import shutil
2: import os
3:
4: try:
5: shutil.rmtree(os.path.join(os.environ["ALLUSERSPROFILE"], "EEHPH2"))
6: except:
7: pass
Both of these scripts were compiled using pyinstaller:
pyinstaller cleanup.py --icon icon.ico --onefile
The other script was done in the same way.
This post was pretty long; so thanks for getting to this point. If this helped you, please leave a comment below.
Comments
Post a Comment