add version, change icon file name
This commit is contained in:
parent
ef7452a378
commit
071f34e0c3
BIN
projectinitializer.ico
Normal file
BIN
projectinitializer.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
36
projectinitializer.spec
Normal file
36
projectinitializer.spec
Normal file
@ -0,0 +1,36 @@
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
block_cipher = None
|
||||
|
||||
a = Analysis(scripts=['projectinitializer\\__main__.py'],
|
||||
pathex=['projectinitializer'],
|
||||
binaries=[],
|
||||
datas=[('projectinitializer.ico', '.')],
|
||||
hiddenimports=[],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=None,
|
||||
noarchive=False)
|
||||
|
||||
pyz = PYZ(a.pure, a.zipped_data, cipher=None)
|
||||
|
||||
exe = EXE(pyz,
|
||||
a.scripts,
|
||||
[], # Binaries/Datas usually handled by Analysis/COLLECT
|
||||
exclude_binaries=True, # Let COLLECT handle binaries in one-dir
|
||||
name='projectinitializer',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True, # Use UPX based on config
|
||||
runtime_tmpdir=None,
|
||||
console=True, # Set console based on GUI checkbox
|
||||
disable_windowed_traceback=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
icon='projectinitializer.ico')
|
||||
90
projectinitializer/_version.py
Normal file
90
projectinitializer/_version.py
Normal file
@ -0,0 +1,90 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# File generated by PyInstaller GUI Wrapper. DO NOT EDIT MANUALLY.
|
||||
# Contains build-time information scraped from Git (if available)
|
||||
# and a helper function to format version strings.
|
||||
|
||||
|
||||
import re
|
||||
|
||||
# --- Version Data (Generated) ---
|
||||
# This section is automatically generated by the build process.
|
||||
__version__ = "v.0.0.0.1-1-g1fdf738-dirty"
|
||||
GIT_COMMIT_HASH = "1fdf738d0257a954189792f557dc2bcd427f1264"
|
||||
GIT_BRANCH = "master"
|
||||
BUILD_TIMESTAMP = "2025-05-08T08:42:23Z"
|
||||
IS_GIT_REPO = True
|
||||
|
||||
# --- Default Values (for comparison or fallback) ---
|
||||
DEFAULT_VERSION = "0.0.0+unknown"
|
||||
DEFAULT_COMMIT = "Unknown"
|
||||
DEFAULT_BRANCH = "Unknown"
|
||||
|
||||
# --- Helper Function ---
|
||||
def get_version_string(format_string=None):
|
||||
"""
|
||||
Returns a formatted string based on the build version information.
|
||||
|
||||
Args:
|
||||
format_string (str, optional): A format string using placeholders.
|
||||
Defaults to "{{version}} ({{branch}}/{{commit_short}})" if None.
|
||||
Placeholders:
|
||||
{{version}}: Full version string (e.g., 'v1.0.0-5-gabcdef-dirty')
|
||||
{{tag}}: Clean tag part if exists (e.g., 'v1.0.0'), else DEFAULT_VERSION.
|
||||
{{commit}}: Full Git commit hash.
|
||||
{{commit_short}}: Short Git commit hash (7 chars).
|
||||
{{branch}}: Git branch name.
|
||||
{{dirty}}: '-dirty' if the repo was dirty, empty otherwise.
|
||||
{{timestamp}}: Full build timestamp (ISO 8601 UTC).
|
||||
{{timestamp_short}}: Build date only (YYYY-MM-DD).
|
||||
{{is_git}}: 'Git' if IS_GIT_REPO is True, 'Unknown' otherwise.
|
||||
|
||||
Returns:
|
||||
str: The formatted version string, or an error message if formatting fails.
|
||||
"""
|
||||
if format_string is None:
|
||||
format_string = "{version} ({branch}/{commit_short})" # Sensible default
|
||||
|
||||
replacements = {}
|
||||
try:
|
||||
# Prepare data dictionary for substitution
|
||||
replacements['version'] = __version__ if __version__ else DEFAULT_VERSION
|
||||
replacements['commit'] = GIT_COMMIT_HASH if GIT_COMMIT_HASH else DEFAULT_COMMIT
|
||||
replacements['commit_short'] = GIT_COMMIT_HASH[:7] if GIT_COMMIT_HASH and len(GIT_COMMIT_HASH) >= 7 else DEFAULT_COMMIT
|
||||
replacements['branch'] = GIT_BRANCH if GIT_BRANCH else DEFAULT_BRANCH
|
||||
replacements['timestamp'] = BUILD_TIMESTAMP if BUILD_TIMESTAMP else "Unknown"
|
||||
replacements['timestamp_short'] = BUILD_TIMESTAMP.split('T')[0] if BUILD_TIMESTAMP and 'T' in BUILD_TIMESTAMP else "Unknown"
|
||||
replacements['is_git'] = "Git" if IS_GIT_REPO else "Unknown"
|
||||
replacements['dirty'] = "-dirty" if __version__ and __version__.endswith('-dirty') else ""
|
||||
|
||||
# Extract clean tag using regex (handles versions like v1.0.0, 1.0.0)
|
||||
tag = DEFAULT_VERSION
|
||||
if __version__ and IS_GIT_REPO:
|
||||
# Match optional 'v' prefix, then major.minor.patch
|
||||
match = re.match(r'^(v?([0-9]+)\.([0-9]+)\.([0-9]+))', __version__)
|
||||
if match:
|
||||
tag = match.group(1) # Get the full tag (e.g., 'v1.0.0')
|
||||
replacements['tag'] = tag
|
||||
|
||||
# Perform substitution using regex to find placeholders {placeholder}
|
||||
output_string = format_string
|
||||
# Iterate through placeholders and replace them in the format string
|
||||
for placeholder, value in replacements.items():
|
||||
# Compile regex pattern for {placeholder}, allowing for whitespace inside braces
|
||||
pattern = re.compile(r'{\s*' + re.escape(placeholder) + r'\s*}')
|
||||
# Substitute found patterns with the corresponding string value
|
||||
output_string = pattern.sub(str(value), output_string)
|
||||
|
||||
# Optional: Check if any placeholders remain unsubstituted (could indicate typo)
|
||||
if re.search(r'{\s*[\w_]+\s*}', output_string):
|
||||
# You might want to log this or handle it, for now, we return the string as is
|
||||
# print(f"Warning: Unsubstituted placeholders remain in version string: {output_string}")
|
||||
pass
|
||||
|
||||
return output_string
|
||||
|
||||
except Exception as e:
|
||||
# Return a simple error message in case of unexpected formatting issues
|
||||
# Avoid printing directly from this generated function
|
||||
return f"[Formatting Error: {e}]"
|
||||
|
||||
|
||||
@ -6,31 +6,16 @@ from typing import Dict, Any
|
||||
|
||||
# --- Constants ---
|
||||
CONFIG_FILE_NAME: str = ".project_initializer_config.json"
|
||||
DEFAULT_ICON_FILE_NAME: str = "default_icon.ico" # Nome del file icona in assets/
|
||||
DEFAULT_ICON_FILE_NAME: str = "default_icon.ico"
|
||||
|
||||
# Path to the assets directory within the package
|
||||
# Path(__file__) is the path to this settings.py file
|
||||
# .parent is the config/ directory
|
||||
# .parent is the project_initializer/ directory
|
||||
# / "assets" gives project_initializer/assets/
|
||||
PACKAGE_ASSETS_PATH: Path = Path(__file__).resolve().parent.parent / "assets"
|
||||
DEFAULT_ICON_PATH: Path = PACKAGE_ASSETS_PATH / DEFAULT_ICON_FILE_NAME
|
||||
|
||||
|
||||
# --- Configuration Management ---
|
||||
|
||||
# --- Configuration Management (invariato) ---
|
||||
def get_config_file_path() -> Path:
|
||||
"""
|
||||
Gets the path to the configuration file.
|
||||
Stores it in the user's home directory.
|
||||
"""
|
||||
return Path.home() / CONFIG_FILE_NAME
|
||||
|
||||
def load_app_configuration() -> Dict[str, Any]:
|
||||
"""
|
||||
Loads the application configuration (e.g., last used root directory).
|
||||
Returns a dictionary with the configuration or an empty dictionary if not found/error.
|
||||
"""
|
||||
config_path = get_config_file_path()
|
||||
if config_path.exists():
|
||||
try:
|
||||
@ -45,9 +30,6 @@ def load_app_configuration() -> Dict[str, Any]:
|
||||
return {}
|
||||
|
||||
def save_app_configuration(config_data: Dict[str, Any]) -> None:
|
||||
"""
|
||||
Saves the application configuration to a JSON file.
|
||||
"""
|
||||
config_path = get_config_file_path()
|
||||
try:
|
||||
with open(config_path, "w", encoding="utf-8") as f:
|
||||
@ -58,7 +40,7 @@ def save_app_configuration(config_data: Dict[str, Any]) -> None:
|
||||
# --- File Templates ---
|
||||
|
||||
def get_gitignore_template() -> str:
|
||||
"""Returns the .gitignore template string."""
|
||||
# ... (contenuto invariato, omesso per brevità) ...
|
||||
return """# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
@ -209,13 +191,10 @@ dmypy.json
|
||||
# Temporary files
|
||||
*.swp
|
||||
*~
|
||||
|
||||
_dist/
|
||||
_build/
|
||||
"""
|
||||
|
||||
def get_readme_template(project_name_original: str) -> str:
|
||||
"""Returns the README.md template string, formatted with the project name."""
|
||||
# ... (contenuto invariato, omesso per brevità) ...
|
||||
return f"""# {project_name_original}
|
||||
|
||||
A brief description of {project_name_original}.
|
||||
@ -235,7 +214,7 @@ A brief description of {project_name_original}.
|
||||
"""
|
||||
|
||||
def get_main_py_template(project_name_original: str, project_name_lower: str) -> str:
|
||||
"""Returns the __main__.py template string for the new project."""
|
||||
# ... (contenuto invariato, omesso per brevità) ...
|
||||
return f"""# {project_name_lower}/__main__.py
|
||||
|
||||
# Example import assuming your main logic is in a 'main' function
|
||||
@ -255,17 +234,20 @@ if __name__ == "__main__":
|
||||
main()
|
||||
"""
|
||||
|
||||
def get_spec_file_template(project_name_original: str, project_name_lower: str) -> str:
|
||||
|
||||
# --- MODIFICA QUI per accettare project_icon_filename ---
|
||||
def get_spec_file_template(project_name_original: str, project_name_lower: str, project_icon_filename: str) -> str:
|
||||
"""Returns the .spec file template string for PyInstaller."""
|
||||
return f"""# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
block_cipher = None
|
||||
|
||||
a = Analysis(
|
||||
['{project_name_lower}/__main__.py'], # Main script of the project being built
|
||||
pathex=['.'], # Current directory, where the .spec file is, and project_name_lower is a subfolder
|
||||
['{project_name_lower}/__main__.py'],
|
||||
pathex=['.'],
|
||||
binaries=[],
|
||||
datas=[('{DEFAULT_ICON_FILE_NAME}', '.')], # Icon file relative to project root
|
||||
# Usa project_icon_filename nella sezione datas
|
||||
datas=[('{project_icon_filename}', '.')],
|
||||
hiddenimports=[],
|
||||
hookspath=[],
|
||||
runtime_hooks=[],
|
||||
@ -291,13 +273,15 @@ exe = EXE(
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=True, # Set to False for GUI-only applications
|
||||
icon='{DEFAULT_ICON_FILE_NAME}' # Icon relative to project root
|
||||
console=True,
|
||||
# Usa project_icon_filename per l'opzione icon
|
||||
icon='{project_icon_filename}'
|
||||
)
|
||||
"""
|
||||
# --- FINE MODIFICA ---
|
||||
|
||||
def get_english_manual_template(project_name_original: str) -> str:
|
||||
"""Returns the English manual template string."""
|
||||
# ... (contenuto invariato, omesso per brevità) ...
|
||||
return f"""# {project_name_original} - English Manual
|
||||
|
||||
## Introduction
|
||||
@ -325,7 +309,7 @@ Common issues and their solutions.
|
||||
"""
|
||||
|
||||
def get_italian_manual_template(project_name_original: str) -> str:
|
||||
"""Returns the Italian manual template string."""
|
||||
# ... (contenuto invariato, omesso per brevità) ...
|
||||
return f"""# {project_name_original} - Manuale Italiano
|
||||
|
||||
## Introduzione
|
||||
|
||||
@ -44,9 +44,21 @@ def create_project(root_directory_str: str, project_name_original: str) -> str:
|
||||
raise ProjectCreationError(f"Root directory '{root_directory}' does not exist or is not a directory.")
|
||||
|
||||
project_root_path = root_directory / project_name_original
|
||||
# Sanitize project_name_original for use as Python module/package name
|
||||
project_name_lower = project_name_original.lower().replace("-", "_").replace(" ", "_")
|
||||
# Basic validation for a Python module name (simplistic)
|
||||
|
||||
# Sanitize project_name_original for use as a filename (e.g., for the icon)
|
||||
# Rimuovi spazi e caratteri problematici, ma mantieni il case originale se possibile per il nome file .ico
|
||||
# Per il file .spec e l'icona, è meglio usare un nome file "pulito".
|
||||
# Usiamo project_name_original ma rimuovendo solo gli spazi, o potremmo usare project_name_lower
|
||||
# per coerenza con il file .spec. Scegliamo project_name_original ma sanitizzato.
|
||||
# Un nome file come "Mio Progetto.ico" è valido su Windows, ma meno portabile.
|
||||
# "MioProgetto.ico" è meglio.
|
||||
project_icon_filename_base = "".join(c if c.isalnum() or c in ['_', '-'] else '' for c in project_name_original)
|
||||
if not project_icon_filename_base: # Se il nome originale era tipo "!@#$%"
|
||||
project_icon_filename_base = project_name_lower # Fallback a nome lowercase
|
||||
project_icon_filename = f"{project_icon_filename_base}.ico"
|
||||
|
||||
|
||||
if not project_name_lower.isidentifier():
|
||||
raise ProjectCreationError(
|
||||
f"The sanitized project name '{project_name_lower}' (derived from '{project_name_original}') "
|
||||
@ -54,7 +66,6 @@ def create_project(root_directory_str: str, project_name_original: str) -> str:
|
||||
"not starting with a number."
|
||||
)
|
||||
|
||||
|
||||
if project_root_path.exists():
|
||||
raise ProjectCreationError(f"Project directory '{project_root_path}' already exists.")
|
||||
|
||||
@ -73,17 +84,16 @@ def create_project(root_directory_str: str, project_name_original: str) -> str:
|
||||
core_subpath.mkdir()
|
||||
print(f"Info: Created directory: {core_subpath}")
|
||||
(core_subpath / "__init__.py").touch()
|
||||
(core_subpath / "core.py").touch() # Empty core logic file
|
||||
(core_subpath / "core.py").touch()
|
||||
|
||||
gui_subpath = src_package_path / "gui"
|
||||
gui_subpath.mkdir()
|
||||
print(f"Info: Created directory: {gui_subpath}")
|
||||
(gui_subpath / "__init__.py").touch()
|
||||
(gui_subpath / "gui.py").touch() # Empty gui logic file
|
||||
(gui_subpath / "gui.py").touch()
|
||||
|
||||
(src_package_path / "__init__.py").touch() # Make project_name_lower a package
|
||||
(src_package_path / "__init__.py").touch()
|
||||
|
||||
# Create __main__.py for the new project
|
||||
main_py_content = settings.get_main_py_template(project_name_original, project_name_lower)
|
||||
with open(src_package_path / "__main__.py", "w", encoding="utf-8") as f:
|
||||
f.write(main_py_content)
|
||||
@ -110,26 +120,25 @@ def create_project(root_directory_str: str, project_name_original: str) -> str:
|
||||
f.write(readme_content)
|
||||
print(f"Info: Created file: {project_root_path / 'README.md'}")
|
||||
|
||||
# --- MODIFICA QUI per usare project_icon_filename ---
|
||||
# 6) Create .spec file for PyInstaller for the new project
|
||||
spec_content = settings.get_spec_file_template(project_name_original, project_name_lower)
|
||||
# The .spec file should be in the project_root_path
|
||||
# Passa project_icon_filename al template del file .spec
|
||||
spec_content = settings.get_spec_file_template(project_name_original, project_name_lower, project_icon_filename)
|
||||
with open(project_root_path / f"{project_name_lower}.spec", "w", encoding="utf-8") as f:
|
||||
f.write(spec_content)
|
||||
print(f"Info: Created file: {project_root_path / f'{project_name_lower}.spec'}")
|
||||
|
||||
# 7) Copy default .ico file to the new project's root
|
||||
# The icon in the .spec file is referenced relative to the project root.
|
||||
destination_icon_path = project_root_path / settings.DEFAULT_ICON_FILE_NAME
|
||||
# 7) Copy default .ico file to the new project's root with the new name
|
||||
# Usa project_icon_filename come nome del file di destinazione
|
||||
destination_icon_path = project_root_path / project_icon_filename
|
||||
if settings.DEFAULT_ICON_PATH.exists():
|
||||
shutil.copy2(settings.DEFAULT_ICON_PATH, destination_icon_path)
|
||||
print(f"Info: Copied icon: {destination_icon_path} from {settings.DEFAULT_ICON_PATH}")
|
||||
else:
|
||||
# This case should ideally be handled by ensuring the tool's assets are present.
|
||||
# For robustness, we can create an empty placeholder if it's truly missing.
|
||||
destination_icon_path.touch()
|
||||
print(f"Warning: Default icon '{settings.DEFAULT_ICON_PATH}' not found. "
|
||||
f"Created empty placeholder: {destination_icon_path}")
|
||||
|
||||
# --- FINE MODIFICA ICONA ---
|
||||
|
||||
# 8) Create .gitignore for the new project
|
||||
gitignore_content = settings.get_gitignore_template()
|
||||
@ -142,7 +151,6 @@ def create_project(root_directory_str: str, project_name_original: str) -> str:
|
||||
print(f"Info: Initializing Git repository in {project_root_path}...")
|
||||
subprocess.run(["git", "init"], cwd=project_root_path, check=True, capture_output=True, text=True)
|
||||
print(f"Info: Git repository initialized.")
|
||||
# Optional: Add all files and make an initial commit
|
||||
subprocess.run(["git", "add", "."], cwd=project_root_path, check=True, capture_output=True, text=True)
|
||||
print(f"Info: All files added to Git staging area.")
|
||||
subprocess.run(
|
||||
@ -151,19 +159,14 @@ def create_project(root_directory_str: str, project_name_original: str) -> str:
|
||||
)
|
||||
print("Info: Initial commit made.")
|
||||
except FileNotFoundError:
|
||||
# Git command not found, not a fatal error for project creation itself.
|
||||
# The user can initialize git manually later.
|
||||
print("Warning: Git command not found. Please install Git to initialize a repository automatically.")
|
||||
print(" The project structure has been created, but a Git repository was not initialized.")
|
||||
except subprocess.CalledProcessError as e:
|
||||
# Error during Git operation.
|
||||
error_message = f"Error during Git operation: {e.stderr}"
|
||||
if e.stdout:
|
||||
error_message += f"\nGit stdout: {e.stdout}"
|
||||
if e.stdout: error_message += f"\nGit stdout: {e.stdout}"
|
||||
print(f"Warning: {error_message}")
|
||||
print(" The project structure has been created, but there was an issue with Git initialization.")
|
||||
|
||||
|
||||
success_message = (
|
||||
f"Project '{project_name_original}' created successfully in '{project_root_path}'.\n"
|
||||
f"To run your new project (example): python -m {project_name_lower}"
|
||||
@ -172,8 +175,6 @@ def create_project(root_directory_str: str, project_name_original: str) -> str:
|
||||
return success_message
|
||||
|
||||
except OSError as e:
|
||||
# Catch other OS-level errors (permissions, disk full, etc.)
|
||||
# Attempt to clean up if partial creation occurred
|
||||
error_msg = f"OS error during project creation: {e}"
|
||||
print(f"Error: {error_msg}")
|
||||
if project_root_path.exists() and project_root_path.is_dir():
|
||||
@ -183,10 +184,10 @@ def create_project(root_directory_str: str, project_name_original: str) -> str:
|
||||
except OSError as cleanup_e:
|
||||
print(f"Error: Error during cleanup of '{project_root_path}': {cleanup_e}")
|
||||
raise ProjectCreationError(error_msg) from e
|
||||
except Exception as e: # Catch any other unexpected error
|
||||
except Exception as e:
|
||||
error_msg = f"An unexpected error occurred: {e}"
|
||||
print(f"Error: {error_msg}")
|
||||
if project_root_path.exists() and project_root_path.is_dir(): # Defensive cleanup
|
||||
if project_root_path.exists() and project_root_path.is_dir():
|
||||
try:
|
||||
shutil.rmtree(project_root_path)
|
||||
print(f"Info: Cleaned up partially created project at {project_root_path} due to unexpected error.")
|
||||
|
||||
@ -6,10 +6,29 @@ from pathlib import Path
|
||||
from projectinitializer.config import settings # type: ignore
|
||||
from projectinitializer.core import project_creator # type: ignore
|
||||
|
||||
# --- Import Version Info FOR THE WRAPPER ITSELF ---
|
||||
try:
|
||||
# Use absolute import based on package name
|
||||
from projectinitializer import _version as wrapper_version
|
||||
WRAPPER_APP_VERSION_STRING = f"{wrapper_version.__version__} ({wrapper_version.GIT_BRANCH}/{wrapper_version.GIT_COMMIT_HASH[:7]})"
|
||||
WRAPPER_BUILD_INFO = f"Wrapper Built: {wrapper_version.BUILD_TIMESTAMP}"
|
||||
except ImportError:
|
||||
# This might happen if you run the wrapper directly from source
|
||||
# without generating its _version.py first (if you use that approach for the wrapper itself)
|
||||
WRAPPER_APP_VERSION_STRING = "(Dev Wrapper)"
|
||||
WRAPPER_BUILD_INFO = "Wrapper build time unknown"
|
||||
# --- End Import Version Info ---
|
||||
|
||||
# --- Constants for Version Generation ---
|
||||
DEFAULT_VERSION = "0.0.0+unknown"
|
||||
DEFAULT_COMMIT = "Unknown"
|
||||
DEFAULT_BRANCH = "Unknown"
|
||||
# --- End Constants ---
|
||||
|
||||
class AppWindow:
|
||||
def __init__(self, master: tk.Tk):
|
||||
self.master = master
|
||||
master.title("Project Initializer Tool")
|
||||
master.title(f"Project Initializer Tool - {WRAPPER_APP_VERSION_STRING}")
|
||||
# master.geometry("500x200") # Optional: set a default size
|
||||
|
||||
self.app_config = settings.load_app_configuration()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user