mit neuen venv und exe-Files
This commit is contained in:
1
venv3_12/Lib/site-packages/auto_py_to_exe/__init__.py
Normal file
1
venv3_12/Lib/site-packages/auto_py_to_exe/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
__version__ = "2.44.2"
|
||||
110
venv3_12/Lib/site-packages/auto_py_to_exe/__main__.py
Normal file
110
venv3_12/Lib/site-packages/auto_py_to_exe/__main__.py
Normal file
@@ -0,0 +1,110 @@
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
from . import shims
|
||||
|
||||
shims.install_shims()
|
||||
|
||||
from . import __version__, config, ui, validation # noqa: E402
|
||||
|
||||
|
||||
def start_ui(logging_level, build_directory_override):
|
||||
"""Open the interface"""
|
||||
# Suppress the global logger to only show error+ to the console
|
||||
logging.getLogger().handlers[0].setLevel(logging_level)
|
||||
|
||||
# Setup the build folder
|
||||
if build_directory_override is None:
|
||||
config.temporary_directory = tempfile.mkdtemp()
|
||||
else:
|
||||
config.temporary_directory = build_directory_override
|
||||
|
||||
# Start UI
|
||||
ui.start(config.ui_open_mode)
|
||||
|
||||
# Remove build folder to clean up from builds (if we created it)
|
||||
if build_directory_override is None:
|
||||
shutil.rmtree(config.temporary_directory)
|
||||
|
||||
|
||||
def run():
|
||||
"""Module entry point"""
|
||||
# Parse arguments
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"filename", nargs="?", type=validation.argparse_file_exists, help="pass a file into the interface", default=None
|
||||
)
|
||||
parser.add_argument(
|
||||
"-nc",
|
||||
"--no-chrome",
|
||||
action="store_true",
|
||||
help="do not open in chrome's app mode",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-nu",
|
||||
"--no-ui",
|
||||
action="store_true",
|
||||
help="do not open a browser to show the application and simply print out where it's being hosted from. "
|
||||
"When using this option, you must manually stop the application using Ctrl+C",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--config",
|
||||
nargs="?",
|
||||
type=validation.argparse_file_json,
|
||||
help="provide a json file containing a UI configuration to pre-populate the ui",
|
||||
default=None,
|
||||
)
|
||||
parser.add_argument("-o", "--output-dir", nargs="?", help="the directory to put output in", default="output")
|
||||
parser.add_argument(
|
||||
"-bdo",
|
||||
"--build-directory-override",
|
||||
nargs="?",
|
||||
help="a directory for build files (overrides the default)",
|
||||
default=None,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-lang",
|
||||
"--language",
|
||||
nargs="?",
|
||||
help="hint the language to use by default - language codes can be found in the README",
|
||||
default=None,
|
||||
metavar="LANGUAGE_CODE",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--logging-level",
|
||||
nargs="?",
|
||||
type=validation.argparse_logging_level,
|
||||
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
|
||||
help="the level to use for logging - defaults to ERROR",
|
||||
default="ERROR",
|
||||
)
|
||||
parser.add_argument("-v", "--version", action="version", version=f"auto-py-to-exe {__version__}")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Setup config from arguments
|
||||
config.package_filename = args.filename
|
||||
config.supplied_ui_configuration = args.config
|
||||
config.default_output_directory = os.path.abspath(args.output_dir)
|
||||
config.language_hint = args.language
|
||||
|
||||
if args.no_ui:
|
||||
config.ui_open_mode = config.UIOpenMode.NONE
|
||||
elif args.no_chrome:
|
||||
config.ui_open_mode = config.UIOpenMode.USER_DEFAULT
|
||||
else:
|
||||
config.ui_open_mode = config.UIOpenMode.CHROME
|
||||
|
||||
# Validate --build-directory-override exists if supplied
|
||||
if (args.build_directory_override is not None) and (not os.path.isdir(args.build_directory_override)):
|
||||
raise ValueError("--build-directory-override must be a directory")
|
||||
|
||||
logging_level = getattr(logging, args.logging_level)
|
||||
start_ui(logging_level, args.build_directory_override)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
25
venv3_12/Lib/site-packages/auto_py_to_exe/config.py
Normal file
25
venv3_12/Lib/site-packages/auto_py_to_exe/config.py
Normal file
@@ -0,0 +1,25 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
class UIOpenMode:
|
||||
NONE = 0
|
||||
CHROME = 1
|
||||
USER_DEFAULT = 2
|
||||
|
||||
|
||||
# Temporary directory for packaging scripts to speed up consecutive builds. Created on application start.
|
||||
temporary_directory = None
|
||||
|
||||
# Frontend
|
||||
FRONTEND_ASSET_FOLDER = os.path.join(os.path.dirname(os.path.realpath(__file__)), "web")
|
||||
|
||||
# Pre-defined variables by Python
|
||||
DEFAULT_RECURSION_LIMIT = sys.getrecursionlimit()
|
||||
|
||||
# Argument-influenced configuration
|
||||
package_filename = None
|
||||
ui_open_mode = UIOpenMode.CHROME
|
||||
supplied_ui_configuration = None
|
||||
default_output_directory = os.path.abspath("output")
|
||||
language_hint = None
|
||||
87
venv3_12/Lib/site-packages/auto_py_to_exe/dialogs.py
Normal file
87
venv3_12/Lib/site-packages/auto_py_to_exe/dialogs.py
Normal file
@@ -0,0 +1,87 @@
|
||||
import platform
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
from tkinter import Tk
|
||||
except ImportError:
|
||||
try:
|
||||
from Tkinter import Tk
|
||||
except ImportError:
|
||||
# If no versions of tkinter exist (most likely linux) provide a message
|
||||
if sys.version_info.major < 3:
|
||||
print("Error: Tkinter not found")
|
||||
print('For linux, you can install Tkinter by executing: "sudo apt-get install python-tk"')
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("Error: tkinter not found")
|
||||
print('For linux, you can install tkinter by executing: "sudo apt-get install python3-tk"')
|
||||
sys.exit(1)
|
||||
try:
|
||||
from tkinter.filedialog import askdirectory, askopenfilename, askopenfilenames, asksaveasfilename
|
||||
except ImportError:
|
||||
from tkFileDialog import askdirectory, askopenfilename, askopenfilenames, asksaveasfilename
|
||||
|
||||
|
||||
root = Tk()
|
||||
root.withdraw()
|
||||
if platform.system() == "Windows":
|
||||
root.iconbitmap(str(Path(__file__).parent / "web/favicon.ico"))
|
||||
root.wm_attributes("-topmost", 1)
|
||||
|
||||
|
||||
def ask_file(file_type):
|
||||
"""Ask the user to select a file"""
|
||||
if (file_type is None) or (platform.system() == "Darwin"):
|
||||
file_path = askopenfilename()
|
||||
else:
|
||||
if file_type == "python":
|
||||
file_types = [("Python files", "*.py;*.pyw"), ("All files", "*")]
|
||||
elif file_type == "icon":
|
||||
file_types = [("Icon files", "*.ico"), ("All files", "*")]
|
||||
elif file_type == "json":
|
||||
file_types = [("JSON Files", "*.json"), ("All files", "*")]
|
||||
else:
|
||||
file_types = [("All files", "*")]
|
||||
file_path = askopenfilename(title="Select a file", filetypes=file_types)
|
||||
root.update()
|
||||
|
||||
# bool(file_path) will help filter our the negative cases; an empty string or an empty tuple
|
||||
return file_path if bool(file_path) else None
|
||||
|
||||
|
||||
def ask_files():
|
||||
"""Ask the user to select one or more files"""
|
||||
file_paths = askopenfilenames(title="Select one or more files")
|
||||
root.update()
|
||||
|
||||
return file_paths if bool(file_paths) else None
|
||||
|
||||
|
||||
def ask_folder():
|
||||
"""Ask the user to select a folder"""
|
||||
folder = askdirectory(title="Select a folder")
|
||||
root.update()
|
||||
|
||||
return folder if bool(folder) else None
|
||||
|
||||
|
||||
def ask_file_save_location(file_type):
|
||||
"""Ask the user where to save a file"""
|
||||
if (file_type is None) or (platform.system() == "Darwin"):
|
||||
file_path = asksaveasfilename(title="Select where to save")
|
||||
else:
|
||||
if file_type == "json":
|
||||
file_types = [("JSON Files", "*.json"), ("All files", "*")]
|
||||
else:
|
||||
file_types = [("All files", "*")]
|
||||
file_path = asksaveasfilename(title="Select where to save", filetypes=file_types)
|
||||
root.update()
|
||||
|
||||
if bool(file_path):
|
||||
if file_type == "json":
|
||||
return file_path if file_path.endswith(".json") else file_path + ".json"
|
||||
else:
|
||||
return file_path
|
||||
else:
|
||||
return None
|
||||
151
venv3_12/Lib/site-packages/auto_py_to_exe/packaging.py
Normal file
151
venv3_12/Lib/site-packages/auto_py_to_exe/packaging.py
Normal file
@@ -0,0 +1,151 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import shlex
|
||||
import shutil
|
||||
import sys
|
||||
import traceback
|
||||
from typing import Optional
|
||||
|
||||
from PyInstaller.__main__ import run as run_pyinstaller
|
||||
|
||||
from . import __version__ as version
|
||||
from . import config
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def __get_pyinstaller_argument_parser():
|
||||
from PyInstaller.building.build_main import __add_options as add_build_options
|
||||
from PyInstaller.building.makespec import __add_options as add_makespec_options
|
||||
from PyInstaller.log import __add_options as add_log_options
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
add_makespec_options(parser)
|
||||
add_build_options(parser)
|
||||
add_log_options(parser)
|
||||
|
||||
parser.add_argument(
|
||||
"filenames",
|
||||
metavar="scriptname",
|
||||
nargs="+",
|
||||
help=(
|
||||
"name of scriptfiles to be processed or "
|
||||
"exactly one .spec-file. If a .spec-file is "
|
||||
"specified, most options are unnecessary "
|
||||
"and are ignored."
|
||||
),
|
||||
) # From PyInstaller.__main__.run
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def get_pyinstaller_options():
|
||||
parser = __get_pyinstaller_argument_parser()
|
||||
|
||||
options = []
|
||||
for action in parser._actions:
|
||||
# Clean out what we can't send over to the ui
|
||||
# Here is what we currently have: https://github.com/python/cpython/blob/master/Lib/argparse.py#L771
|
||||
del action.container
|
||||
options.append(action)
|
||||
|
||||
return [o.__dict__ for o in options]
|
||||
|
||||
|
||||
def will_packaging_overwrite_existing(file_path: str, manual_name: Optional[str], one_file: str, output_folder: str):
|
||||
"""Checks if there is a possibility of a previous output being overwritten."""
|
||||
if not os.path.exists(output_folder):
|
||||
return False
|
||||
|
||||
no_extension = manual_name if manual_name is not None else ".".join(os.path.basename(file_path).split(".")[:-1])
|
||||
if one_file and no_extension + ".exe" in os.listdir(output_folder):
|
||||
return True
|
||||
if (not one_file) and no_extension in os.listdir(output_folder):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def __move_package(src, dst):
|
||||
"""Move the output package to the desired path (default is output/ - set in script.js)"""
|
||||
# Make sure the destination exists
|
||||
if not os.path.exists(dst):
|
||||
os.makedirs(dst)
|
||||
|
||||
# Move all files/folders in dist/
|
||||
for file_or_folder in os.listdir(src):
|
||||
_dst = os.path.join(dst, file_or_folder)
|
||||
# If this already exists in the destination, delete it
|
||||
if os.path.exists(_dst):
|
||||
if os.path.isfile(_dst):
|
||||
os.remove(_dst)
|
||||
else:
|
||||
shutil.rmtree(_dst)
|
||||
# Move file
|
||||
shutil.move(os.path.join(src, file_or_folder), dst)
|
||||
|
||||
|
||||
def package(pyinstaller_command, options):
|
||||
"""
|
||||
Call PyInstaller to package a script using provided arguments and options.
|
||||
:param pyinstaller_command: Command to supply to PyInstaller
|
||||
:param options: auto-py-to-exe specific options for setup and cleaning up
|
||||
:return: Whether packaging was successful
|
||||
"""
|
||||
|
||||
# Show current version
|
||||
logger.info("Running auto-py-to-exe v" + version)
|
||||
|
||||
# Notify the user of the workspace and setup building to it
|
||||
logger.info("Building directory: {}".format(config.temporary_directory))
|
||||
|
||||
# Override arguments
|
||||
dist_path = os.path.join(config.temporary_directory, "application")
|
||||
build_path = os.path.join(config.temporary_directory, "build")
|
||||
extra_args = ["--distpath", dist_path] + ["--workpath", build_path] + ["--specpath", config.temporary_directory]
|
||||
|
||||
logger.info("Provided command: {}".format(pyinstaller_command))
|
||||
|
||||
# Setup options
|
||||
increase_recursion_limit = options["increaseRecursionLimit"]
|
||||
output_directory = os.path.abspath(options["outputDirectory"])
|
||||
|
||||
if increase_recursion_limit:
|
||||
sys.setrecursionlimit(5000)
|
||||
logger.info("Recursion Limit is set to 5000")
|
||||
else:
|
||||
sys.setrecursionlimit(config.DEFAULT_RECURSION_LIMIT)
|
||||
|
||||
# Run PyInstaller
|
||||
fail = False
|
||||
try:
|
||||
pyinstaller_args = shlex.split(pyinstaller_command) + extra_args
|
||||
|
||||
# Display the command we are using and leave a space to separate out PyInstallers logs
|
||||
logger.info("Executing: {}".format(" ".join(pyinstaller_args)))
|
||||
logger.info("")
|
||||
|
||||
run_pyinstaller(pyinstaller_args[1:])
|
||||
except: # noqa: E722
|
||||
fail = True
|
||||
logger.exception("An error occurred while packaging")
|
||||
|
||||
# Move project if there was no failure
|
||||
logger.info("")
|
||||
if not fail:
|
||||
logger.info("Moving project to: {0}".format(output_directory))
|
||||
try:
|
||||
__move_package(dist_path, output_directory)
|
||||
except: # noqa: E722
|
||||
logger.error("Failed to move project")
|
||||
logger.exception(traceback.format_exc())
|
||||
else:
|
||||
logger.info("Project output will not be moved to output folder")
|
||||
return False
|
||||
|
||||
# Set complete
|
||||
return True
|
||||
45
venv3_12/Lib/site-packages/auto_py_to_exe/shims.py
Normal file
45
venv3_12/Lib/site-packages/auto_py_to_exe/shims.py
Normal file
@@ -0,0 +1,45 @@
|
||||
import sys
|
||||
|
||||
from . import utils
|
||||
|
||||
|
||||
def install_shims():
|
||||
"""Install shims to fix version incompatibility"""
|
||||
install_bottle_import_redirect_shim()
|
||||
|
||||
|
||||
def install_bottle_import_redirect_shim():
|
||||
"""
|
||||
https://github.com/brentvollebregt/auto-py-to-exe/issues/433 explains that a ModuleNotFoundError is raised when trying
|
||||
to import bottle extensions using Python 3.12.
|
||||
This shim will patch this issue with some code that is currently on bottle's main branch.
|
||||
This shim is only needed on Python versions >=3.12 and bottle versions <=0.12.25 (hoping the next version fixes this issue)
|
||||
"""
|
||||
|
||||
# First check if setting this shim is needed
|
||||
if sys.version_info < (3, 12):
|
||||
return
|
||||
|
||||
import bottle
|
||||
|
||||
if utils.parse_version_tuple(bottle.__version__) > (0, 12, 25):
|
||||
return
|
||||
|
||||
if hasattr(bottle._ImportRedirect, "find_spec"):
|
||||
return
|
||||
|
||||
print(
|
||||
f"Info: Installing shim for bottle import redirects (using Python={sys.version_info[0]}.{sys.version_info[1]}.{sys.version_info[2]} and bottle={bottle.__version__})"
|
||||
)
|
||||
|
||||
# Add the shim
|
||||
def find_spec(self, fullname, path, target=None):
|
||||
if "." not in fullname:
|
||||
return
|
||||
if fullname.rsplit(".", 1)[0] != self.name:
|
||||
return
|
||||
from importlib.util import spec_from_loader
|
||||
|
||||
return spec_from_loader(fullname, self)
|
||||
|
||||
bottle._ImportRedirect.find_spec = find_spec
|
||||
200
venv3_12/Lib/site-packages/auto_py_to_exe/ui.py
Normal file
200
venv3_12/Lib/site-packages/auto_py_to_exe/ui.py
Normal file
@@ -0,0 +1,200 @@
|
||||
import argparse
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
|
||||
import eel
|
||||
from eel import chrome
|
||||
|
||||
from . import config, dialogs, packaging, utils
|
||||
|
||||
LOGGING_HANDLER_NAME = "auto-py-to-exe logging handler"
|
||||
|
||||
|
||||
class UIOpenMode:
|
||||
NONE = 0
|
||||
CHROME = 1
|
||||
USER_DEFAULT = 2
|
||||
|
||||
|
||||
# Setup eels root folder
|
||||
eel.init(config.FRONTEND_ASSET_FOLDER)
|
||||
|
||||
|
||||
def __setup_logging_ui_forwarding():
|
||||
"""Setup forwarding of logs by PyInstaller and auto-py-to-exe to the ui"""
|
||||
|
||||
pyinstaller_logger = logging.getLogger("PyInstaller")
|
||||
# Make sure to check if the handler has already been setup so it doesn't get re-added on reload
|
||||
if not any([i.get_name() == LOGGING_HANDLER_NAME for i in pyinstaller_logger.handlers]):
|
||||
handler = logging.StreamHandler(utils.ForwardToFunctionStream(send_message_to_ui_output))
|
||||
handler.set_name(LOGGING_HANDLER_NAME)
|
||||
handler.setFormatter(logging.Formatter("%(relativeCreated)d %(levelname)s: %(message)s"))
|
||||
pyinstaller_logger.addHandler(handler)
|
||||
|
||||
module_logger = logging.getLogger("auto_py_to_exe")
|
||||
if not any([i.get_name() == LOGGING_HANDLER_NAME for i in module_logger.handlers]):
|
||||
handler = logging.StreamHandler(utils.ForwardToFunctionStream(send_message_to_ui_output))
|
||||
handler.set_name(LOGGING_HANDLER_NAME)
|
||||
handler.setFormatter(logging.Formatter("%(message)s"))
|
||||
module_logger.addHandler(handler)
|
||||
|
||||
|
||||
def __get_pyinstaller_options():
|
||||
options = packaging.get_pyinstaller_options()
|
||||
|
||||
# Filter out removed arguments (PyInstaller v6.0.0 removed some arguments but added proper handlers for people still using them - we need to ignore them)
|
||||
options = [option for option in options if option["help"] != argparse.SUPPRESS]
|
||||
|
||||
# In PyInstaller v6.0.0 --hide-console options were not set correctly (like --debug), fix them here
|
||||
for option in options:
|
||||
if isinstance(option["choices"], set):
|
||||
option["choices"] = list(option["choices"])
|
||||
|
||||
return options
|
||||
|
||||
|
||||
def __can_use_chrome():
|
||||
"""Identify if Chrome is available for Eel to use"""
|
||||
chrome_instance_path = chrome.find_path()
|
||||
return chrome_instance_path is not None and os.path.exists(chrome_instance_path)
|
||||
|
||||
|
||||
@eel.expose
|
||||
def initialise():
|
||||
"""Called by the UI when opened. Used to pass initial values and setup state we couldn't set until now."""
|
||||
__setup_logging_ui_forwarding()
|
||||
|
||||
# Pass initial values to the client
|
||||
return {
|
||||
"filename": config.package_filename,
|
||||
"suppliedUiConfiguration": config.supplied_ui_configuration,
|
||||
"options": __get_pyinstaller_options(),
|
||||
"warnings": utils.get_warnings(),
|
||||
"pathSeparator": os.pathsep,
|
||||
"defaultOutputFolder": config.default_output_directory,
|
||||
"languageHint": config.language_hint,
|
||||
}
|
||||
|
||||
|
||||
@eel.expose
|
||||
def open_output_in_explorer(output_directory, input_filename, is_one_file):
|
||||
"""Open a file in the local file explorer"""
|
||||
utils.open_output_in_explorer(output_directory, input_filename, is_one_file)
|
||||
|
||||
|
||||
@eel.expose
|
||||
def ask_file(file_type):
|
||||
"""Ask the user to select a file"""
|
||||
return dialogs.ask_file(file_type)
|
||||
|
||||
|
||||
@eel.expose
|
||||
def ask_files():
|
||||
return dialogs.ask_files()
|
||||
|
||||
|
||||
@eel.expose
|
||||
def ask_folder():
|
||||
return dialogs.ask_folder()
|
||||
|
||||
|
||||
@eel.expose
|
||||
def does_file_exist(file_path):
|
||||
"""Checks if a file exists"""
|
||||
return os.path.isfile(file_path)
|
||||
|
||||
|
||||
@eel.expose
|
||||
def does_folder_exist(path):
|
||||
"""Checks if a folder exists"""
|
||||
return os.path.isdir(path)
|
||||
|
||||
|
||||
@eel.expose
|
||||
def is_file_an_ico(file_path):
|
||||
"""Checks if a file is an ico file"""
|
||||
if not os.path.isfile(file_path):
|
||||
return None
|
||||
|
||||
# Open the file and read the first 4 bytes
|
||||
with open(file_path, "rb") as f:
|
||||
data = f.read(4)
|
||||
if data == b"\x00\x00\x01\x00":
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
@eel.expose
|
||||
def convert_path_to_absolute(path: str) -> str:
|
||||
"""Converts a path to an absolute path if it exists. If it doesn't exist, returns the path as is."""
|
||||
if not os.path.exists(path):
|
||||
return path
|
||||
return os.path.abspath(path)
|
||||
|
||||
|
||||
@eel.expose
|
||||
def import_configuration():
|
||||
"""Get configuration data from a file"""
|
||||
file_path = dialogs.ask_file("json")
|
||||
if file_path is not None:
|
||||
with open(file_path) as f:
|
||||
return json.load(f)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
@eel.expose
|
||||
def export_configuration(configuration):
|
||||
"""Write configuration data to a file"""
|
||||
file_path = dialogs.ask_file_save_location("json")
|
||||
if file_path is not None:
|
||||
with open(file_path, "w") as f:
|
||||
json.dump(configuration, f, indent=True)
|
||||
|
||||
|
||||
@eel.expose
|
||||
def will_packaging_overwrite_existing(file_path, manual_name, one_file, output_folder):
|
||||
"""Checks if there is a possibility of a previous output being overwritten"""
|
||||
return packaging.will_packaging_overwrite_existing(file_path, manual_name, one_file, output_folder)
|
||||
|
||||
|
||||
@eel.expose
|
||||
def package(command, non_pyinstaller_options):
|
||||
"""Package the script provided using the options selected by the user"""
|
||||
packaging_options = {
|
||||
"increaseRecursionLimit": non_pyinstaller_options["increaseRecursionLimit"],
|
||||
"outputDirectory": non_pyinstaller_options["outputDirectory"],
|
||||
}
|
||||
|
||||
packaging_successful = packaging.package(
|
||||
pyinstaller_command=command,
|
||||
options=packaging_options,
|
||||
)
|
||||
|
||||
send_message_to_ui_output("Complete.\n")
|
||||
eel.signalPackagingComplete(packaging_successful)()
|
||||
|
||||
|
||||
def send_message_to_ui_output(message):
|
||||
"""Show a message in the ui output"""
|
||||
eel.putMessageInOutput(message)()
|
||||
|
||||
|
||||
def start(open_mode):
|
||||
"""Start the UI using Eel"""
|
||||
try:
|
||||
chrome_available = __can_use_chrome()
|
||||
if open_mode == UIOpenMode.CHROME and chrome_available:
|
||||
eel.start("index.html", size=(650, 672), port=0)
|
||||
elif open_mode == UIOpenMode.USER_DEFAULT or (open_mode == UIOpenMode.CHROME and not chrome_available):
|
||||
eel.start("index.html", size=(650, 672), port=0, mode="user default")
|
||||
else:
|
||||
port = utils.get_port()
|
||||
print("Server starting at http://localhost:" + str(port) + "/index.html")
|
||||
eel.start(
|
||||
"index.html", size=(650, 672), host="localhost", port=port, mode=None, close_callback=lambda x, y: None
|
||||
)
|
||||
except (SystemExit, KeyboardInterrupt):
|
||||
pass # This is what the bottle server raises
|
||||
193
venv3_12/Lib/site-packages/auto_py_to_exe/utils.py
Normal file
193
venv3_12/Lib/site-packages/auto_py_to_exe/utils.py
Normal file
@@ -0,0 +1,193 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import io
|
||||
import os
|
||||
import platform
|
||||
import socket
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import requests
|
||||
from PyInstaller import __version__ as pyinstaller_version_string
|
||||
|
||||
from . import __version__
|
||||
|
||||
|
||||
class ForwardToFunctionStream(io.TextIOBase):
|
||||
def __init__(self, output_function=print):
|
||||
self.output_function = output_function
|
||||
|
||||
def write(self, string):
|
||||
self.output_function(string)
|
||||
return len(string)
|
||||
|
||||
|
||||
def open_output_in_explorer(output_directory, input_filename, is_one_file):
|
||||
"""Open the output in the local file explorer"""
|
||||
folder_directory = os.path.abspath(output_directory)
|
||||
if platform.system() == "Windows":
|
||||
# For windows, we can highlight the file in the explorer window
|
||||
target_base_to_open = Path(input_filename).stem + (".exe" if is_one_file else "")
|
||||
target_path_to_open = Path(folder_directory) / target_base_to_open
|
||||
if target_path_to_open.exists():
|
||||
os.popen(f'explorer /select,"{target_path_to_open}"')
|
||||
else:
|
||||
# If the file doesn't exist, just open the folder)
|
||||
os.startfile(folder_directory, "explore")
|
||||
elif platform.system() == "Linux":
|
||||
os.system('xdg-open "' + folder_directory + '"')
|
||||
elif platform.system() == "Darwin":
|
||||
os.system('open "' + folder_directory + '"')
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class PackageVersion:
|
||||
def __init__(self, version_string, url) -> None:
|
||||
self.version_string = version_string
|
||||
self.version = parse_version_tuple(version_string)
|
||||
self.url = url
|
||||
|
||||
|
||||
def __get_latest_version_for_library(library_repo):
|
||||
try:
|
||||
response = requests.get(f"https://api.github.com/repos/{library_repo}/releases/latest")
|
||||
response.raise_for_status()
|
||||
response_data = response.json()
|
||||
latest_release_tag_name = response_data["tag_name"].strip("v")
|
||||
return PackageVersion(latest_release_tag_name.strip("v"), response_data["html_url"])
|
||||
except requests.exceptions.RequestException:
|
||||
return None
|
||||
|
||||
|
||||
def __get_latest_auto_py_to_exe_version():
|
||||
"""Get the latest version of auto-py-to-exe"""
|
||||
return __get_latest_version_for_library("brentvollebregt/auto-py-to-exe")
|
||||
|
||||
|
||||
def __get_latest_pyinstaller_version():
|
||||
"""Get the latest version of PyInstaller"""
|
||||
return __get_latest_version_for_library("pyinstaller/pyinstaller")
|
||||
|
||||
|
||||
def get_warnings():
|
||||
warnings = []
|
||||
|
||||
# Check auto-py-to-exe version is it latest
|
||||
try:
|
||||
current_auto_py_to_exe_version = parse_version_tuple(__version__)
|
||||
latest_auto_py_to_exe_version = __get_latest_auto_py_to_exe_version()
|
||||
if latest_auto_py_to_exe_version is None:
|
||||
raise Exception("Unable to check for the latest version of auto-py-to-exe.")
|
||||
elif latest_auto_py_to_exe_version.version > current_auto_py_to_exe_version:
|
||||
message = f'<a href="{latest_auto_py_to_exe_version.url}" target="_blank">A new version of auto-py-to-exe has been released</a>: {__version__} → {latest_auto_py_to_exe_version.version_string}'
|
||||
message += "\nUpgrade using: python -m pip install auto-py-to-exe --upgrade"
|
||||
warnings.append(message)
|
||||
except Exception as e:
|
||||
message = f"\nWarning: {e}"
|
||||
warnings.append(message)
|
||||
|
||||
# Check PyInstaller version is it latest
|
||||
try:
|
||||
current_pyinstaller_version = parse_version_tuple(pyinstaller_version_string)
|
||||
latest_pyinstaller_version = __get_latest_pyinstaller_version()
|
||||
if latest_pyinstaller_version is None:
|
||||
raise Exception("Unable to check for the latest version of PyInstaller.")
|
||||
elif latest_pyinstaller_version.version > current_pyinstaller_version:
|
||||
message = f'<a href="{latest_pyinstaller_version.url}" target="_blank">A new version of PyInstaller has been released</a>: {pyinstaller_version_string} → {latest_pyinstaller_version.version_string}'
|
||||
message += "\nUpgrade using: python -m pip install pyinstaller --upgrade"
|
||||
warnings.append(message)
|
||||
except VersionParseError:
|
||||
pass # Don't warn about a new version when using a non-official release
|
||||
except Exception as e:
|
||||
message = f"\nWarning: {e}"
|
||||
warnings.append(message)
|
||||
|
||||
try:
|
||||
pyinstaller_version = parse_version_tuple(pyinstaller_version_string)
|
||||
except ValueError:
|
||||
message = "Unable to parse PyInstaller version - this may be because you aren't using an official release."
|
||||
message += "\nYou are currently using PyInstaller {pyinstaller_version}.".format(
|
||||
pyinstaller_version=pyinstaller_version_string
|
||||
)
|
||||
message += "\nIf this is an official release, please report this issue on GitHub."
|
||||
warnings.append(message)
|
||||
return warnings
|
||||
|
||||
# Make sure PyInstaller 3.4 or above is being used with Python 3.7
|
||||
try:
|
||||
if sys.version_info >= (3, 7) and pyinstaller_version < (3, 4):
|
||||
message = "You will need PyInstaller 3.4 or above to use this tool with Python 3.7."
|
||||
message += "\nYou are currently using PyInstaller {pyinstaller_version}.".format(
|
||||
pyinstaller_version=pyinstaller_version_string
|
||||
)
|
||||
message += "\nPlease upgrade PyInstaller: python -m pip install pyinstaller --upgrade"
|
||||
warnings.append(message)
|
||||
except ValueError:
|
||||
pass # Dev branches will have pyinstaller_version as a string in the form X.Y.devZ+HASH. Ignore it if this is the case.
|
||||
|
||||
# Make sure PyInstaller 4.0 or above is being used with Python 3.8 and 3.9
|
||||
try:
|
||||
if (
|
||||
sys.version_info.major == 3
|
||||
and (sys.version_info.minor == 8 or sys.version_info.minor == 9)
|
||||
and pyinstaller_version < (4, 1)
|
||||
):
|
||||
message = "PyInstaller 4.0 and below does not officially support Python 3.8 and 3.9."
|
||||
message += "\nYou are currently using PyInstaller {pyinstaller_version}.".format(
|
||||
pyinstaller_version=pyinstaller_version_string
|
||||
)
|
||||
message += "\nIt is highly recommended to update your version of PyInstaller using: python -m pip install pyinstaller --upgrade"
|
||||
warnings.append(message)
|
||||
except ValueError:
|
||||
pass # Dev branches will have pyinstaller_version as a string in the form X.Y.devZ+HASH. Ignore it if this is the case.
|
||||
|
||||
# Make sure PyInstaller 4.6 or above is being used with Python 3.10
|
||||
try:
|
||||
if sys.version_info.major == 3 and sys.version_info.minor == 10 and pyinstaller_version < (4, 6):
|
||||
message = "You will need PyInstaller 4.6 or above to use this tool with Python 3.10."
|
||||
message += "\nYou are currently using PyInstaller {pyinstaller_version}.".format(
|
||||
pyinstaller_version=pyinstaller_version_string
|
||||
)
|
||||
message += "\nPlease upgrade PyInstaller: python -m pip install pyinstaller --upgrade"
|
||||
warnings.append(message)
|
||||
except ValueError:
|
||||
pass # Dev branches will have pyinstaller_version as a string in the form X.Y.devZ+HASH. Ignore it if this is the case.
|
||||
|
||||
# If Python 3.10.0 is being used, we are probably going to see `IndexError: tuple index out of range`.
|
||||
if sys.version_info.major == 3 and sys.version_info.minor == 10 and sys.version_info.micro == 0:
|
||||
message = 'You are using Python 3.10.0. <a href="https://github.com/brentvollebregt/auto-py-to-exe/issues/215" target="_blank">This version of Python has a bug that causes PyInstaller to fail.</a>'
|
||||
message += "\nPlease upgrade to Python 3.10.1 or above."
|
||||
warnings.append(message)
|
||||
|
||||
# Make sure we are not using Python from the Windows Store
|
||||
if r"Packages\PythonSoftwareFoundation.Python." in sys.executable:
|
||||
message = "It looks like you may be using Python from the Windows Store, the Python binary you are currently using is at: "
|
||||
message += '"' + sys.executable + '"'
|
||||
message += "\n\nPython from the Windows Store is not supported by PyInstaller so you may get errors referencing \"win32ctypes.pywin32.pywintypes.error: (1920, 'LoadLibraryEx', 'The file cannot be accessed by the system'\"."
|
||||
message += '\n<a href="https://github.com/brentvollebregt/auto-py-to-exe/issues/166#issuecomment-827492005" target="_blank">To fix this, use a distribution of Python from python.org.</a>'
|
||||
warnings.append(message)
|
||||
|
||||
return warnings
|
||||
|
||||
|
||||
def get_port():
|
||||
"""Get an available port by starting a new server, stopping and and returning the port"""
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.bind(("localhost", 0))
|
||||
port = sock.getsockname()[1]
|
||||
sock.close()
|
||||
return port
|
||||
|
||||
|
||||
class VersionParseError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def parse_version_tuple(version_string):
|
||||
"""Turn a version string into a tuple of integers e.g. "1.2.3" -> (1, 2, 3)"""
|
||||
try:
|
||||
return tuple(map(int, (version_string.split("."))))
|
||||
except ValueError:
|
||||
raise VersionParseError()
|
||||
35
venv3_12/Lib/site-packages/auto_py_to_exe/validation.py
Normal file
35
venv3_12/Lib/site-packages/auto_py_to_exe/validation.py
Normal file
@@ -0,0 +1,35 @@
|
||||
import argparse
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
|
||||
|
||||
def argparse_file_exists(file_path):
|
||||
"""Validates whether a file exists."""
|
||||
if not os.path.isfile(file_path):
|
||||
raise argparse.ArgumentTypeError("File does not exist")
|
||||
|
||||
return os.path.abspath(file_path)
|
||||
|
||||
|
||||
def argparse_file_json(file_path):
|
||||
"""Validates whether a file contains JSON parsable by Python. Raises argparse.ArgumentTypeError if not."""
|
||||
if not os.path.isfile(file_path):
|
||||
raise argparse.ArgumentTypeError("Provided configuration file does not exist")
|
||||
|
||||
try:
|
||||
with open(file_path, "r") as file:
|
||||
data = json.load(file)
|
||||
except json.decoder.JSONDecodeError:
|
||||
raise argparse.ArgumentTypeError("Provided configuration file content is not json")
|
||||
except Exception as e:
|
||||
raise argparse.ArgumentTypeError("Cannot parse provided configuration file:\n" + str(e))
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def argparse_logging_level(level):
|
||||
"""Validates that a string value is a valid logging level and returns the corresponding value."""
|
||||
if hasattr(logging, level.upper()):
|
||||
return level.upper()
|
||||
raise argparse.ArgumentTypeError("Invalid logging level: " + str(level))
|
||||
BIN
venv3_12/Lib/site-packages/auto_py_to_exe/web/Nunito-Light.ttf
Normal file
BIN
venv3_12/Lib/site-packages/auto_py_to_exe/web/Nunito-Light.ttf
Normal file
Binary file not shown.
Binary file not shown.
184
venv3_12/Lib/site-packages/auto_py_to_exe/web/css/general.css
Normal file
184
venv3_12/Lib/site-packages/auto_py_to_exe/web/css/general.css
Normal file
@@ -0,0 +1,184 @@
|
||||
@font-face {
|
||||
font-family: 'Nunito';
|
||||
src: url('../Nunito-Light.ttf');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Vazirmatn';
|
||||
src: url('../Vazirmatn-Light.ttf');
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
unicode-range: U+0600-06FF, U+0750-077F, U+08A0-08FF, U+FB50-FDFF, U+FE70-FEFF;
|
||||
}
|
||||
|
||||
:root {
|
||||
--background: #fbfbfb;
|
||||
--primary: #458bc6;
|
||||
--primary-darker: #1c79c7;
|
||||
--primary-transparent: #1c79c71a;
|
||||
--error: red;
|
||||
--unselected: lightgrey;
|
||||
--disabled: #808080;
|
||||
--text: #000000;
|
||||
|
||||
--title: #666666;
|
||||
--warning-background: #fff3cd;
|
||||
--warning-border: #a0987c;
|
||||
--warning-text: #000000;
|
||||
|
||||
--border-radius: 4px;
|
||||
}
|
||||
|
||||
.dark-theme {
|
||||
--background: #15131e;
|
||||
--unselected: #5f5f5f;
|
||||
--text: #ffffff;
|
||||
|
||||
--title: #e2e2e2;
|
||||
--warning-background: #ffdc6d;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--background);
|
||||
font-family: 'Vazirmatn', 'Nunito', Helvetica, Arial, sans-serif;
|
||||
font-weight: 100;
|
||||
margin: 0 18px 10px 18px;
|
||||
}
|
||||
|
||||
.mid {
|
||||
/* Global center alignment */
|
||||
margin: auto;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
/* Headers */
|
||||
|
||||
h2 {
|
||||
font-weight: normal;
|
||||
font-size: 25px;
|
||||
margin: 10px 0 2px 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 17px;
|
||||
margin: 10px 0 4px 0;
|
||||
}
|
||||
|
||||
.sub_header {
|
||||
/* Headers in tabs */
|
||||
font-size: 17px;
|
||||
margin: 10px 2px 4px 2px;
|
||||
}
|
||||
|
||||
h2 > small {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* Generic inputs */
|
||||
|
||||
button,
|
||||
input,
|
||||
select {
|
||||
border: 1px solid var(--primary);
|
||||
border-radius: var(--border-radius);
|
||||
padding: 4px;
|
||||
|
||||
font-family: 'Vazirmatn', 'Nunito', Helvetica, Arial, sans-serif;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
input,
|
||||
select,
|
||||
textarea,
|
||||
select option {
|
||||
background-color: var(--background);
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline: none; /* Don't show outline so you can see the colour change */
|
||||
}
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
border-radius: var(--border-radius);
|
||||
background: transparent;
|
||||
padding: 3px 8px;
|
||||
transition: border 0.3s, background 0.3s;
|
||||
border-style: solid;
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
button:not(.selected):not(.unselected):hover {
|
||||
/* Apply hovers to non-state buttons */
|
||||
background: var(--primary-transparent);
|
||||
}
|
||||
|
||||
button.selected,
|
||||
button.unselected:hover {
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
button.unselected {
|
||||
border-color: var(--unselected);
|
||||
color: var(--unselected);
|
||||
}
|
||||
|
||||
button.large {
|
||||
border-width: 3px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
/* Info icon */
|
||||
|
||||
.info_icon {
|
||||
/* Information icon */
|
||||
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAq0lEQVR4XnWQywrDIBBF82P+WPtXebiSbJKtq0jAtVAEwW0WIZ16p04aWjpwcbge5tU0Nfq+V13XteV9QDVX8i/Qrei5LAtt28ZCXuADf9dKxzzPhEgpsRDwKqwatCiiaZrIe09aazLGMOicAwi1qIh5xGAw50z7vtM4juyB+QFDCFzNWnt6DEpr0bquPMIwDKcnrXkZMWOMrAv0XqZufhf4C/ycR6JW/nvwF51vE+IcomqCAAAAAElFTkSuQmCC) -0px -0px
|
||||
no-repeat;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
overflow: hidden;
|
||||
margin-left: 0.25em;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Small notes */
|
||||
|
||||
.note {
|
||||
font-size: 14px;
|
||||
font-style: italic;
|
||||
margin: 8px 0 0 0;
|
||||
}
|
||||
|
||||
/* Filepath-browse layout */
|
||||
|
||||
.filepath-browse-layout {
|
||||
display: grid;
|
||||
grid-gap: 4px;
|
||||
grid-template-columns: 1fr 120px;
|
||||
}
|
||||
|
||||
/* Icon-specific */
|
||||
|
||||
#icon-invalid-warning {
|
||||
font-size: 14px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
/* Utils */
|
||||
|
||||
.noselect {
|
||||
/* Don't select tab text */
|
||||
-webkit-touch-callout: none; /* iOS Safari */
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-khtml-user-select: none; /* Konqueror HTML */
|
||||
-moz-user-select: none; /* Firefox */
|
||||
-ms-user-select: none; /* Internet Explorer/Edge */
|
||||
user-select: none;
|
||||
}
|
||||
363
venv3_12/Lib/site-packages/auto_py_to_exe/web/css/main.css
Normal file
363
venv3_12/Lib/site-packages/auto_py_to_exe/web/css/main.css
Normal file
@@ -0,0 +1,363 @@
|
||||
/* Header */
|
||||
|
||||
#header {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
#header .title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#header .title img {
|
||||
height: 41px;
|
||||
}
|
||||
|
||||
#header .title h1 {
|
||||
font-weight: 100;
|
||||
font-size: 30px;
|
||||
color: var(--title);
|
||||
margin: 0 0 0 10px;
|
||||
}
|
||||
|
||||
#header .title > a {
|
||||
display: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#header .title > a:hover {
|
||||
-webkit-mask-image: linear-gradient(-75deg, rgb(0, 0, 0) 30%, rgba(0, 0, 0, 0.5) 50%, rgb(0, 0, 0) 70%);
|
||||
-webkit-mask-size: 200%;
|
||||
animation: shine 2s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
@-webkit-keyframes shine {
|
||||
from {
|
||||
-webkit-mask-position: 150%;
|
||||
}
|
||||
to {
|
||||
-webkit-mask-position: -50%;
|
||||
}
|
||||
}
|
||||
|
||||
#header .extra-links {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#header .extra-links a {
|
||||
filter: grayscale(1);
|
||||
transition: filter 0.3s;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#header .extra-links a span {
|
||||
font-size: 15px;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
#header .extra-links a:hover {
|
||||
filter: grayscale(0);
|
||||
}
|
||||
|
||||
#header .extra-links a img {
|
||||
height: 20px;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
#header .extra-links a:not(:first-child) img {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
#header .ui-config {
|
||||
margin-top: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
#header .ui-config [for='language-selection'] {
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
#header .ui-config #language-selection {
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
#header .ui-config #theme-toggle {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
display: flex;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
#language-selection {
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
/* Warnings */
|
||||
|
||||
#warnings {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
#warnings > div {
|
||||
background: var(--warning-background);
|
||||
border: 1px solid var(--warning-border);
|
||||
border-radius: var(--border-radius);
|
||||
margin: 10px 0;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
#warnings > div > p {
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
#warnings > div > p,
|
||||
#warnings > div > p a {
|
||||
color: var(--warning-text);
|
||||
}
|
||||
|
||||
/* Sections */
|
||||
|
||||
div[id*='section'] {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
div[id*='section'] .header {
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div[id*='section'] .header img {
|
||||
height: 30px;
|
||||
transition: transform 0.4s;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
div[id*='section'] .header h2 {
|
||||
display: inline;
|
||||
margin: 0 0 0 15px;
|
||||
}
|
||||
|
||||
div[id*='section'] .content {
|
||||
display: none; /* Hide sections by default */
|
||||
margin: 5px 10px 0 10px;
|
||||
}
|
||||
|
||||
/* Additional Files */
|
||||
|
||||
#datas-add-buttons {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
grid-gap: 5px;
|
||||
}
|
||||
|
||||
/* Advanced tab */
|
||||
|
||||
.option-container {
|
||||
/* Option containers */
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.option-container > span {
|
||||
/* Option name */
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.option-container.multiple-input > img {
|
||||
height: 23px;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.option-container.multiple-input > div {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.option-container.multiple-input > div > div,
|
||||
#datas-list > div {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
grid-gap: 4px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.option-container.multiple-input > div > div.dual-value,
|
||||
#datas-list > div {
|
||||
grid-template-columns: 1fr 1fr auto;
|
||||
}
|
||||
|
||||
.option-container.multiple-input > div > div > img,
|
||||
#datas-list > div > img {
|
||||
height: 100%;
|
||||
padding: 2px 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.option-container.input {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
grid-gap: 5px;
|
||||
}
|
||||
|
||||
.option-container.input.with-browse {
|
||||
grid-template-columns: auto 1fr auto;
|
||||
}
|
||||
|
||||
/* Current Command */
|
||||
|
||||
#current-command textarea {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Output */
|
||||
|
||||
#output {
|
||||
display: none; /* Hidden by default */
|
||||
}
|
||||
|
||||
#output.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#output textarea {
|
||||
width: 100%;
|
||||
white-space: pre;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
#output textarea.failure {
|
||||
border-color: var(--error);
|
||||
}
|
||||
|
||||
/* Common issues link formatting */
|
||||
|
||||
#common-issue-link {
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
margin-bottom: 5px;
|
||||
display: none; /* Default to not shown */
|
||||
}
|
||||
|
||||
#common-issue-link.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#common-issue-link a {
|
||||
text-decoration: none;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
#common-issue-link a:hover {
|
||||
color: var(--primary-darker);
|
||||
}
|
||||
|
||||
/* Package / Open Output Folder Buttons */
|
||||
|
||||
#package-button-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
#package-button,
|
||||
#open-output-folder-button {
|
||||
width: 100%;
|
||||
background-color: var(--primary);
|
||||
border: 1px solid var(--primary);
|
||||
font-size: 15px;
|
||||
color: white;
|
||||
height: 38px;
|
||||
padding: 0 30px;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
letter-spacing: 0.1rem;
|
||||
text-transform: uppercase;
|
||||
white-space: nowrap;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
#package-button:hover,
|
||||
#open-output-folder-button:hover {
|
||||
background-color: var(--primary-darker);
|
||||
}
|
||||
|
||||
#package-button:disabled {
|
||||
background-color: var(--disabled);
|
||||
border-color: var(--disabled);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
#open-output-folder-button {
|
||||
display: none; /* Default to not shown */
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
#open-output-folder-button.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Loading spinner (from https://projects.lukehaas.me/css-loaders/) */
|
||||
|
||||
.loading-spinner-wrapper {
|
||||
display: flex;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgb(0 0 0 / 75%);
|
||||
}
|
||||
|
||||
.loading-label {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.loading-spinner,
|
||||
.loading-spinner:after {
|
||||
border-radius: 50%;
|
||||
width: 8em;
|
||||
height: 8em;
|
||||
}
|
||||
.loading-spinner {
|
||||
margin: 60px auto;
|
||||
font-size: 10px;
|
||||
position: relative;
|
||||
text-indent: -9999em;
|
||||
border-top: 1.1em solid rgba(256, 256, 256, 0.2);
|
||||
border-right: 1.1em solid rgba(256, 256, 256, 0.2);
|
||||
border-bottom: 1.1em solid rgba(256, 256, 256, 0.2);
|
||||
border-left: 1.1em solid #ffffff;
|
||||
-webkit-transform: translateZ(0);
|
||||
-ms-transform: translateZ(0);
|
||||
transform: translateZ(0);
|
||||
-webkit-animation: load8 1.1s infinite linear;
|
||||
animation: load8 1.1s infinite linear;
|
||||
}
|
||||
@-webkit-keyframes load8 {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@keyframes load8 {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
62
venv3_12/Lib/site-packages/auto_py_to_exe/web/css/modal.css
Normal file
62
venv3_12/Lib/site-packages/auto_py_to_exe/web/css/modal.css
Normal file
@@ -0,0 +1,62 @@
|
||||
:root {
|
||||
--modal-offset: 0;
|
||||
--modal-padding: 200px;
|
||||
--modal-fallback-color: rgba(0, 0, 0, 0.4);
|
||||
--modal-coverage-area: 100%;
|
||||
}
|
||||
|
||||
.modal-coverage {
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
padding-top: var(--modal-padding);
|
||||
left: var(--modal-offset);
|
||||
top: var(--modal-offset);
|
||||
width: var(--modal-coverage-area);
|
||||
height: var(--modal-coverage-area);
|
||||
overflow: auto;
|
||||
background-color: var(--modal-fallback-color);
|
||||
}
|
||||
|
||||
.modal-coverage-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background-color: var(--background);
|
||||
margin: auto;
|
||||
padding: 16px;
|
||||
border: 2px solid var(--primary);
|
||||
border-radius: var(--border-radius);
|
||||
width: 80%;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
color: var(--primary);
|
||||
float: right;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.close-btn:hover,
|
||||
.close-btn:focus {
|
||||
color: var(--primary-darker);
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.modal-section {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.modal-header h2 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.modal-btn {
|
||||
margin-right: 4px;
|
||||
}
|
||||
BIN
venv3_12/Lib/site-packages/auto_py_to_exe/web/favicon.ico
Normal file
BIN
venv3_12/Lib/site-packages/auto_py_to_exe/web/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 163 KiB |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 448 512"
|
||||
style="color: #458BC6;">
|
||||
<path fill="currentColor"
|
||||
d="M232.5 163.5l122.8 122.8c4.7 4.7 4.7 12.3 0 17l-22.6 22.6c-4.7 4.7-12.3 4.7-17 0L224 234.2l-91.7 91.7c-4.7 4.7-12.3 4.7-17 0l-22.6-22.6c-4.7-4.7-4.7-12.3 0-17l122.8-122.8c4.7-4.7 12.3-4.7 17 0zM448 80v352c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V80c0-26.5 21.5-48 48-48h352c26.5 0 48 21.5 48 48zm-48 346V86c0-3.3-2.7-6-6-6H54c-3.3 0-6 2.7-6 6v340c0 3.3 2.7 6 6 6h340c3.3 0 6-2.7 6-6z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 540 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 512 512"><path fill="currentColor" d="M283.211 512c78.962 0 151.079-35.925 198.857-94.792 7.068-8.708-.639-21.43-11.562-19.35-124.203 23.654-238.262-71.576-238.262-196.954 0-72.222 38.662-138.635 101.498-174.394 9.686-5.512 7.25-20.197-3.756-22.23A258.156 258.156 0 0 0 283.211 0c-141.309 0-256 114.511-256 256 0 141.309 114.511 256 256 256z"/></svg>
|
||||
|
After Width: | Height: | Size: 416 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 448 512"
|
||||
style="color: green;">
|
||||
<path fill="currentColor"
|
||||
d="M352 240v32c0 6.6-5.4 12-12 12h-88v88c0 6.6-5.4 12-12 12h-32c-6.6 0-12-5.4-12-12v-88h-88c-6.6 0-12-5.4-12-12v-32c0-6.6 5.4-12 12-12h88v-88c0-6.6 5.4-12 12-12h32c6.6 0 12 5.4 12 12v88h88c6.6 0 12 5.4 12 12zm96-160v352c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V80c0-26.5 21.5-48 48-48h352c26.5 0 48 21.5 48 48zm-48 346V86c0-3.3-2.7-6-6-6H54c-3.3 0-6 2.7-6 6v340c0 3.3 2.7 6 6 6h340c3.3 0 6-2.7 6-6z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 549 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 448 512"
|
||||
style="color: red;">
|
||||
<path fill="currentColor"
|
||||
d="M108 284c-6.6 0-12-5.4-12-12v-32c0-6.6 5.4-12 12-12h232c6.6 0 12 5.4 12 12v32c0 6.6-5.4 12-12 12H108zM448 80v352c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V80c0-26.5 21.5-48 48-48h352c26.5 0 48 21.5 48 48zm-48 346V86c0-3.3-2.7-6-6-6H54c-3.3 0-6 2.7-6 6v340c0 3.3 2.7 6 6 6h340c3.3 0 6-2.7 6-6z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 444 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 512 512" style="color: white;"><path fill="currentColor" d="M256 160c-52.9 0-96 43.1-96 96s43.1 96 96 96 96-43.1 96-96-43.1-96-96-96zm246.4 80.5l-94.7-47.3 33.5-100.4c4.5-13.6-8.4-26.5-21.9-21.9l-100.4 33.5-47.4-94.8c-6.4-12.8-24.6-12.8-31 0l-47.3 94.7L92.7 70.8c-13.6-4.5-26.5 8.4-21.9 21.9l33.5 100.4-94.7 47.4c-12.8 6.4-12.8 24.6 0 31l94.7 47.3-33.5 100.5c-4.5 13.6 8.4 26.5 21.9 21.9l100.4-33.5 47.3 94.7c6.4 12.8 24.6 12.8 31 0l47.3-94.7 100.4 33.5c13.6 4.5 26.5-8.4 21.9-21.9l-33.5-100.4 94.7-47.3c13-6.5 13-24.7.2-31.1zm-155.9 106c-49.9 49.9-131.1 49.9-181 0-49.9-49.9-49.9-131.1 0-181 49.9-49.9 131.1-49.9 181 0 49.9 49.9 49.9 131.1 0 181z"/></svg>
|
||||
|
After Width: | Height: | Size: 722 B |
280
venv3_12/Lib/site-packages/auto_py_to_exe/web/index.html
Normal file
280
venv3_12/Lib/site-packages/auto_py_to_exe/web/index.html
Normal file
@@ -0,0 +1,280 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Auto Py To Exe</title>
|
||||
<script>
|
||||
// Provided for type checking
|
||||
window.eel = {
|
||||
initialise: () => ({
|
||||
filename: null,
|
||||
options: [],
|
||||
suppliedUiConfiguration: {},
|
||||
warnings: [],
|
||||
pathSeparator: '',
|
||||
defaultOutputFolder: '',
|
||||
languageHint: '',
|
||||
}),
|
||||
does_file_exist: (path) => false,
|
||||
does_folder_exist: (path) => false,
|
||||
ask_file: (file_type) => '',
|
||||
ask_files: () => [],
|
||||
ask_folder: () => '',
|
||||
is_file_an_ico: (file_path) => null,
|
||||
convert_path_to_absolute: (path) => '',
|
||||
open_output_in_explorer: (output_directory, input_filename, is_one_file) => {},
|
||||
will_packaging_overwrite_existing: (file_path, manual_name, one_file, output_folder) => true,
|
||||
package: (command, non_pyinstaller_options) => {},
|
||||
import_configuration: () => {},
|
||||
export_configuration: (configuration) => {},
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="/eel.js"></script>
|
||||
<script type="text/javascript" src="/js/constants.js"></script>
|
||||
<script type="text/javascript" src="/js/i18n.js"></script>
|
||||
<script type="text/javascript" src="/js/initialise.js"></script>
|
||||
<script type="text/javascript" src="/js/configuration.js"></script>
|
||||
<script type="text/javascript" src="/js/staticEvents.js"></script>
|
||||
<script type="text/javascript" src="/js/interface.js"></script>
|
||||
<script type="text/javascript" src="/js/utils.js"></script>
|
||||
<script type="text/javascript" src="/js/modal.js"></script>
|
||||
<script type="text/javascript" src="/js/messages.js"></script>
|
||||
<script type="text/javascript" src="/js/packaging.js"></script>
|
||||
<script type="text/javascript" src="/js/importExport.js"></script>
|
||||
<link rel="stylesheet" href="/css/general.css" />
|
||||
<link rel="stylesheet" href="/css/main.css" />
|
||||
<link rel="stylesheet" href="/css/modal.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="mid">
|
||||
<div id="header">
|
||||
<div class="title">
|
||||
<a href="https://github.com/brentvollebregt/auto-py-to-exe" target="_blank"><img src="/favicon.ico" /></a>
|
||||
<a href="https://github.com/brentvollebregt/auto-py-to-exe" target="_blank"><h1>Auto Py to Exe</h1></a>
|
||||
</div>
|
||||
<div>
|
||||
<div class="extra-links">
|
||||
<a href="https://github.com/brentvollebregt/auto-py-to-exe" target="_blank">
|
||||
<span>GitHub</span>
|
||||
<img src="https://github.githubassets.com/favicons/favicon.png" alt="GitHub favicon" />
|
||||
</a>
|
||||
<a
|
||||
href="https://nitratine.net/blog/post/issues-when-using-auto-py-to-exe/?utm_source=auto_py_to_exe&utm_medium=application_link&utm_campaign=auto_py_to_exe_help&utm_content=top"
|
||||
target="_blank"
|
||||
>
|
||||
<span data-i18n="ui.links.helpPost">Help Post</span>
|
||||
<img src="https://nitratine.net/static/img/favicon-384x384.png" alt="Nitratine favicon" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="ui-config">
|
||||
<label for="language-selection">
|
||||
<small data-i18n="ui.title.language">Language</small><small>:</small>
|
||||
</label>
|
||||
<select id="language-selection"></select>
|
||||
|
||||
<span id="theme-toggle">
|
||||
<img src="img/sun.svg" id="on-dark-theme-button" style="display: none" />
|
||||
<img src="img/moon.svg" id="on-light-theme-button" style="display: inline" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="warnings"></div>
|
||||
|
||||
<div>
|
||||
<h2 data-i18n="ui.title.scriptLocation">Script Location</h2>
|
||||
<div class="filepath-browse-layout">
|
||||
<input
|
||||
id="entry-script"
|
||||
placeholder="Path to file"
|
||||
required
|
||||
data-i18n_placeholder="ui.placeholders.pathToFile"
|
||||
/>
|
||||
<button id="entry-script-search" data-i18n="ui.button.browse">Browse</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2>
|
||||
<span data-i18n="ui.title.oneFile">Onefile</span>
|
||||
<small>(--onedir / --onefile)</small>
|
||||
</h2>
|
||||
<div>
|
||||
<button id="one-directory-button" class="large" data-i18n="ui.button.oneDirectory">One Directory</button>
|
||||
<button id="one-file-button" class="large" data-i18n="ui.button.oneFile">One File</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2>
|
||||
<span data-i18n="ui.title.consoleWindow">Console Window</span>
|
||||
<small>(--console / --windowed)</small>
|
||||
</h2>
|
||||
<div>
|
||||
<button id="console-based-button" class="large" data-i18n="ui.button.consoleBased">Console Based</button>
|
||||
<button id="window-based-button" class="large" data-i18n="ui.button.windowBased">
|
||||
Window Based (hide the console)
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="section-icon">
|
||||
<div class="header noselect" onclick="expandSection('icon')">
|
||||
<img src="img/chevron-square-up.svg" alt="Icon Section Chevron" />
|
||||
<h2>
|
||||
<span data-i18n="ui.title.icon">Icon</span>
|
||||
<small>(--icon)</small>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="filepath-browse-layout">
|
||||
<input id="icon-path" placeholder=".ico file" data-i18n_placeholder="ui.placeholders.icoFile" />
|
||||
<button id="icon-path-search" data-i18n="ui.button.browse">Browse</button>
|
||||
</div>
|
||||
<div>
|
||||
<span id="icon-invalid-warning" style="display: none">
|
||||
⚠️
|
||||
<span data-i18n="ui.notes.invalidIcoFormatWarning">Warning: this file is not a valid .ico file</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="section-additional-files">
|
||||
<div class="header noselect" onclick="expandSection('additional-files')">
|
||||
<img src="img/chevron-square-up.svg" alt="Additional Files Section Chevron" />
|
||||
<h2>
|
||||
<span data-i18n="ui.title.additionalFiles">Additional Files</span>
|
||||
<small>(--add-data)</small>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div id="datas-add-buttons">
|
||||
<button id="additional-files-add-files-button" data-i18n="ui.button.addFiles">Add Files</button>
|
||||
<button id="additional-files-add-folder" data-i18n="ui.button.addFolder">Add Folder</button>
|
||||
<button id="additional-files-add-blank" data-i18n="ui.button.addBlank">Add Blank</button>
|
||||
</div>
|
||||
<div id="datas-list"></div>
|
||||
<p
|
||||
id="onefileAdditionalFilesNote"
|
||||
class="note"
|
||||
style="display: none"
|
||||
data-i18n="ui.notes.oneFileAdditionalFilesNote"
|
||||
>
|
||||
Be careful when using additional files with onefile mode;
|
||||
<a href="https://stackoverflow.com/a/13790741/" style="text-decoration: none">read this</a>
|
||||
and update your code to work with PyInstaller.
|
||||
</p>
|
||||
<p class="note" data-i18n="ui.notes.rootDirectory">
|
||||
If you want to put files in the root directory, put a period (.) in the destination.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="section-advanced">
|
||||
<div class="header noselect" onclick="expandSection('advanced')">
|
||||
<img src="img/chevron-square-up.svg" alt="Advanced Section Chevron" />
|
||||
<h2 data-i18n="ui.title.advanced">Advanced</h2>
|
||||
</div>
|
||||
<div class="content"></div>
|
||||
</div>
|
||||
|
||||
<div id="section-settings">
|
||||
<div class="header noselect" onclick="expandSection('settings')">
|
||||
<img src="img/chevron-square-up.svg" alt="Advanced Section Chevron" />
|
||||
<h2 data-i18n="ui.title.settings">Settings</h2>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div>
|
||||
<h3 data-i18n="ui.title.specificOptions">auto-py-to-exe Specific Options</h3>
|
||||
<div class="option-container input">
|
||||
<span>
|
||||
<span data-i18n="ui.title.outputDirectory">Output Directory</span>
|
||||
<span
|
||||
title="The directory to put the output in. Will be created if it doesn't exist"
|
||||
class="info_icon"
|
||||
data-i18n_title="ui.helpText.outputDirectory"
|
||||
></span>
|
||||
</span>
|
||||
<div class="filepath-browse-layout">
|
||||
<input
|
||||
id="output-directory"
|
||||
placeholder="DIRECTORY"
|
||||
data-i18n_placeholder="ui.placeholders.directory"
|
||||
/>
|
||||
<button id="output-directory-search" data-i18n="ui.button.browse">Browse</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="option-container switch">
|
||||
<span>
|
||||
<span data-i18n="ui.title.increaseRecursionLimit">Increase Recursion Limit</span>
|
||||
<span
|
||||
title="Having this enabled will set the recursion limit to 5000 using sys.setrecursionlimit(5000)."
|
||||
class="info_icon"
|
||||
data-i18n_title="ui.helpText.increaseRecursionLimit"
|
||||
></span>
|
||||
</span>
|
||||
<button id="recursion-limit-switch" data-i18n="ui.button.enable">Enable</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3 data-i18n="ui.title.manuallyProvideOptions">Manually Provide Options</h3>
|
||||
<div class="option-container input">
|
||||
<span>
|
||||
<span data-i18n="ui.title.manualArgumentInput">Manual Argument Input</span>
|
||||
<span
|
||||
title="Inject raw text into the generated command."
|
||||
class="info_icon"
|
||||
data-i18n_title="ui.helpText.manualArgumentInput"
|
||||
></span>
|
||||
</span>
|
||||
<input id="raw-arguments" placeholder="ARGUMENTS" data-i18n_placeholder="ui.placeholders.arguments" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3 data-i18n="ui.title.configuration">Configuration</h3>
|
||||
<button id="configuration-import" data-i18n="ui.button.importConfig">Import Config From JSON File</button>
|
||||
<button id="configuration-export" data-i18n="ui.button.exportConfig">Export Config To JSON File</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="current-command">
|
||||
<h2 data-i18n="ui.title.currentCommand">Current Command</h2>
|
||||
<textarea readonly></textarea>
|
||||
</div>
|
||||
|
||||
<div id="output">
|
||||
<h2 data-i18n="ui.title.output">Output</h2>
|
||||
<textarea readonly></textarea>
|
||||
</div>
|
||||
|
||||
<div id="common-issue-link" data-i18n="ui.notes.somethingWrongWithOutput">
|
||||
Something wrong with your exe? Read
|
||||
<a
|
||||
href="https://nitratine.net/blog/post/issues-when-using-auto-py-to-exe/?utm_source=auto_py_to_exe&utm_medium=application_link&utm_campaign=auto_py_to_exe_help&utm_content=bottom"
|
||||
target="_blank"
|
||||
>
|
||||
this post on how to fix common issues
|
||||
</a>
|
||||
for possible solutions.
|
||||
</div>
|
||||
|
||||
<div id="package-button-wrapper">
|
||||
<button id="package-button" data-i18n="ui.button.convert">Convert .py to .exe</button>
|
||||
<button id="open-output-folder-button" data-i18n="ui.button.openOutputFolder">Open Output Folder</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modal-area" class="modal-coverage modal-coverage-hidden"></div>
|
||||
|
||||
<div id="spinner-root" class="loading-spinner-wrapper">
|
||||
<div>
|
||||
<div class="loading-spinner"></div>
|
||||
<span class="loading-label">Initializing...</span>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
Handle configuration modifications
|
||||
*/
|
||||
|
||||
const configurationGetters = []; // Each function in this should either return null or [option.dest, value]
|
||||
const configurationSetters = {}; // dest: fn(value) => void, used to set option values
|
||||
const configurationCleaners = []; // Each function in this should clear a dest value
|
||||
|
||||
// Get option-value pairs [[option, value], ...]
|
||||
const getCurrentConfiguration = async (skipTransformations = false) => {
|
||||
const currentConfiguration = [
|
||||
{
|
||||
optionDest: 'noconfirm',
|
||||
value: true,
|
||||
},
|
||||
];
|
||||
|
||||
// Call all functions to get data
|
||||
configurationGetters.forEach((getter) => {
|
||||
const optionValuePair = getter();
|
||||
if (optionValuePair !== null) {
|
||||
currentConfiguration.push({
|
||||
optionDest: optionValuePair[0],
|
||||
value: optionValuePair[1],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (skipTransformations) {
|
||||
return currentConfiguration;
|
||||
}
|
||||
|
||||
// Convert all relative paths to absolute paths
|
||||
for (const c of currentConfiguration) {
|
||||
const option = options.find((o) => o.dest === c.optionDest);
|
||||
if (option === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
[OPTION_INPUT_VALUE_FILE, OPTION_INPUT_VALUE_DIRECTORY].some((v) => option.allowedInputValues.includes(v)) ||
|
||||
option.dest === 'filenames'
|
||||
) {
|
||||
c.value = await convertPathToAbsolute(c.value);
|
||||
}
|
||||
if (
|
||||
[OPTION_INPUT_VALUE_DOUBLE_FILE_DEST, OPTION_INPUT_VALUE_DOUBLE_DIRECTORY_DEST].some((v) =>
|
||||
option.allowedInputValues.includes(v)
|
||||
)
|
||||
) {
|
||||
const [src, dest] = c.value.split(pathSeparator);
|
||||
c.value = `${await convertPathToAbsolute(src)}${pathSeparator}${dest}`;
|
||||
}
|
||||
}
|
||||
|
||||
return currentConfiguration;
|
||||
};
|
||||
|
||||
const getNonPyinstallerConfiguration = () => {
|
||||
return {
|
||||
outputDirectory: document.getElementById('output-directory').value,
|
||||
increaseRecursionLimit: !document.getElementById('recursion-limit-switch').classList.contains('unselected'),
|
||||
manualArguments: document.getElementById('raw-arguments').value,
|
||||
};
|
||||
};
|
||||
|
||||
const getCurrentCommand = async () => {
|
||||
const currentConfiguration = await getCurrentConfiguration();
|
||||
|
||||
// Match configuration values with the correct flags
|
||||
const optionsAndValues = currentConfiguration
|
||||
.filter((c) => c.optionDest !== 'filenames')
|
||||
.map((c) => {
|
||||
// Identify the options
|
||||
const option = options.find((o) => o.dest === c.optionDest);
|
||||
|
||||
if (option.nargs === 0) {
|
||||
// For switches, there are some switches for false switches that we can use
|
||||
const potentialOption = options.find((o) => o.dest === c.optionDest && o.const === c.value);
|
||||
if (potentialOption !== undefined) {
|
||||
return chooseOptionString(potentialOption.option_strings);
|
||||
} else {
|
||||
return null; // If there is no alternate option, skip it as it won't be required
|
||||
}
|
||||
} else {
|
||||
const optionFlag = chooseOptionString(option.option_strings);
|
||||
return `${optionFlag} "${c.value}"`;
|
||||
}
|
||||
})
|
||||
.filter((x) => x !== null);
|
||||
|
||||
// Identify the entry script provided
|
||||
const entryScriptConfig = currentConfiguration.find((c) => c.optionDest === 'filenames');
|
||||
const entryScript = entryScriptConfig === undefined ? '' : entryScriptConfig.value;
|
||||
|
||||
return `pyinstaller ${optionsAndValues.join(' ')} ${
|
||||
getNonPyinstallerConfiguration().manualArguments
|
||||
} "${entryScript}"`;
|
||||
};
|
||||
|
||||
const updateCurrentCommandDisplay = async () => {
|
||||
document.querySelector('#current-command textarea').value = await getCurrentCommand();
|
||||
};
|
||||
|
||||
const isCommandDefault = async () => {
|
||||
return (await getCurrentCommand()) === 'pyinstaller --noconfirm --onedir --console ""';
|
||||
};
|
||||
@@ -0,0 +1,84 @@
|
||||
const options_ignored = ['help'];
|
||||
const options_static = ['filenames', 'onefile', 'console', 'icon_file', 'datas'];
|
||||
const options_overridden = ['specpath', 'distpath', 'workpath', 'noconfirm'];
|
||||
|
||||
const options_inputTypeFile = [
|
||||
'runtime_hooks',
|
||||
'version_file',
|
||||
'manifest',
|
||||
'resources',
|
||||
'splash',
|
||||
'entitlements_file',
|
||||
'icon_file',
|
||||
];
|
||||
const options_inputTypeDirectory = ['upx_dir', 'pathex', 'hookspath'];
|
||||
const options_inputTypeDoubleFileDest = ['datas', 'binaries'];
|
||||
const options_inputTypeDoubleDirectoryDest = ['datas'];
|
||||
|
||||
const advancedSections = [
|
||||
{
|
||||
titleI18nPath: 'dynamic.title.generalOptions',
|
||||
options: ['name', 'contents_directory', 'upx_dir', 'clean_build', 'loglevel'],
|
||||
},
|
||||
{
|
||||
titleI18nPath: 'dynamic.title.whatToBundleWhereToSearch',
|
||||
options: [
|
||||
'binaries',
|
||||
'pathex',
|
||||
'hiddenimports',
|
||||
'collect_submodules',
|
||||
'collect_data',
|
||||
'collect_binaries',
|
||||
'collect_all',
|
||||
'copy_metadata',
|
||||
'recursive_copy_metadata',
|
||||
'splash',
|
||||
'hookspath',
|
||||
'runtime_hooks',
|
||||
'excludes',
|
||||
'key',
|
||||
],
|
||||
},
|
||||
{
|
||||
titleI18nPath: 'dynamic.title.howToGenerate',
|
||||
options: ['debug', 'optimize', 'python_options', 'strip', 'noupx', 'upx_exclude'],
|
||||
},
|
||||
{
|
||||
titleI18nPath: 'dynamic.title.windowsAndMacOsXSpecificOptions',
|
||||
options: ['hide_console', 'disable_windowed_traceback'],
|
||||
},
|
||||
{
|
||||
titleI18nPath: 'dynamic.title.windowsSpecificOptions',
|
||||
options: ['version_file', 'manifest', 'embed_manifest', 'resources', 'uac_admin', 'uac_uiaccess'],
|
||||
},
|
||||
{
|
||||
titleI18nPath: 'dynamic.title.macOsxSpecificOptions',
|
||||
options: ['argv_emulation', 'bundle_identifier', 'target_arch', 'codesign_identity', 'entitlements_file'],
|
||||
},
|
||||
{
|
||||
titleI18nPath: 'dynamic.title.rarelyUsedSpecialOptions',
|
||||
options: ['runtime_tmpdir', 'bootloader_ignore_signals'],
|
||||
},
|
||||
];
|
||||
|
||||
// String constants
|
||||
OPTION_IGNORED = 'OPTION_IGNORED';
|
||||
OPTION_STATIC = 'OPTION_STATIC';
|
||||
OPTION_OVERRIDDEN = 'OPTION_OVERRIDDEN';
|
||||
OPTION_SHOW = 'OPTION_SHOW';
|
||||
|
||||
OPTION_INPUT_TYPE_SWITCH = 'OPTION_INPUT_TYPE_SWITCH';
|
||||
OPTION_INPUT_TYPE_DROPDOWN = 'OPTION_INPUT_TYPE_DROPDOWN';
|
||||
OPTION_INPUT_TYPE_INPUT = 'OPTION_INPUT_TYPE_INPUT';
|
||||
OPTION_INPUT_TYPE_MULTIPLE_INPUT = 'OPTION_INPUT_TYPE_MULTIPLE_INPUT';
|
||||
OPTION_INPUT_TYPE_DOUBLE_MULTIPLE_INPUT = 'OPTION_INPUT_TYPE_DOUBLE_MULTIPLE_INPUT';
|
||||
|
||||
OPTION_INPUT_VALUE_TEXT = 'OPTION_INPUT_VALUE_TEXT';
|
||||
OPTION_INPUT_VALUE_FILE = 'OPTION_INPUT_VALUE_FILE';
|
||||
OPTION_INPUT_VALUE_DIRECTORY = 'OPTION_INPUT_VALUE_DIRECTORY';
|
||||
OPTION_INPUT_VALUE_DOUBLE_FILE_DEST = 'OPTION_INPUT_VALUE_DOUBLE_FILE_DEST';
|
||||
OPTION_INPUT_VALUE_DOUBLE_DIRECTORY_DEST = 'OPTION_INPUT_VALUE_DOUBLE_DIRECTORY_DEST';
|
||||
|
||||
PACKAGING_STATE_READY = 'PACKAGING_STATE_READY';
|
||||
PACKAGING_STATE_PACKAGING = 'PACKAGING_STATE_PACKAGING';
|
||||
PACKAGING_STATE_COMPLETE = 'PACKAGING_STATE_COMPLETE';
|
||||
2344
venv3_12/Lib/site-packages/auto_py_to_exe/web/js/i18n.js
Normal file
2344
venv3_12/Lib/site-packages/auto_py_to_exe/web/js/i18n.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,66 @@
|
||||
const importConfiguration = (configuration) => {
|
||||
// TODO Check for version to support older versions
|
||||
|
||||
// Re-init UI by clearing everything (copy the array first as it will be mutated during the iteration)
|
||||
[...configurationCleaners].forEach((cleaner) => cleaner());
|
||||
|
||||
if ('pyinstallerOptions' in configuration) {
|
||||
configuration.pyinstallerOptions.forEach(({ optionDest, value }) => {
|
||||
if (configurationSetters.hasOwnProperty(optionDest)) {
|
||||
configurationSetters[optionDest](value);
|
||||
} else {
|
||||
// TODO Warn user?
|
||||
// TODO noconfirm is expected to come here
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// setup nonPyinstallerOptions
|
||||
if ('nonPyinstallerOptions' in configuration) {
|
||||
if ('increaseRecursionLimit' in configuration.nonPyinstallerOptions) {
|
||||
recursionLimitToggle(configuration.nonPyinstallerOptions.increaseRecursionLimit);
|
||||
}
|
||||
if ('manualArguments' in configuration.nonPyinstallerOptions) {
|
||||
document.getElementById('raw-arguments').value = configuration.nonPyinstallerOptions.manualArguments;
|
||||
}
|
||||
if ('outputDirectory' in configuration.nonPyinstallerOptions) {
|
||||
document.getElementById('output-directory').value = configuration.nonPyinstallerOptions.outputDirectory;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const _collectDataToExport = async () => {
|
||||
const nonPyinstallerConfiguration = getNonPyinstallerConfiguration();
|
||||
delete nonPyinstallerConfiguration.outputDirectory; // This does not need to be saved in the config
|
||||
|
||||
return {
|
||||
version: 'auto-py-to-exe-configuration_v1',
|
||||
pyinstallerOptions: await getCurrentConfiguration(true),
|
||||
nonPyinstallerOptions: nonPyinstallerConfiguration,
|
||||
};
|
||||
};
|
||||
|
||||
const onConfigurationImport = async () => {
|
||||
if (!(await isCommandDefault())) {
|
||||
const response = await displayModal(
|
||||
getTranslation('dynamic.modal.configModalTitle'),
|
||||
getTranslation('dynamic.modal.configModalDescription'),
|
||||
[
|
||||
getTranslation('dynamic.modal.configModalConfirmButton'),
|
||||
getTranslation('dynamic.modal.configModalCancelButton'),
|
||||
]
|
||||
);
|
||||
|
||||
if (response !== getTranslation('dynamic.modal.configModalConfirmButton')) return;
|
||||
}
|
||||
|
||||
const data = await eel.import_configuration()();
|
||||
if (data !== null) {
|
||||
importConfiguration(data);
|
||||
}
|
||||
};
|
||||
|
||||
const onConfigurationExport = async () => {
|
||||
const data = await _collectDataToExport();
|
||||
await eel.export_configuration(data)();
|
||||
};
|
||||
109
venv3_12/Lib/site-packages/auto_py_to_exe/web/js/initialise.js
Normal file
109
venv3_12/Lib/site-packages/auto_py_to_exe/web/js/initialise.js
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
Handle the initialisation of the ui
|
||||
*/
|
||||
|
||||
let options = [];
|
||||
|
||||
let pathSeparator = '';
|
||||
|
||||
const buildUpOptions = (providedOptions) => {
|
||||
return providedOptions.map((option) => {
|
||||
const name = option.dest;
|
||||
|
||||
let placement = OPTION_SHOW;
|
||||
if (options_ignored.indexOf(name) !== -1) {
|
||||
placement = OPTION_IGNORED;
|
||||
} else if (options_static.indexOf(name) !== -1) {
|
||||
placement = OPTION_STATIC;
|
||||
} else if (options_overridden.indexOf(name) !== -1) {
|
||||
placement = OPTION_OVERRIDDEN;
|
||||
}
|
||||
|
||||
let inputType = OPTION_INPUT_TYPE_INPUT;
|
||||
if (option.nargs === 0) {
|
||||
inputType = OPTION_INPUT_TYPE_SWITCH;
|
||||
} else if (option.choices !== null) {
|
||||
inputType = OPTION_INPUT_TYPE_DROPDOWN;
|
||||
} else if (option.dest === 'datas' || option.dest === 'binaries') {
|
||||
inputType = OPTION_INPUT_TYPE_DOUBLE_MULTIPLE_INPUT;
|
||||
} else if (option.default !== null || option.dest === 'upx_exclude') {
|
||||
inputType = OPTION_INPUT_TYPE_MULTIPLE_INPUT;
|
||||
}
|
||||
|
||||
const allowedInputValues = [];
|
||||
if (options_inputTypeFile.indexOf(name) !== -1) {
|
||||
allowedInputValues.push(OPTION_INPUT_VALUE_FILE);
|
||||
}
|
||||
if (options_inputTypeDirectory.indexOf(name) !== -1) {
|
||||
allowedInputValues.push(OPTION_INPUT_VALUE_DIRECTORY);
|
||||
}
|
||||
if (options_inputTypeDoubleFileDest.indexOf(name) !== -1) {
|
||||
allowedInputValues.push(OPTION_INPUT_VALUE_DOUBLE_FILE_DEST);
|
||||
}
|
||||
if (options_inputTypeDoubleDirectoryDest.indexOf(name) !== -1) {
|
||||
allowedInputValues.push(OPTION_INPUT_VALUE_DOUBLE_DIRECTORY_DEST);
|
||||
}
|
||||
if (allowedInputValues.length === 0) {
|
||||
allowedInputValues.push(OPTION_INPUT_VALUE_TEXT);
|
||||
}
|
||||
|
||||
return {
|
||||
...option,
|
||||
placement,
|
||||
inputType,
|
||||
allowedInputValues,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
// Get initialisation data from the server and setup the ui
|
||||
window.addEventListener('load', async () => {
|
||||
// Get initialisation data from Python
|
||||
console.log('Getting initialisation data');
|
||||
const initialisationData = await eel.initialise()();
|
||||
console.log('Received initialisation data');
|
||||
options = buildUpOptions(initialisationData.options);
|
||||
pathSeparator = initialisationData.pathSeparator;
|
||||
|
||||
// Setup user's default color scheme
|
||||
setupTheme();
|
||||
|
||||
// Setup ui events (for static content) and setup initial state
|
||||
setupEvents();
|
||||
|
||||
// Setup language selection
|
||||
setupLanguageSelection();
|
||||
|
||||
// Setup advanced section (for dynamic content)
|
||||
constructAdvancedSection(options);
|
||||
|
||||
// Setup json config file is supplied
|
||||
if (initialisationData.suppliedUiConfiguration !== null) {
|
||||
importConfiguration(initialisationData.suppliedUiConfiguration);
|
||||
}
|
||||
|
||||
// Set the output directory to the default if it hasn't already been set by `initialisationData.suppliedUiConfiguration`
|
||||
if (document.getElementById('output-directory').value === '') {
|
||||
document.getElementById('output-directory').value = initialisationData.defaultOutputFolder;
|
||||
}
|
||||
|
||||
// If a file is provided, put it in the script location
|
||||
if (initialisationData.filename !== null) {
|
||||
configurationSetters['filenames'](initialisationData.filename);
|
||||
}
|
||||
|
||||
// Display any warnings provided
|
||||
setupWarnings(initialisationData.warnings);
|
||||
|
||||
// Update the current command when setup is complete
|
||||
await updateCurrentCommandDisplay();
|
||||
|
||||
// Try to translate to the default browser language
|
||||
translate(initialisationData.languageHint);
|
||||
|
||||
// If the server stops, close the UI
|
||||
window.eel._websocket.addEventListener('close', (e) => window.close());
|
||||
|
||||
console.log('Application initialised');
|
||||
document.getElementById('spinner-root').style.display = 'none';
|
||||
});
|
||||
462
venv3_12/Lib/site-packages/auto_py_to_exe/web/js/interface.js
Normal file
462
venv3_12/Lib/site-packages/auto_py_to_exe/web/js/interface.js
Normal file
@@ -0,0 +1,462 @@
|
||||
/*
|
||||
Handle visual events
|
||||
*/
|
||||
|
||||
// Expand a section (typically triggered by clicking on a section heading)
|
||||
const expandSection = (sectionName) => {
|
||||
const root = document.getElementById(`section-${sectionName}`);
|
||||
const chevron = root.querySelector('.header img');
|
||||
const content = root.querySelector(`.content`);
|
||||
|
||||
if (root.getAttribute('data-expanded') === null) {
|
||||
// Show the section
|
||||
chevron.style.transform = 'rotate(0deg)';
|
||||
content.style.display = 'block';
|
||||
root.setAttribute('data-expanded', '');
|
||||
} else {
|
||||
// Hide the section
|
||||
chevron.style.transform = 'rotate(180deg)';
|
||||
content.style.display = 'none';
|
||||
root.removeAttribute('data-expanded');
|
||||
}
|
||||
};
|
||||
|
||||
// Colour an input based on the "allowed" arguments. Returns whether the field is valid or not
|
||||
const colourInput = async (inputNode, allowedToBeEmpty, allowedToBeFile, allowedToBeADirectory) => {
|
||||
const { value } = inputNode;
|
||||
if (
|
||||
(allowedToBeEmpty && value === '') ||
|
||||
(!allowedToBeEmpty && value !== '' && !allowedToBeFile && !allowedToBeADirectory) ||
|
||||
(allowedToBeFile && (await doesFileExist(value))) ||
|
||||
(allowedToBeADirectory && (await doesFolderExist(value)))
|
||||
) {
|
||||
inputNode.style.border = '';
|
||||
return true;
|
||||
} else {
|
||||
inputNode.style.border = '1px solid rgb(244, 67, 54)';
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const addDoubleInputForSrcDst = (
|
||||
parentNode,
|
||||
optionDest,
|
||||
source,
|
||||
destination,
|
||||
sourceCanBeFile,
|
||||
sourceCanBeDirectory
|
||||
) => {
|
||||
// Construct visible inputs
|
||||
const wrapper = document.createElement('div');
|
||||
parentNode.appendChild(wrapper);
|
||||
const sourceInput = document.createElement('input');
|
||||
wrapper.appendChild(sourceInput);
|
||||
const destinationInput = document.createElement('input');
|
||||
wrapper.appendChild(destinationInput);
|
||||
const removeButton = document.createElement('img');
|
||||
wrapper.appendChild(removeButton);
|
||||
|
||||
wrapper.classList.add('dual-value');
|
||||
|
||||
sourceInput.value = source;
|
||||
sourceInput.addEventListener('input', (event) => {
|
||||
colourInput(sourceInput, false, sourceCanBeFile, sourceCanBeDirectory);
|
||||
void updateCurrentCommandDisplay();
|
||||
});
|
||||
colourInput(sourceInput, false, sourceCanBeFile, sourceCanBeDirectory);
|
||||
|
||||
destinationInput.value = destination;
|
||||
destinationInput.addEventListener('input', (event) => {
|
||||
void updateCurrentCommandDisplay();
|
||||
});
|
||||
|
||||
// Add configurationGetter
|
||||
const configurationGetter = () => [optionDest, `${sourceInput.value}${pathSeparator}${destinationInput.value}`];
|
||||
configurationGetters.push(configurationGetter);
|
||||
|
||||
// Setup removal
|
||||
const onRemove = () => {
|
||||
wrapper.remove();
|
||||
const configurationGetterIndex = configurationGetters.indexOf(configurationGetter);
|
||||
configurationGetters.splice(configurationGetterIndex, 1);
|
||||
const configurationCleanerIndex = configurationCleaners.indexOf(onRemove);
|
||||
configurationCleaners.splice(configurationCleanerIndex, 1);
|
||||
void updateCurrentCommandDisplay();
|
||||
};
|
||||
removeButton.src = 'img/remove.svg';
|
||||
removeButton.addEventListener('click', onRemove);
|
||||
configurationCleaners.push(onRemove);
|
||||
|
||||
void updateCurrentCommandDisplay();
|
||||
};
|
||||
|
||||
const _createSubSectionInAdvanced = (title, i18nPath, options) => {
|
||||
const parent = document.querySelector('#section-advanced .content');
|
||||
|
||||
// The div wrapping the whole section
|
||||
const subSectionNode = document.createElement('div');
|
||||
parent.appendChild(subSectionNode);
|
||||
|
||||
// Setup title
|
||||
const subSectionTitleNode = document.createElement('h3');
|
||||
subSectionTitleNode.textContent = title;
|
||||
subSectionTitleNode.classList.add('noselect');
|
||||
subSectionTitleNode.dataset.i18n = i18nPath;
|
||||
subSectionNode.appendChild(subSectionTitleNode);
|
||||
|
||||
// Setup options
|
||||
options.forEach((o) => {
|
||||
// Container for option
|
||||
const container = document.createElement('div');
|
||||
subSectionNode.appendChild(container);
|
||||
container.classList.add('option-container');
|
||||
|
||||
// Option title / name
|
||||
const optionNode = document.createElement('span');
|
||||
container.appendChild(optionNode);
|
||||
optionNode.textContent = chooseOptionString(o.option_strings);
|
||||
|
||||
// Help icon
|
||||
const helpNode = document.createElement('span');
|
||||
optionNode.appendChild(helpNode); // Put the icon inside the option text
|
||||
helpNode.title = o.help.replace(/R\|/, '');
|
||||
helpNode.classList.add('info_icon');
|
||||
|
||||
if (o.inputType === OPTION_INPUT_TYPE_SWITCH) {
|
||||
container.classList.add('switch');
|
||||
|
||||
// Add button (take note of the target argument state using `const`)
|
||||
const enableButton = document.createElement('button');
|
||||
container.appendChild(enableButton);
|
||||
if (o.const === true) {
|
||||
enableButton.dataset.i18n = 'dynamic.button.enable';
|
||||
} else if (o.const === false) {
|
||||
enableButton.dataset.i18n = 'dynamic.button.disable';
|
||||
} else {
|
||||
throw new Error('Unknown o.const value: ' + JSON.stringify(o));
|
||||
}
|
||||
enableButton.textContent = getTranslation(enableButton.dataset.i18n);
|
||||
enableButton.classList.add('unselected');
|
||||
|
||||
// Function used to set the value of the switch
|
||||
const setValue = (enabled) => {
|
||||
if (enabled) {
|
||||
enableButton.classList.remove('unselected');
|
||||
enableButton.classList.add('selected');
|
||||
} else {
|
||||
enableButton.classList.add('unselected');
|
||||
enableButton.classList.remove('selected');
|
||||
}
|
||||
void updateCurrentCommandDisplay();
|
||||
};
|
||||
|
||||
// When clicked, toggle the value
|
||||
enableButton.addEventListener('click', () => {
|
||||
setValue(!enableButton.classList.contains('selected'));
|
||||
});
|
||||
|
||||
// Add configurationGetter
|
||||
const configurationGetter = () => [o.dest, !enableButton.classList.contains('unselected')];
|
||||
configurationGetters.push(configurationGetter);
|
||||
|
||||
// Add configurationSetter
|
||||
configurationSetters[o.dest] = setValue;
|
||||
|
||||
// Add configurationCleaner
|
||||
configurationCleaners.push(() => setValue(false));
|
||||
|
||||
// Allow a default value of `true` to come through
|
||||
if (o.default === true) {
|
||||
setValue(true);
|
||||
}
|
||||
} else if (o.inputType === OPTION_INPUT_TYPE_DROPDOWN) {
|
||||
container.classList.add('choice');
|
||||
|
||||
// Add dropdown
|
||||
const selectNode = document.createElement('select');
|
||||
container.appendChild(selectNode);
|
||||
selectNode.addEventListener('change', (event) => {
|
||||
void updateCurrentCommandDisplay();
|
||||
});
|
||||
|
||||
// Add options (including default '')
|
||||
const defaultOptionNode = document.createElement('option');
|
||||
selectNode.appendChild(defaultOptionNode);
|
||||
defaultOptionNode.textContent = '';
|
||||
|
||||
const choices = Array.isArray(o.choices) ? o.choices : Object.keys(o.choices);
|
||||
choices.map((choice) => {
|
||||
const optionNode = document.createElement('option');
|
||||
selectNode.appendChild(optionNode);
|
||||
optionNode.textContent = choice;
|
||||
optionNode.value = choice;
|
||||
});
|
||||
|
||||
// Add configurationGetter
|
||||
const configurationGetter = () => {
|
||||
const value = selectNode.value;
|
||||
return value === '' ? null : [o.dest, value];
|
||||
};
|
||||
configurationGetters.push(configurationGetter);
|
||||
|
||||
// Add configurationSetter
|
||||
configurationSetters[o.dest] = (value) => {
|
||||
if (choices.indexOf(value) !== 1) {
|
||||
selectNode.value = value;
|
||||
} else {
|
||||
selectNode.value = '';
|
||||
}
|
||||
selectNode.dispatchEvent(new Event('change'));
|
||||
};
|
||||
|
||||
// Add configurationCleaner
|
||||
configurationCleaners.push(() => {
|
||||
selectNode.value = '';
|
||||
selectNode.dispatchEvent(new Event('change'));
|
||||
});
|
||||
} else if (o.inputType === OPTION_INPUT_TYPE_INPUT) {
|
||||
container.classList.add('input');
|
||||
|
||||
const isOptionFileBased = o.allowedInputValues.indexOf(OPTION_INPUT_VALUE_FILE) !== -1;
|
||||
const isOptionDirectoryBased = o.allowedInputValues.indexOf(OPTION_INPUT_VALUE_DIRECTORY) !== -1;
|
||||
|
||||
// Add input node
|
||||
const inputNode = document.createElement('input');
|
||||
container.appendChild(inputNode);
|
||||
inputNode.placeholder = o.metavar || 'VALUE';
|
||||
inputNode.addEventListener('input', (event) => {
|
||||
void updateCurrentCommandDisplay();
|
||||
|
||||
if (isOptionFileBased || isOptionDirectoryBased) {
|
||||
colourInput(inputNode, true, isOptionFileBased, isOptionDirectoryBased);
|
||||
}
|
||||
});
|
||||
|
||||
// Show browse button if required (only file or folder - not both)
|
||||
if (isOptionFileBased || isOptionDirectoryBased) {
|
||||
container.classList.add('with-browse');
|
||||
const searchButton = document.createElement('button');
|
||||
container.appendChild(searchButton);
|
||||
searchButton.dataset.i18n = isOptionFileBased
|
||||
? 'dynamic.button.browseForFile'
|
||||
: 'dynamic.button.browseForFolder';
|
||||
searchButton.textContent = getTranslation(searchButton.dataset.i18n);
|
||||
searchButton.addEventListener('click', async () => {
|
||||
const value = isOptionFileBased ? await askForFile(null) : await askForFolder();
|
||||
if (value !== null) {
|
||||
inputNode.value = value;
|
||||
inputNode.dispatchEvent(new Event('input'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Add configurationGetter
|
||||
const configurationGetter = () => {
|
||||
const value = inputNode.value;
|
||||
return value === '' ? null : [o.dest, value];
|
||||
};
|
||||
configurationGetters.push(configurationGetter);
|
||||
|
||||
// Add configurationSetter
|
||||
configurationSetters[o.dest] = (value) => {
|
||||
inputNode.value = value;
|
||||
inputNode.dispatchEvent(new Event('input'));
|
||||
};
|
||||
|
||||
// Add configurationCleaner
|
||||
configurationCleaners.push(() => {
|
||||
inputNode.value = '';
|
||||
inputNode.dispatchEvent(new Event('input'));
|
||||
});
|
||||
} else if (o.inputType === OPTION_INPUT_TYPE_MULTIPLE_INPUT) {
|
||||
container.classList.add('multiple-input');
|
||||
|
||||
const isOptionFileBased = o.allowedInputValues.indexOf(OPTION_INPUT_VALUE_FILE) !== -1;
|
||||
const isOptionDirectoryBased = o.allowedInputValues.indexOf(OPTION_INPUT_VALUE_DIRECTORY) !== -1;
|
||||
|
||||
// Add button to add an entry
|
||||
const addButton = document.createElement('img');
|
||||
container.appendChild(addButton);
|
||||
addButton.src = 'img/plus.svg';
|
||||
|
||||
// Container to hold the values
|
||||
const valuesContainer = document.createElement('div');
|
||||
container.appendChild(valuesContainer);
|
||||
|
||||
const addValue = (value) => {
|
||||
// Container to hold the pair
|
||||
const valueContainer = document.createElement('div');
|
||||
valuesContainer.appendChild(valueContainer);
|
||||
|
||||
// Value input
|
||||
const inputNode = document.createElement('input');
|
||||
valueContainer.appendChild(inputNode);
|
||||
inputNode.value = value;
|
||||
inputNode.placeholder = o.metavar || 'VALUE';
|
||||
colourInput(inputNode, false, isOptionFileBased, isOptionDirectoryBased);
|
||||
inputNode.addEventListener('input', (event) => {
|
||||
colourInput(inputNode, false, isOptionFileBased, isOptionDirectoryBased);
|
||||
void updateCurrentCommandDisplay();
|
||||
});
|
||||
|
||||
// Add configurationGetter
|
||||
const configurationGetter = () => [o.dest, inputNode.value];
|
||||
configurationGetters.push(configurationGetter);
|
||||
|
||||
// Remove button
|
||||
const removeButtonNode = document.createElement('img');
|
||||
removeButtonNode.src = 'img/remove.svg';
|
||||
valueContainer.appendChild(removeButtonNode);
|
||||
const onRemove = () => {
|
||||
valueContainer.remove();
|
||||
const configurationGetterIndex = configurationGetters.indexOf(configurationGetter);
|
||||
configurationGetters.splice(configurationGetterIndex, 1);
|
||||
const configurationCleanerIndex = configurationCleaners.indexOf(onRemove);
|
||||
configurationCleaners.splice(configurationCleanerIndex, 1);
|
||||
void updateCurrentCommandDisplay();
|
||||
};
|
||||
removeButtonNode.addEventListener('click', onRemove);
|
||||
|
||||
// Add configurationCleaner
|
||||
configurationCleaners.push(onRemove);
|
||||
|
||||
void updateCurrentCommandDisplay();
|
||||
};
|
||||
|
||||
// Event to add a new input pair
|
||||
addButton.addEventListener('click', async () => {
|
||||
// Get initial value
|
||||
let initialValue = '';
|
||||
if (isOptionFileBased || isOptionDirectoryBased) {
|
||||
initialValue = isOptionFileBased ? await askForFile(null) : await askForFolder();
|
||||
if (initialValue === null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
addValue(initialValue);
|
||||
});
|
||||
|
||||
// Add configurationSetter
|
||||
configurationSetters[o.dest] = (value) => {
|
||||
addValue(value);
|
||||
};
|
||||
} else if (o.inputType === OPTION_INPUT_TYPE_DOUBLE_MULTIPLE_INPUT) {
|
||||
container.classList.add('multiple-input');
|
||||
|
||||
const isOptionFileBased = o.allowedInputValues.indexOf(OPTION_INPUT_VALUE_DOUBLE_FILE_DEST) !== -1;
|
||||
const isOptionDirectoryBased = o.allowedInputValues.indexOf(OPTION_INPUT_VALUE_DOUBLE_DIRECTORY_DEST) !== -1;
|
||||
|
||||
// Add button to add an entry
|
||||
const addButton = document.createElement('img');
|
||||
container.appendChild(addButton);
|
||||
addButton.src = 'img/plus.svg';
|
||||
|
||||
// Container to hold the value pairs
|
||||
const valuesContainer = document.createElement('div');
|
||||
container.appendChild(valuesContainer);
|
||||
|
||||
addButton.addEventListener('click', async () => {
|
||||
// Get initial value
|
||||
let initialValue = '';
|
||||
if (isOptionFileBased || isOptionDirectoryBased) {
|
||||
initialValue = isOptionFileBased ? await askForFile(null) : await askForFolder();
|
||||
if (initialValue === null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
addDoubleInputForSrcDst(valuesContainer, o.dest, initialValue, '.', true, false);
|
||||
});
|
||||
|
||||
// Add configurationSetter
|
||||
configurationSetters[o.dest] = (value) => {
|
||||
const [val1, val2] = value.split(pathSeparator);
|
||||
addDoubleInputForSrcDst(valuesContainer, o.dest, val1, val2, true, false);
|
||||
};
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const constructAdvancedSection = () => {
|
||||
// Setup pre-defined sections
|
||||
advancedSections.forEach((section) =>
|
||||
_createSubSectionInAdvanced(
|
||||
getTranslation(section.titleI18nPath),
|
||||
section.titleI18nPath,
|
||||
options.filter((o) => section.options.indexOf(o.dest) !== -1)
|
||||
)
|
||||
);
|
||||
|
||||
// Setup extra arguments
|
||||
const usedSectionOptions = flatMap(advancedSections.map((s) => s.options));
|
||||
const extraOptions = options.filter(
|
||||
(option) =>
|
||||
usedSectionOptions.indexOf(option.dest) === -1 &&
|
||||
option.placement !== OPTION_IGNORED &&
|
||||
option.placement !== OPTION_STATIC &&
|
||||
option.placement !== OPTION_OVERRIDDEN
|
||||
);
|
||||
if (extraOptions.length > 0) {
|
||||
_createSubSectionInAdvanced(getTranslation('dynamic.title.other'), 'dynamic.title.other', extraOptions);
|
||||
}
|
||||
};
|
||||
|
||||
const setupWarnings = (warnings) => {
|
||||
if (warnings.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const warningsRootNode = document.getElementById('warnings');
|
||||
|
||||
warnings.forEach((warningContent) => {
|
||||
// Create wrapper
|
||||
const wrapperNode = document.createElement('div');
|
||||
warningsRootNode.appendChild(wrapperNode);
|
||||
|
||||
// Create message
|
||||
const messageNode = document.createElement('p');
|
||||
wrapperNode.appendChild(messageNode);
|
||||
messageNode.innerHTML = warningContent;
|
||||
});
|
||||
};
|
||||
|
||||
const setupLanguageSelection = () => {
|
||||
const languageSelectNode = document.getElementById('language-selection');
|
||||
languageSelectNode.addEventListener('change', (event) => {
|
||||
translate(event.target.value);
|
||||
});
|
||||
supportedLanguages.forEach((language) => {
|
||||
const option = document.createElement('option');
|
||||
option.innerText = language.name;
|
||||
option.value = language.code;
|
||||
languageSelectNode.appendChild(option);
|
||||
});
|
||||
languageSelectNode.value = currentLanguage;
|
||||
};
|
||||
|
||||
// Toggle theme (triggered by clicking moon or sun)
|
||||
const _toggleTheme = () => {
|
||||
const root = document.querySelector('body');
|
||||
const onDarkThemeButton = document.querySelector('#on-dark-theme-button');
|
||||
const onLightThemeButton = document.querySelector('#on-light-theme-button');
|
||||
|
||||
if (root.classList.contains('dark-theme')) {
|
||||
onLightThemeButton.style.display = 'inline';
|
||||
onDarkThemeButton.style.display = 'none';
|
||||
} else {
|
||||
// dark
|
||||
onLightThemeButton.style.display = 'none';
|
||||
onDarkThemeButton.style.display = 'inline';
|
||||
}
|
||||
|
||||
root.classList.toggle('dark-theme');
|
||||
};
|
||||
|
||||
// Check if user's default color scheme is dark
|
||||
const setupTheme = () => {
|
||||
document.getElementById('theme-toggle').addEventListener('click', _toggleTheme);
|
||||
|
||||
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||
_toggleTheme();
|
||||
}
|
||||
};
|
||||
19
venv3_12/Lib/site-packages/auto_py_to_exe/web/js/messages.js
Normal file
19
venv3_12/Lib/site-packages/auto_py_to_exe/web/js/messages.js
Normal file
@@ -0,0 +1,19 @@
|
||||
eel.expose(putMessageInOutput);
|
||||
function putMessageInOutput(message) {
|
||||
const outputNode = document.querySelector('#output textarea');
|
||||
outputNode.value += message; // Add the message
|
||||
if (!message.endsWith('\n')) {
|
||||
outputNode.value += '\n'; // If there was no new line, add one
|
||||
}
|
||||
|
||||
// Set the correct height to fit all the output and then scroll to the bottom
|
||||
outputNode.style.height = 'auto';
|
||||
outputNode.style.height = outputNode.scrollHeight + 10 + 'px';
|
||||
window.scrollTo(0, document.body.scrollHeight);
|
||||
}
|
||||
|
||||
eel.expose(signalPackagingComplete);
|
||||
function signalPackagingComplete(successful) {
|
||||
setPackagingComplete(successful);
|
||||
window.scrollTo(0, document.body.scrollHeight);
|
||||
}
|
||||
105
venv3_12/Lib/site-packages/auto_py_to_exe/web/js/modal.js
Normal file
105
venv3_12/Lib/site-packages/auto_py_to_exe/web/js/modal.js
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Renders the native JS modal over the window.
|
||||
* Returns selected option from **buttonOptions** list.
|
||||
*
|
||||
* Input:
|
||||
* - title: string
|
||||
* - description: string
|
||||
* - [optional] buttonOptions: string[] = ['Yes', 'No']
|
||||
* - [optional]: closeEvent: string = 'Close'
|
||||
*
|
||||
* Returns:
|
||||
* - Promise<string>
|
||||
*/
|
||||
const displayModal = (title, description, buttonOptions = ['Yes', 'No'], closeEvent = 'Close') => {
|
||||
const buildHeader = (_title) => {
|
||||
const header = document.createElement('div');
|
||||
header.classList.add('modal-section', 'modal-header');
|
||||
|
||||
const closeButton = document.createElement('span');
|
||||
closeButton.classList.add('close-btn');
|
||||
closeButton.innerHTML = '×';
|
||||
header.appendChild(closeButton);
|
||||
|
||||
const titleElement = document.createElement('h2');
|
||||
titleElement.innerText = _title;
|
||||
header.appendChild(titleElement);
|
||||
|
||||
return {
|
||||
header: header,
|
||||
closeButton: closeButton,
|
||||
};
|
||||
};
|
||||
|
||||
const buildBody = (_description) => {
|
||||
const modalBody = document.createElement('div');
|
||||
modalBody.classList.add('modal-section');
|
||||
|
||||
const descriptionElement = document.createElement('a');
|
||||
descriptionElement.innerText = _description;
|
||||
modalBody.appendChild(descriptionElement);
|
||||
|
||||
return {
|
||||
body: modalBody,
|
||||
};
|
||||
};
|
||||
|
||||
const buildFooter = () => {
|
||||
const footerButtons = [];
|
||||
const footer = document.createElement('div');
|
||||
footer.classList.add('modal-section', 'modal-footer');
|
||||
|
||||
for (const label of buttonOptions) {
|
||||
const footerButton = document.createElement('button');
|
||||
footerButton.classList.add('modal-btn');
|
||||
footerButton.innerText = label;
|
||||
footer.appendChild(footerButton);
|
||||
footerButtons.push(footerButton);
|
||||
}
|
||||
|
||||
return {
|
||||
footer: footer,
|
||||
footerButtons: footerButtons,
|
||||
};
|
||||
};
|
||||
|
||||
const modalArea = document.getElementById('modal-area');
|
||||
modalArea.classList.remove('modal-coverage-hidden');
|
||||
|
||||
const headerElement = buildHeader(title);
|
||||
const bodyElement = buildBody(description);
|
||||
const footerElement = buildFooter();
|
||||
const footerButtons = footerElement.footerButtons;
|
||||
|
||||
const clearEventListeners = () => {
|
||||
headerElement.closeButton.removeEventListener('click', (_) => {});
|
||||
footerButtons.forEach((button) => button.removeEventListener('click', (_) => {}));
|
||||
};
|
||||
|
||||
const modalContent = document.createElement('div');
|
||||
modalContent.classList.add('modal-content');
|
||||
|
||||
modalContent.appendChild(headerElement.header);
|
||||
modalContent.appendChild(bodyElement.body);
|
||||
modalContent.appendChild(footerElement.footer);
|
||||
|
||||
modalArea.appendChild(modalContent);
|
||||
|
||||
return new Promise((resolve) => {
|
||||
headerElement.closeButton.addEventListener('click', (_) => {
|
||||
clearEventListeners();
|
||||
modalArea.removeChild(modalContent);
|
||||
modalArea.classList.add('modal-coverage-hidden');
|
||||
resolve(closeEvent);
|
||||
});
|
||||
|
||||
for (const [label, button] of zip(buttonOptions, footerButtons)) {
|
||||
button.addEventListener('click', (_) => {
|
||||
clearEventListeners();
|
||||
modalArea.removeChild(modalContent);
|
||||
modalArea.classList.add('modal-coverage-hidden');
|
||||
resolve(label);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
let packagingState = PACKAGING_STATE_READY;
|
||||
|
||||
const setPackagingState = (newState) => {
|
||||
packagingState = newState;
|
||||
|
||||
const outputSectionNode = document.getElementById('output');
|
||||
const outputTextAreaNode = outputSectionNode.querySelector('textarea');
|
||||
const convertButtonNode = document.getElementById('package-button');
|
||||
const openOutputButtonNode = document.getElementById('open-output-folder-button');
|
||||
const commonIssueLinkNode = document.getElementById('common-issue-link');
|
||||
|
||||
switch (newState) {
|
||||
case PACKAGING_STATE_READY:
|
||||
// Clear output
|
||||
outputSectionNode.classList.remove('show');
|
||||
outputTextAreaNode.value = '';
|
||||
outputTextAreaNode.rows = 0;
|
||||
outputTextAreaNode.classList.remove('failure');
|
||||
// Set the main button back to initial value
|
||||
convertButtonNode.dataset.i18n = 'ui.button.convert';
|
||||
convertButtonNode.innerHTML = getTranslation(convertButtonNode.dataset.i18n);
|
||||
// Hide open folder button
|
||||
openOutputButtonNode.classList.remove('show');
|
||||
// Hide common issue link
|
||||
commonIssueLinkNode.classList.remove('show');
|
||||
return;
|
||||
case PACKAGING_STATE_PACKAGING:
|
||||
// Disable convert button
|
||||
convertButtonNode.disabled = true;
|
||||
convertButtonNode.dataset.i18n = 'dynamic.button.converting';
|
||||
convertButtonNode.innerHTML = getTranslation(convertButtonNode.dataset.i18n);
|
||||
// Show output
|
||||
outputSectionNode.classList.add('show');
|
||||
return;
|
||||
case PACKAGING_STATE_COMPLETE:
|
||||
// Re-enable convert button and re-purpose it
|
||||
convertButtonNode.disabled = false;
|
||||
convertButtonNode.dataset.i18n = 'dynamic.button.clearOutput';
|
||||
convertButtonNode.innerHTML = getTranslation(convertButtonNode.dataset.i18n);
|
||||
// Show open folder button (beside "Clear Output" button)
|
||||
openOutputButtonNode.classList.add('show');
|
||||
// Show common issue link
|
||||
commonIssueLinkNode.classList.add('show');
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const startPackaging = async () => {
|
||||
eel.package(await getCurrentCommand(), getNonPyinstallerConfiguration())();
|
||||
setPackagingState(PACKAGING_STATE_PACKAGING);
|
||||
};
|
||||
|
||||
const setPackagingComplete = (successful) => {
|
||||
setPackagingState(PACKAGING_STATE_COMPLETE);
|
||||
|
||||
// Show red border around output on failure
|
||||
if (!successful) {
|
||||
const outputTextAreaNode = document.querySelector('#output textarea');
|
||||
outputTextAreaNode.classList.add('failure');
|
||||
}
|
||||
};
|
||||
261
venv3_12/Lib/site-packages/auto_py_to_exe/web/js/staticEvents.js
Normal file
261
venv3_12/Lib/site-packages/auto_py_to_exe/web/js/staticEvents.js
Normal file
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
Handle user events
|
||||
*/
|
||||
|
||||
// Top level inputs
|
||||
|
||||
const scriptLocationChange = async (event) => {
|
||||
colourInput(event.target, false, true, false);
|
||||
void updateCurrentCommandDisplay();
|
||||
};
|
||||
|
||||
const scriptLocationSearch = async (event) => {
|
||||
const entryScriptNode = document.getElementById('entry-script');
|
||||
const value = await askForFile('python');
|
||||
if (value !== null) {
|
||||
entryScriptNode.value = value;
|
||||
await scriptLocationChange({ target: entryScriptNode });
|
||||
}
|
||||
};
|
||||
|
||||
const oneFileOptionChange = (option) => (event) => {
|
||||
const onefileAdditionalFilesNote = document.getElementById('onefileAdditionalFilesNote');
|
||||
onefileAdditionalFilesNote.style.display = option === 'one-file' ? 'block' : 'none'; // Show the note if one-file is being used
|
||||
const oneFileButton = document.getElementById('one-file-button');
|
||||
oneFileButton.classList.add(option === 'one-file' ? 'selected' : 'unselected');
|
||||
oneFileButton.classList.remove(option !== 'one-file' ? 'selected' : 'unselected');
|
||||
const oneDirectoryButton = document.getElementById('one-directory-button');
|
||||
oneDirectoryButton.classList.add(option === 'one-directory' ? 'selected' : 'unselected');
|
||||
oneDirectoryButton.classList.remove(option !== 'one-directory' ? 'selected' : 'unselected');
|
||||
void updateCurrentCommandDisplay();
|
||||
};
|
||||
|
||||
const consoleWindowOptionChange = (option) => (event) => {
|
||||
const consoleButton = document.getElementById('console-based-button');
|
||||
consoleButton.classList.add(option === 'console' ? 'selected' : 'unselected');
|
||||
consoleButton.classList.remove(option !== 'console' ? 'selected' : 'unselected');
|
||||
const windowButton = document.getElementById('window-based-button');
|
||||
windowButton.classList.add(option === 'window' ? 'selected' : 'unselected');
|
||||
windowButton.classList.remove(option !== 'window' ? 'selected' : 'unselected');
|
||||
void updateCurrentCommandDisplay();
|
||||
};
|
||||
|
||||
const iconLocationChange = async (event) => {
|
||||
const valid = await colourInput(event.target, true, true, false);
|
||||
void updateCurrentCommandDisplay();
|
||||
|
||||
// If valid and a value exists, show the message if the file is not an ico file
|
||||
const warningElement = document.getElementById('icon-invalid-warning');
|
||||
if (valid && event.target.value !== '') {
|
||||
const isIcoFile = await isFileAnIco(event.target.value);
|
||||
warningElement.style.display = isIcoFile === false ? 'block' : 'none'; // isIcoFile is boolean | null
|
||||
} else {
|
||||
warningElement.style.display = 'none';
|
||||
}
|
||||
};
|
||||
|
||||
const iconLocationSearch = async (event) => {
|
||||
const iconPathNode = document.getElementById('icon-path');
|
||||
const value = await askForFile('icon');
|
||||
if (value !== null) {
|
||||
iconPathNode.value = value;
|
||||
await iconLocationChange({ target: iconPathNode });
|
||||
}
|
||||
};
|
||||
|
||||
const additionalFilesAddFiles = async (event) => {
|
||||
const files = await askForFiles();
|
||||
if (files !== null) {
|
||||
const datasListNode = document.getElementById('datas-list');
|
||||
files.forEach((file) => {
|
||||
addDoubleInputForSrcDst(datasListNode, 'datas', file, '.', true, true);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const additionalFilesAddFolder = async (event) => {
|
||||
const folder = await askForFolder();
|
||||
if (folder !== '') {
|
||||
const datasListNode = document.getElementById('datas-list');
|
||||
const destinationFolder = folder.split(/[/\\]/);
|
||||
addDoubleInputForSrcDst(
|
||||
datasListNode,
|
||||
'datas',
|
||||
folder,
|
||||
`${destinationFolder[destinationFolder.length - 1]}/`,
|
||||
true,
|
||||
true
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const additionalFilesAddBlank = (event) => {
|
||||
const datasListNode = document.getElementById('datas-list');
|
||||
addDoubleInputForSrcDst(datasListNode, 'datas', '', '.', true, true);
|
||||
};
|
||||
|
||||
// Settings section events
|
||||
|
||||
const outputDirectorySearch = async (event) => {
|
||||
const folder = await askForFolder();
|
||||
if (folder !== '') {
|
||||
const outputDirectoryInput = document.getElementById('output-directory');
|
||||
outputDirectoryInput.value = folder;
|
||||
}
|
||||
};
|
||||
|
||||
const recursionLimitToggle = (enabled) => {
|
||||
const button = document.getElementById('recursion-limit-switch');
|
||||
if (enabled) {
|
||||
button.classList.add('selected');
|
||||
button.classList.remove('unselected');
|
||||
} else {
|
||||
button.classList.remove('selected');
|
||||
button.classList.add('unselected');
|
||||
}
|
||||
};
|
||||
|
||||
const rawArgumentsChange = (event) => {
|
||||
void updateCurrentCommandDisplay();
|
||||
};
|
||||
|
||||
const packageScript = async (event) => {
|
||||
if (packagingState === PACKAGING_STATE_PACKAGING) {
|
||||
// Do not do anything while packaging
|
||||
return;
|
||||
}
|
||||
if (packagingState === PACKAGING_STATE_COMPLETE) {
|
||||
// This is now the clear output button
|
||||
setPackagingState(PACKAGING_STATE_READY);
|
||||
return;
|
||||
}
|
||||
|
||||
// Pre-checks
|
||||
const currentConfiguration = await getCurrentConfiguration();
|
||||
const entryScript = currentConfiguration.find((c) => c.optionDest === 'filenames').value;
|
||||
|
||||
if (entryScript === '') {
|
||||
alert(getTranslation('nonDom.alert.noScriptsLocationProvided'));
|
||||
return;
|
||||
}
|
||||
|
||||
const willOverwrite = await eel.will_packaging_overwrite_existing(
|
||||
entryScript,
|
||||
currentConfiguration.find((c) => c.optionDest === 'name')?.value,
|
||||
currentConfiguration.find((c) => c.optionDest === 'onefile').value,
|
||||
getNonPyinstallerConfiguration().outputDirectory
|
||||
)();
|
||||
if (willOverwrite && !confirm(getTranslation('nonDom.alert.overwritePreviousOutput'))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If checks have passed, package the script
|
||||
await startPackaging();
|
||||
};
|
||||
|
||||
const openOutputFolder = async (event) => {
|
||||
const currentConfiguration = await getCurrentConfiguration();
|
||||
const entryScript = currentConfiguration.find((c) => c.optionDest === 'filenames').value;
|
||||
const isOneFile = currentConfiguration.find((c) => c.optionDest === 'onefile').value;
|
||||
eel.open_output_in_explorer(getNonPyinstallerConfiguration().outputDirectory, entryScript, isOneFile)();
|
||||
};
|
||||
|
||||
const setupEvents = () => {
|
||||
// Script location
|
||||
document.getElementById('entry-script').addEventListener('input', scriptLocationChange);
|
||||
document.getElementById('entry-script-search').addEventListener('click', scriptLocationSearch);
|
||||
|
||||
// Output bundle type
|
||||
document.getElementById('one-directory-button').addEventListener('click', oneFileOptionChange('one-directory'));
|
||||
document.getElementById('one-file-button').addEventListener('click', oneFileOptionChange('one-file'));
|
||||
|
||||
// Console switch
|
||||
document.getElementById('console-based-button').addEventListener('click', consoleWindowOptionChange('console'));
|
||||
document.getElementById('window-based-button').addEventListener('click', consoleWindowOptionChange('window'));
|
||||
|
||||
// Icon
|
||||
document.getElementById('icon-path').addEventListener('input', iconLocationChange);
|
||||
document.getElementById('icon-path-search').addEventListener('click', iconLocationSearch);
|
||||
|
||||
// Additional files
|
||||
document.getElementById('additional-files-add-files-button').addEventListener('click', additionalFilesAddFiles);
|
||||
document.getElementById('additional-files-add-folder').addEventListener('click', additionalFilesAddFolder);
|
||||
document.getElementById('additional-files-add-blank').addEventListener('click', additionalFilesAddBlank);
|
||||
|
||||
// Settings
|
||||
document.getElementById('output-directory-search').addEventListener('click', outputDirectorySearch);
|
||||
document
|
||||
.getElementById('recursion-limit-switch')
|
||||
.addEventListener('click', (e) => recursionLimitToggle(e.target.classList.contains('unselected')));
|
||||
document.getElementById('raw-arguments').addEventListener('input', rawArgumentsChange);
|
||||
document.getElementById('configuration-import').addEventListener('click', () => onConfigurationImport());
|
||||
document.getElementById('configuration-export').addEventListener('click', () => onConfigurationExport());
|
||||
|
||||
// Build buttons
|
||||
document.getElementById('package-button').addEventListener('click', packageScript);
|
||||
document.getElementById('open-output-folder-button').addEventListener('click', openOutputFolder);
|
||||
|
||||
// Add configurationGetters
|
||||
const getEntryScript = () => ['filenames', document.getElementById('entry-script').value];
|
||||
const getOnefile = () => [
|
||||
'onefile',
|
||||
document.getElementById('one-directory-button').classList.contains('unselected'),
|
||||
];
|
||||
const getConsole = () => ['console', document.getElementById('window-based-button').classList.contains('unselected')];
|
||||
const getIcon = () => {
|
||||
const path = document.getElementById('icon-path').value;
|
||||
return path === '' ? null : ['icon_file', path];
|
||||
};
|
||||
configurationGetters.push(getEntryScript);
|
||||
configurationGetters.push(getOnefile);
|
||||
configurationGetters.push(getConsole);
|
||||
configurationGetters.push(getIcon);
|
||||
|
||||
// Add configurationSetters
|
||||
const setEntryScript = (value) => {
|
||||
document.getElementById('entry-script').value = value;
|
||||
scriptLocationChange({ target: document.getElementById('entry-script') });
|
||||
};
|
||||
const setOnefile = (value) => {
|
||||
if (value) {
|
||||
document.getElementById('one-directory-button').classList.add('unselected');
|
||||
document.getElementById('one-file-button').classList.remove('unselected');
|
||||
} else {
|
||||
document.getElementById('one-directory-button').classList.remove('unselected');
|
||||
document.getElementById('one-file-button').classList.add('unselected');
|
||||
}
|
||||
};
|
||||
const setConsole = (value) => {
|
||||
if (value) {
|
||||
document.getElementById('console-based-button').classList.remove('unselected');
|
||||
document.getElementById('window-based-button').classList.add('unselected');
|
||||
} else {
|
||||
document.getElementById('console-based-button').classList.add('unselected');
|
||||
document.getElementById('window-based-button').classList.remove('unselected');
|
||||
}
|
||||
};
|
||||
const setAdditionalFile = (value) => {
|
||||
const datasListNode = document.getElementById('datas-list');
|
||||
const [val1, val2] = value.split(pathSeparator);
|
||||
addDoubleInputForSrcDst(datasListNode, 'datas', val1, val2, true, true);
|
||||
};
|
||||
const setIcon = (value) => {
|
||||
document.getElementById('icon-path').value = value;
|
||||
document.getElementById('icon-path').dispatchEvent(new Event('input'));
|
||||
};
|
||||
configurationSetters['filenames'] = setEntryScript;
|
||||
configurationSetters['onefile'] = setOnefile;
|
||||
configurationSetters['console'] = setConsole;
|
||||
configurationSetters['datas'] = setAdditionalFile;
|
||||
configurationSetters['icon_file'] = setIcon;
|
||||
|
||||
configurationCleaners.push(() => setEntryScript('')); // filenames
|
||||
configurationCleaners.push(() => setOnefile(false)); // onefile
|
||||
configurationCleaners.push(() => setConsole(true)); // console
|
||||
configurationCleaners.push(() => setIcon('')); // icon_file
|
||||
|
||||
// Soft initialise (to trigger any required initial events)
|
||||
setEntryScript('');
|
||||
setOnefile(false);
|
||||
setConsole(true);
|
||||
};
|
||||
50
venv3_12/Lib/site-packages/auto_py_to_exe/web/js/utils.js
Normal file
50
venv3_12/Lib/site-packages/auto_py_to_exe/web/js/utils.js
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
Util functions
|
||||
*/
|
||||
|
||||
const flatMap = (xs) => xs.reduce((x, y) => x.concat(y), []); // Not all browsers have Array.flatMap
|
||||
|
||||
/*
|
||||
* Equivalent of Python zip(*args) function. Usage:
|
||||
*
|
||||
* for (let [var1, ..., varN] of zip(arr1, ..., arrN)) {
|
||||
* ...
|
||||
* }
|
||||
*/
|
||||
const zip = (...arrays) => [...arrays[0]].map((_, index) => arrays.map((arr) => arr[index]));
|
||||
|
||||
const doesFileExist = async (path) => {
|
||||
return await eel.does_file_exist(path)();
|
||||
};
|
||||
|
||||
const doesFolderExist = async (path) => {
|
||||
return await eel.does_folder_exist(path)();
|
||||
};
|
||||
|
||||
const askForFile = async (fileType) => {
|
||||
return await eel.ask_file(fileType)();
|
||||
};
|
||||
|
||||
const askForFiles = async () => {
|
||||
return await eel.ask_files()();
|
||||
};
|
||||
|
||||
const askForFolder = async () => {
|
||||
return await eel.ask_folder()();
|
||||
};
|
||||
|
||||
const isFileAnIco = async (file_path) => {
|
||||
return await eel.is_file_an_ico(file_path)();
|
||||
};
|
||||
|
||||
const convertPathToAbsolute = async (path) => {
|
||||
return await eel.convert_path_to_absolute(path)();
|
||||
};
|
||||
|
||||
const chooseOptionString = (optionStrings) => {
|
||||
// Try not to use compressed flags
|
||||
if (optionStrings[0].length === 2 && optionStrings.length > 1) {
|
||||
return optionStrings[1];
|
||||
}
|
||||
return optionStrings[0];
|
||||
};
|
||||
Reference in New Issue
Block a user