Skip to content

Commit 4df1e6b

Browse files
authored
added version 1.0.0
1 parent e5f5f0b commit 4df1e6b

File tree

2 files changed

+189
-0
lines changed

2 files changed

+189
-0
lines changed

EasyEXE.py

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
import os
2+
import json
3+
from PyInstaller.__main__ import run as pyinstaller_run
4+
import ctypes
5+
from ctypes import wintypes
6+
import pythoncom
7+
from win32com.client import Dispatch, constants
8+
9+
10+
CONFIG_FILE = "easyexe-config.json"
11+
welcome_art = r"""
12+
13+
███████ █████ ███████ ██ ██ ███████ ██ ██ ███████
14+
██ ██ ██ ██ ██ ██ ██ ██ ██ ██
15+
█████ ███████ ███████ ████ █████ ███ █████
16+
██ ██ ██ ██ ██ ██ ██ ██ ██
17+
███████ ██ ██ ███████ ██ ███████ ██ ██ ███████
18+
19+
20+
"""
21+
22+
23+
def clear_console():
24+
os.system('cls' if os.name == 'nt' else 'clear')
25+
26+
27+
def save_config(config):
28+
with open(CONFIG_FILE, "w") as config_file:
29+
json.dump(config, config_file, indent=4)
30+
31+
32+
def load_config():
33+
if os.path.exists(CONFIG_FILE):
34+
with open(CONFIG_FILE, "r") as config_file:
35+
return json.load(config_file)
36+
return {}
37+
38+
39+
def set_file_properties(file_path, properties):
40+
pythoncom.CoInitialize()
41+
try:
42+
# Load the DLL
43+
ctypes.windll.kernel32.SetFileAttributesW(file_path, 0)
44+
# Open the file to set properties
45+
handle = ctypes.windll.kernel32.CreateFileW(file_path, ctypes.c_uint32(0x10000000), ctypes.c_uint32(0), None,
46+
ctypes.c_uint32(3), ctypes.c_uint32(0x80), None)
47+
# Set properties
48+
for key, value in properties.items():
49+
prop_key = key.encode('utf-16le')
50+
prop_value = value.encode('utf-16le')
51+
# Use wintypes.LPWSTR for Unicode strings
52+
ctypes.windll.kernel32.SetFileInformationByHandle(handle, 22, wintypes.LPWSTR(prop_key), len(prop_key))
53+
ctypes.windll.kernel32.SetFileInformationByHandle(handle, 23, wintypes.LPWSTR(prop_value), len(prop_value))
54+
# Close the file handle
55+
ctypes.windll.kernel32.CloseHandle(handle)
56+
finally:
57+
pythoncom.CoUninitialize()
58+
59+
60+
def convert_py_to_exe(script_path, create_onefile=False, create_windowed=False, custom_name=None, custom_distpath=None,
61+
custom_icon=None, file_properties=None):
62+
if not os.path.exists(script_path):
63+
clear_console()
64+
print(welcome_art)
65+
print(f"The specified script does not exist: {script_path}")
66+
return
67+
try:
68+
# Build options based on user input
69+
options = [script_path]
70+
if create_onefile:
71+
options.append('--onefile')
72+
if create_windowed:
73+
options.append('--windowed')
74+
if custom_name:
75+
options.extend(['--name', custom_name])
76+
if custom_distpath:
77+
options.extend(['--distpath', custom_distpath])
78+
if custom_icon:
79+
options.extend(['--icon', custom_icon])
80+
# Run PyInstaller with the specified options
81+
pyinstaller_run(options)
82+
clear_console()
83+
print(welcome_art)
84+
print(f"Successfully converted {script_path} to an executable.")
85+
# Set file properties
86+
if file_properties:
87+
set_file_properties(os.path.join(custom_distpath, f"{custom_name}.exe"), file_properties)
88+
# Save configuration to the cache file
89+
config = {
90+
'script_path': script_path,
91+
'create_onefile': create_onefile,
92+
'create_windowed': create_windowed,
93+
'custom_name': custom_name,
94+
'custom_distpath': custom_distpath,
95+
'custom_icon': custom_icon,
96+
'file_properties': file_properties
97+
}
98+
save_config(config)
99+
except Exception as e:
100+
clear_console()
101+
print(welcome_art)
102+
print(f"An unexpected error occurred: {e}")
103+
104+
105+
def get_user_input():
106+
# Load configuration from the cache file
107+
config = load_config()
108+
clear_console()
109+
print(welcome_art)
110+
script_path_input = input(f"Enter the path to your Python script [{config.get('script_path', '')}]: ").strip(
111+
'"') or config.get('script_path', '')
112+
use_json_details = input("Do you want to use file details from the JSON file? (y/n) [y]: ").lower() != 'n'
113+
if use_json_details:
114+
file_properties = config.get('file_properties', {})
115+
else:
116+
file_properties = {}
117+
print("\nEnter file properties (press Enter to skip):")
118+
file_properties['FileDescription'] = input(
119+
f"File Description [{config.get('file_properties', {}).get('FileDescription', '')}]: ").strip() or config.get(
120+
'file_properties', {}).get('FileDescription', '')
121+
file_properties['FileVersion'] = input(
122+
f"File Version [{config.get('file_properties', {}).get('FileVersion', '')}]: ").strip() or config.get(
123+
'file_properties', {}).get('FileVersion', '')
124+
file_properties['ProductName'] = input(
125+
f"Product Name [{config.get('file_properties', {}).get('ProductName', '')}]: ").strip() or config.get(
126+
'file_properties', {}).get('ProductName', '')
127+
file_properties['ProductVersion'] = input(
128+
f"Product Version [{config.get('file_properties', {}).get('ProductVersion', '')}]: ").strip() or config.get(
129+
'file_properties', {}).get('ProductVersion', '')
130+
file_properties['LegalCopyright'] = input(
131+
f"Copyright [{config.get('file_properties', {}).get('LegalCopyright', '')}]: ").strip() or config.get(
132+
'file_properties', {}).get('LegalCopyright', '')
133+
file_properties['Language'] = input(
134+
f"Language [{config.get('file_properties', {}).get('Language', '')}]: ").strip() or config.get(
135+
'file_properties', {}).get('Language', '')
136+
# Other user choices
137+
clear_console()
138+
print(welcome_art)
139+
print("\nEnter options (press Enter to skip):")
140+
create_onefile = input("Create a single executable file (--onefile)? (y/n) [n]: ").lower() == 'y'
141+
create_windowed = input("Create a windowed (GUI) executable (--windowed)? (y/n) [n]: ").lower() == 'y'
142+
custom_name_input = input(
143+
f"Enter a custom name for the executable (press Enter to skip) [{config.get('custom_name', '')}]: ").strip() or config.get(
144+
'custom_name', '')
145+
custom_distpath_input = input(
146+
f"Enter a custom output directory for the executable (press Enter to skip) [{config.get('custom_distpath', '')}]: ").strip() or config.get(
147+
'custom_distpath', '')
148+
custom_icon_input = input(
149+
f"Enter the path to a custom icon for the executable (press Enter to skip) [{config.get('custom_icon', '')}]: ").strip() or config.get(
150+
'custom_icon', '')
151+
# Save user choices to configuration
152+
config.update({
153+
'custom_name': custom_name_input,
154+
'custom_distpath': custom_distpath_input,
155+
'custom_icon': custom_icon_input,
156+
'file_properties': file_properties
157+
})
158+
save_config(config)
159+
160+
return script_path_input, create_onefile, create_windowed, custom_name_input, custom_distpath_input, custom_icon_input, file_properties
161+
162+
163+
if __name__ == "__main__":
164+
script_path, create_onefile, create_windowed, custom_name, custom_distpath, custom_icon, file_properties = get_user_input()
165+
convert_py_to_exe(
166+
script_path=script_path,
167+
create_onefile=create_onefile,
168+
create_windowed=create_windowed,
169+
custom_name=custom_name,
170+
custom_distpath=custom_distpath,
171+
custom_icon=custom_icon,
172+
file_properties=file_properties
173+
)

easyexe-config.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"script_path": "PATH_TO_SCRIPT",
3+
"create_onefile": true,
4+
"create_windowed": false,
5+
"custom_name": "TESTER",
6+
"custom_distpath": "PATH_TO_SAVE",
7+
"custom_icon": "PATH_TO_ICON",
8+
"file_properties": {
9+
"filedescription": "FILE DESCRIPTION",
10+
"fileversion": "1.2.3",
11+
"productname": "TEST_NAME",
12+
"productversion": "1.2.3",
13+
"legalcopyright": "TEST_COPYRIGHT",
14+
"language": "TEST_LANGUAGE"
15+
}
16+
}

0 commit comments

Comments
 (0)