Today we will go through how to go from a python script to packaged executable with a guided user interface (GUI) for users. First off we still start by writing the scripts that we would like to share with others to be able to use, especially for users that may be uncomfortable in a programming environment and would feel at home with a GUI.
My personal favourite part about Gooey, is that you are essentially creating a command line interface (CLI) tool, which Gooey then uses to generate a GUI. This eliminates having two separate code bases to facilitate CLI & GUI users, which can be very painful at times.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 | def print_file_name(path,filesize):
"""
Inputs:
path (str): filepath to file selected
filezize (bool): whether to print the file size or not
Prints file name of file from path given and if filesize is true then will print the total size of the file in bytes
"""
print(os.path.basename(path))
if filesize:
print(f"File size: {os.path.getsize(path)} bytes")
def get_files_in_folder(path,extension):
"""
Inputs:
path (str): path to folder selected
extension (str): extension to filter by
Prints all files in folder, if an extension is given, will only print the files with the given extension
"""
f = []
for (dirpath, dirnames, filenames) in os.walk(path):
if extension:
for filename in filenames:
if filename.endswith(extension):
f.append(filename)
else:
f.extend(filenames)
return f
|
The 2 functions defined above are for getting information of selected files, or returning a list of files found within a folder (and subfolders).
Now to use Gooey, we need to define a 'main' function for parsing the arguments for the GUI to generate controls. As Gooey is based on the argparse library, if you have previously built CLI tools with argparse, the migration to Gooey is quite simplistic. However as there is always edge cases, ensure to check your tools functionality once you have developed it.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | @Gooey(optional_cols=2,program_name="Gooey Executable with Pyinstaller")
def parse_args():
prog_descrip = 'Pyinstaller example with Gooey'
parser = GooeyParser(description=prog_descrip)
sub_parsers = parser.add_subparsers(help='commands', dest='command')
first_parser = sub_parsers.add_parser('file',help='This function prints the chosen file name')
first_parser.add_argument('file_path',help='Select a random file',type=str,widget='FileChooser')
first_parser.add_argument('--file-size',help='Do you want to print the file size?',action='store_true')
second_parser = sub_parsers.add_parser('folder',help='This funtion prints all files in a folder')
second_parser.add_argument('folder_path',help='Select a folder',type=str,widget='DirChooser')
second_parser.add_argument('--file-type',help='Specify file type with .jpg',type=str)
args = parser.parse_args()
return args
|
By using the Gooey decorator we are able to define many different layout options for our GUI. Since we are trying to enable users to use multiple scripts which are different and separate, I personally like to the optional columns layout, but there are many other types of layouts which can be seen here: https://github.com/chriskiehl/Gooey#layout-customization.
Following this we create our argument parsing function, and in which we define parsers, subparsers and add the arguments. This post will not be covering how to write CLIs, but it is on the list for future posts.
To complete the script, we need to put in the functionality at startup.
| if __name__ == '__main__':
conf = parse_args()
if conf.command == 'file':
print_file_name(conf.file_path,conf.file_size)
elif conf.command == 'folder':
print(get_files_in_folder(conf.folder_path,conf.file_type))
|
By embedding the command names within the arguments we are able to use a variety of functions which may or may not be interconnected.
Once this file is run will generate the following:
Which are fully embedded within the windows file explorer system for selecting files, folders, etc.
Now to package this GUI as an executable, we use PyInstaller. By following Chris Kiehl's (Developer of Gooey) instructions on using Pyinstaller and Gooey: https://chriskiehl.com/article/packaging-gooey-with-pyinstaller. All we need to is create a build.spec file within our directory and run pyinstaller build.spec.
This will then generate a build folder and a dist folder within your current directory. The build folder will contain all the files used in generating the executable, which is found within the dist folder.
The code in it's entirety is:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 | from gooey import Gooey, GooeyParser
import os
@Gooey(optional_cols=2,program_name="Gooey Executable with Pyinstaller")
def parse_args():
prog_descrip = 'Pyinstaller example with Gooey'
parser = GooeyParser(description=prog_descrip)
sub_parsers = parser.add_subparsers(help='commands', dest='command')
first_parser = sub_parsers.add_parser('file',help='This function prints the chosen file name')
first_parser.add_argument('file_path',help='Select a random file',type=str,widget='FileChooser')
first_parser.add_argument('--file-size',help='Do you want to print the file size?',action='store_true')
second_parser = sub_parsers.add_parser('folder',help='This funtion prints all files in a folder')
second_parser.add_argument('folder_path',help='Select a folder',type=str,widget='DirChooser')
second_parser.add_argument('--file-type',help='Specify file type with .jpg',type=str)
args = parser.parse_args()
return args
def print_file_name(path,filesize):
"""
Inputs:
path (str): filepath to file selected
filezize (bool): whether to print the file size or not
Prints file name of file from path given and if filesize is true then will print the total size of the file in bytes
"""
print(os.path.basename(path))
if filesize:
print(f"File size: {os.path.getsize(path)} bytes")
def get_files_in_folder(path,extension):
"""
Inputs:
path (str): path to folder selected
extension (str): extension to filter by
Prints all files in folder, if an extension is given, will only print the files with the given extension
"""
f = []
for (dirpath, dirnames, filenames) in os.walk(path):
if extension:
for filename in filenames:
if filename.endswith(extension):
f.append(filename)
else:
f.extend(filenames)
return f
if __name__ == '__main__':
conf = parse_args()
if conf.command == 'file':
print_file_name(conf.file_path,conf.file_size)
elif conf.command == 'folder':
print(get_files_in_folder(conf.folder_path,conf.file_type))
|
If you run into an error on Windows with the alert "Failed to execute script pyi_rth_pkgres", install the dev version of pyinstaller
pip install https://github.com/pyinstaller/pyinstaller/archive/develop.zip
This was noted in this issue on github: https://github.com/pyinstaller/pyinstaller/issues/2137