Chore: Stop tracking files based on .gitignore update.
Untracked files matching the following rules: - Rule "*.pyc": 10 files
This commit is contained in:
commit
dc0d4b18d1
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
.svn
|
||||||
|
*.pyc
|
||||||
|
_dist/
|
||||||
|
_build/
|
||||||
15
.vscode/launch.json
vendored
Normal file
15
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Python Debugger: Module",
|
||||||
|
"type": "debugpy",
|
||||||
|
"request": "launch",
|
||||||
|
"module": "projectinitializer"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
74
README.md
Normal file
74
README.md
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
# Project Initializer Tool
|
||||||
|
|
||||||
|
A Python application to initialize standard project structures for new Python projects,
|
||||||
|
suitable for use with Git.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Creates a defined directory structure for source code (`project_name/core`, `project_name/gui`).
|
||||||
|
- Generates basic files: `__init__.py`, `__main__.py`, `core.py`, `gui.py`.
|
||||||
|
- Creates a `doc/` directory with template `English-manual.md` and `Italian-manual.md`.
|
||||||
|
- Generates a basic `README.md` for the new project.
|
||||||
|
- Generates a `.spec` file template for PyInstaller.
|
||||||
|
- Copies a default `.ico` file.
|
||||||
|
- Creates a standard `.gitignore` file.
|
||||||
|
- Initializes a local Git repository with an initial commit.
|
||||||
|
- Supports both Command Line Interface (CLI) and Graphical User Interface (GUI) via Tkinter.
|
||||||
|
- Remembers the last used root directory for convenience in GUI mode.
|
||||||
|
|
||||||
|
## Structure of this Tool (`ProjectInitializerTool`)
|
||||||
|
|
||||||
|
This tool itself is organized as follows:
|
||||||
|
|
||||||
|
- `project_initializer/`: Main Python package for the tool.
|
||||||
|
- `__main__.py`: Entry point (`python -m project_initializer`).
|
||||||
|
- `core/`: Core logic for project creation.
|
||||||
|
- `project_creator.py`: Handles the actual file and directory generation.
|
||||||
|
- `config/`: Configuration and file templates.
|
||||||
|
- `settings.py`: Manages app config (like last used directory) and stores file templates.
|
||||||
|
- `gui/`: Tkinter GUI components.
|
||||||
|
- `app_window.py`: Defines the main application window.
|
||||||
|
- `cli/`: Command-line interface components.
|
||||||
|
- `interface.py`: Handles argument parsing and CLI interaction.
|
||||||
|
- `assets/`: Static assets for the tool.
|
||||||
|
- `default_icon.ico`: The default icon copied to new projects.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
- Python 3.7+ (ideally 3.8+ for `pathlib` features and type hinting usage)
|
||||||
|
- Git (optional, for repository initialization in new projects)
|
||||||
|
|
||||||
|
### Running the Tool
|
||||||
|
|
||||||
|
1. Navigate to the `ProjectInitializerTool` directory (the one containing this README).
|
||||||
|
2. Execute the tool using the Python module execution:
|
||||||
|
|
||||||
|
**GUI Mode (Recommended for interactive use):**
|
||||||
|
```bash
|
||||||
|
python -m project_initializer
|
||||||
|
```
|
||||||
|
This will launch the graphical interface.
|
||||||
|
|
||||||
|
**CLI Mode (For scripting or command-line preference):**
|
||||||
|
```bash
|
||||||
|
python -m project_initializer <root_directory> <ProjectName>
|
||||||
|
```
|
||||||
|
- `<root_directory>`: The directory where the new project folder (named `<ProjectName>`) will be created.
|
||||||
|
- `<ProjectName>`: The desired name for your new project (e.g., "MyNewApp").
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```bash
|
||||||
|
python -m project_initializer /home/user/dev/projects MyCoolProject
|
||||||
|
```
|
||||||
|
|
||||||
|
This will create a folder `/home/user/dev/projects/MyCoolProject` with the standard structure.
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
The tool saves the last used root directory in a configuration file (`.project_initializer_config.json`) in your user's home directory.
|
||||||
|
|
||||||
|
## Development (of this `ProjectInitializerTool`)
|
||||||
|
|
||||||
|
- Ensure you have Python installed.
|
||||||
|
- The `default_icon.ico` file should be present in `project_initializer/assets/`.
|
||||||
|
- To run during development: `python -m project_initializer` from the `ProjectInitializerTool` directory.
|
||||||
0
projectinitializer/__init__.py
Normal file
0
projectinitializer/__init__.py
Normal file
124
projectinitializer/__main__.py
Normal file
124
projectinitializer/__main__.py
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
# ProjectInitializerTool/project_initializer/__main__.py
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import argparse
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Per lanciare il tool, la directory ProjectInitializerTool deve essere nel PYTHONPATH
|
||||||
|
# o si deve essere in ProjectInitializerTool/ e lanciare python -m project_initializer
|
||||||
|
# Questo permette gli import relativi corretti.
|
||||||
|
from projectinitializer.gui import app_window # type: ignore
|
||||||
|
from projectinitializer.cli import interface # type: ignore
|
||||||
|
from projectinitializer.config import settings # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_default_icon_exists():
|
||||||
|
"""
|
||||||
|
Checks if the default icon exists in the package's assets.
|
||||||
|
Creates a dummy one if not, for development convenience of the tool itself.
|
||||||
|
This helps prevent errors if the asset is accidentally deleted during development.
|
||||||
|
"""
|
||||||
|
icon_path = settings.DEFAULT_ICON_PATH
|
||||||
|
if not icon_path.exists():
|
||||||
|
print(f"Warning: Default icon '{icon_path}' not found in package assets.", file=sys.stderr)
|
||||||
|
print(f"Attempting to create a dummy empty icon file at that location.", file=sys.stderr)
|
||||||
|
try:
|
||||||
|
icon_path.parent.mkdir(parents=True, exist_ok=True) # Ensure assets directory exists
|
||||||
|
icon_path.touch()
|
||||||
|
print(f"Dummy '{settings.DEFAULT_ICON_FILE_NAME}' created at '{icon_path}'. "
|
||||||
|
"Please replace it with a real .ico file for the tool to function correctly.", file=sys.stderr)
|
||||||
|
except OSError as e:
|
||||||
|
print(f"Critical Error: Could not create dummy icon at '{icon_path}': {e}. "
|
||||||
|
"The tool might not be able to copy the icon to new projects.", file=sys.stderr)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""
|
||||||
|
Main entry point for the Project Initializer tool.
|
||||||
|
Determines whether to run in CLI or GUI mode.
|
||||||
|
"""
|
||||||
|
# Ensure the default icon asset is available for copying
|
||||||
|
ensure_default_icon_exists()
|
||||||
|
|
||||||
|
# Argument parser to decide mode (and for CLI args if that's the mode)
|
||||||
|
# We use a basic parser here just to check for presence of CLI-specific args
|
||||||
|
# or a potential --gui / --cli flag if we add one.
|
||||||
|
# The full CLI parsing is delegated to cli.interface.
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Python Project Initializer Tool.",
|
||||||
|
add_help=False # We'll add help in a context-specific way or let sub-parsers do it
|
||||||
|
)
|
||||||
|
# These arguments are for the CLI mode. If they are present, we assume CLI.
|
||||||
|
# nargs='?' makes them optional for this initial parse, so GUI mode doesn't fail.
|
||||||
|
parser.add_argument("root_directory", nargs="?", help=argparse.SUPPRESS) # Suppress from top-level help
|
||||||
|
parser.add_argument("project_name", nargs="?", help=argparse.SUPPRESS) # Suppress from top-level help
|
||||||
|
|
||||||
|
# Optional explicit mode flags (could be useful in future)
|
||||||
|
# mode_group = parser.add_mutually_exclusive_group()
|
||||||
|
# mode_group.add_argument("--cli", action="store_true", help="Force Command Line Interface mode.")
|
||||||
|
# mode_group.add_argument("--gui", action="store_true", help="Force Graphical User Interface mode.")
|
||||||
|
|
||||||
|
# Parse known arguments without exiting on error for missing positional ones yet.
|
||||||
|
# This allows us to check if any arguments were passed that might indicate CLI intent.
|
||||||
|
# All arguments including script name are in sys.argv
|
||||||
|
|
||||||
|
# Heuristic: if there are arguments beyond the script name itself,
|
||||||
|
# and they are not flags like --help (which cli.interface would handle),
|
||||||
|
# it's likely an attempt to run CLI.
|
||||||
|
# A very simple check: if sys.argv has more than 1 element (script name + something else)
|
||||||
|
# A slightly better one: check if root_directory and project_name are likely provided.
|
||||||
|
|
||||||
|
# If sys.argv has more than 1 element (meaning some args were passed)
|
||||||
|
# AND those arguments are not specific GUI launch flags (if we add them later)
|
||||||
|
# then, we assume it's for the CLI. Otherwise, default to GUI.
|
||||||
|
|
||||||
|
# Let's try parsing the arguments. If root_directory and project_name are provided,
|
||||||
|
# it's CLI mode. Otherwise, it's GUI. This relies on argparse's behavior.
|
||||||
|
# sys.argv[0] is script name or -m module_name
|
||||||
|
|
||||||
|
# If any arguments are passed (beyond script name), assume CLI intent.
|
||||||
|
# The cli.interface.handle_cli_invocation will perform full parsing and error handling.
|
||||||
|
# If no arguments are passed, launch GUI.
|
||||||
|
# This is a simplification. A --cli or --gui flag would be more robust.
|
||||||
|
|
||||||
|
# Check if sys.argv contains arguments that are likely positional arguments for CLI
|
||||||
|
# sys.argv = ['project_initializer/__main__.py', 'arg1', 'arg2'] for `python -m project_initializer arg1 arg2`
|
||||||
|
# sys.argv = ['script.py'] for `python script.py`
|
||||||
|
|
||||||
|
# A common pattern for this is to try to parse, and if specific args are found, run one mode.
|
||||||
|
# For simplicity now: if arguments are passed, assume CLI. cli.interface will validate them.
|
||||||
|
# If no arguments are passed (only script name), assume GUI.
|
||||||
|
|
||||||
|
args_to_parse = sys.argv[1:]
|
||||||
|
|
||||||
|
# Heuristic: if the first argument doesn't start with '-' and there are two arguments,
|
||||||
|
# it's highly probable they are the positional 'root_directory' and 'project_name'.
|
||||||
|
# This avoids accidentally going to CLI if someone types `python -m project_initializer --some-unknown-gui-flag`.
|
||||||
|
is_cli_intent = False
|
||||||
|
if len(args_to_parse) >= 2:
|
||||||
|
# Check if the first two args are likely our positional CLI args
|
||||||
|
# (i.e., they don't look like options/flags)
|
||||||
|
if not args_to_parse[0].startswith('-') and not args_to_parse[1].startswith('-'):
|
||||||
|
is_cli_intent = True
|
||||||
|
elif len(args_to_parse) == 1 and (args_to_parse[0] == "--help" or args_to_parse[0] == "-h"):
|
||||||
|
# If only help is requested, let CLI handler show its help message
|
||||||
|
is_cli_intent = True
|
||||||
|
|
||||||
|
|
||||||
|
if is_cli_intent:
|
||||||
|
# Pass all arguments (sys.argv[1:]) to the CLI handler
|
||||||
|
interface.handle_cli_invocation(args_to_parse)
|
||||||
|
else:
|
||||||
|
# No arguments or arguments that don't match CLI pattern, launch GUI
|
||||||
|
if args_to_parse and not (len(args_to_parse) == 1 and (args_to_parse[0] == "--help" or args_to_parse[0] == "-h")):
|
||||||
|
# Arguments were passed that didn't fit the CLI pattern,
|
||||||
|
# and it wasn't a simple help request. Show a warning/help.
|
||||||
|
print("Info: Unrecognized arguments for CLI mode. Defaulting to GUI mode.", file=sys.stdout)
|
||||||
|
print("For CLI, provide: <root_directory> <project_name>", file=sys.stdout)
|
||||||
|
print("Example: python -m project_initializer /path/to/projects MyNewApp\n", file=sys.stdout)
|
||||||
|
|
||||||
|
print("Launching GUI mode...") # Useful feedback if launched from terminal
|
||||||
|
app_window.launch_gui_application()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
0
projectinitializer/assets/default_icon.ico
Normal file
0
projectinitializer/assets/default_icon.ico
Normal file
0
projectinitializer/cli/__init__.py
Normal file
0
projectinitializer/cli/__init__.py
Normal file
70
projectinitializer/cli/interface.py
Normal file
70
projectinitializer/cli/interface.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# ProjectInitializerTool/project_initializer/cli/interface.py
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
from projectinitializer.core import project_creator # type: ignore
|
||||||
|
# Settings non è direttamente usato qui, ma il core sì
|
||||||
|
# from project_initializer.config import settings
|
||||||
|
|
||||||
|
def handle_cli_invocation(cli_args: list[str] | None = None) -> None:
|
||||||
|
"""
|
||||||
|
Parses command-line arguments and triggers project creation.
|
||||||
|
Manages CLI-specific output and error handling.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cli_args (list[str] | None, optional): A list of command line arguments
|
||||||
|
(e.g. from sys.argv[1:]).
|
||||||
|
If None, sys.argv[1:] is used.
|
||||||
|
"""
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Python Project Initializer Tool (CLI Mode). "
|
||||||
|
"Creates a standard Python project structure."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"root_directory",
|
||||||
|
type=str,
|
||||||
|
help="The absolute or relative root directory where the new project folder will be created."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"project_name",
|
||||||
|
type=str,
|
||||||
|
help="The name of the new project (e.g., 'MyAwesomeProject'). "
|
||||||
|
"This will be used for the main project folder and, in a sanitized form, "
|
||||||
|
"for the Python package name."
|
||||||
|
)
|
||||||
|
# Aggiungere un flag per forzare la modalità CLI se __main__.py diventa più complesso
|
||||||
|
# parser.add_argument("--cli", action="store_true", help="Force CLI mode if auto-detection is ambiguous.")
|
||||||
|
|
||||||
|
if cli_args is None:
|
||||||
|
args = parser.parse_args() # Uses sys.argv[1:] by default
|
||||||
|
else:
|
||||||
|
args = parser.parse_args(cli_args)
|
||||||
|
|
||||||
|
|
||||||
|
root_dir_path = Path(args.root_directory).resolve() # Resolve to absolute path for clarity
|
||||||
|
project_name_str = args.project_name.strip()
|
||||||
|
|
||||||
|
if not project_name_str:
|
||||||
|
print("Error: Project name cannot be empty.", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# La validazione di root_dir_path (se esiste ed è una directory)
|
||||||
|
# e la validazione più approfondita di project_name (es. isidentifier)
|
||||||
|
# sono gestite dalla funzione project_creator.create_project.
|
||||||
|
# Qui facciamo solo controlli di base sugli argomenti forniti.
|
||||||
|
|
||||||
|
try:
|
||||||
|
print(f"Attempting to create project '{project_name_str}' in '{root_dir_path}'...")
|
||||||
|
success_message = project_creator.create_project(str(root_dir_path), project_name_str)
|
||||||
|
# Il core creator stampa già i suoi messaggi di info/successo.
|
||||||
|
# Potremmo voler sopprimere quelli e stampare solo il messaggio finale qui,
|
||||||
|
# ma per ora va bene così.
|
||||||
|
# print(f"\n{success_message}") # Già stampato dal core.
|
||||||
|
sys.exit(0) # Success
|
||||||
|
except project_creator.ProjectCreationError as e:
|
||||||
|
print(f"Error: Project creation failed: {e}", file=sys.stderr)
|
||||||
|
sys.exit(1) # Failure
|
||||||
|
except Exception as e: # Catch-all for other unexpected errors from core or here
|
||||||
|
print(f"Error: An unexpected error occurred: {e}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
0
projectinitializer/config/__init__.py
Normal file
0
projectinitializer/config/__init__.py
Normal file
353
projectinitializer/config/settings.py
Normal file
353
projectinitializer/config/settings.py
Normal file
@ -0,0 +1,353 @@
|
|||||||
|
# ProjectInitializerTool/project_initializer/config/settings.py
|
||||||
|
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
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/
|
||||||
|
|
||||||
|
# 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 ---
|
||||||
|
|
||||||
|
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:
|
||||||
|
with open(config_path, "r", encoding="utf-8") as f:
|
||||||
|
return json.load(f)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
print(f"Warning: Configuration file {config_path} is corrupted. Using defaults.")
|
||||||
|
return {}
|
||||||
|
except IOError:
|
||||||
|
print(f"Warning: Could not read configuration file {config_path}. Using defaults.")
|
||||||
|
return {}
|
||||||
|
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:
|
||||||
|
json.dump(config_data, f, indent=4)
|
||||||
|
except IOError:
|
||||||
|
print(f"Error: Could not write configuration file {config_path}.")
|
||||||
|
|
||||||
|
# --- File Templates ---
|
||||||
|
|
||||||
|
def get_gitignore_template() -> str:
|
||||||
|
"""Returns the .gitignore template string."""
|
||||||
|
return """# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
pip-wheel-metadata/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a CI server in a temp folder.
|
||||||
|
# Then everything is copied to shipping folder during release.
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
.python-version
|
||||||
|
|
||||||
|
# PEP 582; __pypackages__
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# PEP 621; pyproject.toml sections
|
||||||
|
.pdm.toml
|
||||||
|
.pdm.lock
|
||||||
|
# .venv
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# static analysis tool
|
||||||
|
.flake8
|
||||||
|
|
||||||
|
# VS Code
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.history/
|
||||||
|
|
||||||
|
# sublime
|
||||||
|
*.sublime-workspace
|
||||||
|
*.sublime-project
|
||||||
|
|
||||||
|
# Kate
|
||||||
|
.kateproject
|
||||||
|
.kateproject.lock
|
||||||
|
.katenewfile. Neuen Filenamensvorschlag merken.
|
||||||
|
|
||||||
|
# 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."""
|
||||||
|
return f"""# {project_name_original}
|
||||||
|
|
||||||
|
A brief description of {project_name_original}.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
- Feature 1
|
||||||
|
- Feature 2
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
...
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
...
|
||||||
|
|
||||||
|
## License
|
||||||
|
...
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get_main_py_template(project_name_original: str, project_name_lower: str) -> str:
|
||||||
|
"""Returns the __main__.py template string for the new project."""
|
||||||
|
return f"""# {project_name_lower}/__main__.py
|
||||||
|
|
||||||
|
# Example import assuming your main logic is in a 'main' function
|
||||||
|
# within a 'app' module in your '{project_name_lower}.core' package.
|
||||||
|
# from {project_name_lower}.core.app import main as start_application
|
||||||
|
#
|
||||||
|
# Or, if you have a function in {project_name_lower}.core.core:
|
||||||
|
# from {project_name_lower}.core.core import main_function
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print(f"Running {project_name_original}...")
|
||||||
|
# Placeholder: Replace with your application's entry point
|
||||||
|
# Example: start_application()
|
||||||
|
print("To customize, edit '{project_name_lower}/__main__.py' and your core modules.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get_spec_file_template(project_name_original: str, project_name_lower: 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
|
||||||
|
binaries=[],
|
||||||
|
datas=[('{DEFAULT_ICON_FILE_NAME}', '.')], # Icon file relative to project root
|
||||||
|
hiddenimports=[],
|
||||||
|
hookspath=[],
|
||||||
|
runtime_hooks=[],
|
||||||
|
excludes=[],
|
||||||
|
win_no_prefer_redirects=False,
|
||||||
|
win_private_assemblies=False,
|
||||||
|
cipher=block_cipher,
|
||||||
|
noarchive=False
|
||||||
|
)
|
||||||
|
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||||
|
|
||||||
|
exe = EXE(
|
||||||
|
pyz,
|
||||||
|
a.scripts,
|
||||||
|
a.binaries,
|
||||||
|
a.zipfiles,
|
||||||
|
a.datas,
|
||||||
|
[],
|
||||||
|
name='{project_name_original}',
|
||||||
|
debug=False,
|
||||||
|
bootloader_ignore_signals=False,
|
||||||
|
strip=False,
|
||||||
|
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
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get_english_manual_template(project_name_original: str) -> str:
|
||||||
|
"""Returns the English manual template string."""
|
||||||
|
return f"""# {project_name_original} - English Manual
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
Welcome to {project_name_original}. This document provides an overview of how to install, use, and understand the project.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
Describe the installation steps here. For example:
|
||||||
|
1. Clone the repository: `git clone <repository_url>`
|
||||||
|
2. Navigate to the project directory: `cd {project_name_original}`
|
||||||
|
3. Install dependencies: `pip install -r requirements.txt` (if applicable)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
Explain how to run and use the application.
|
||||||
|
- To run the application: `python -m {project_name_original.lower().replace(" ", "_").replace("-", "_")}`
|
||||||
|
- Command-line arguments (if any).
|
||||||
|
- GUI interaction (if any).
|
||||||
|
|
||||||
|
## Development
|
||||||
|
Information for developers contributing to the project.
|
||||||
|
- Code structure.
|
||||||
|
- How to run tests.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
Common issues and their solutions.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get_italian_manual_template(project_name_original: str) -> str:
|
||||||
|
"""Returns the Italian manual template string."""
|
||||||
|
return f"""# {project_name_original} - Manuale Italiano
|
||||||
|
|
||||||
|
## Introduzione
|
||||||
|
Benvenuto in {project_name_original}. Questo documento fornisce una panoramica su come installare, utilizzare e comprendere il progetto.
|
||||||
|
|
||||||
|
## Installazione
|
||||||
|
Descrivi i passaggi di installazione qui. Ad esempio:
|
||||||
|
1. Clona il repository: `git clone <repository_url>`
|
||||||
|
2. Naviga nella directory del progetto: `cd {project_name_original}`
|
||||||
|
3. Installa le dipendenze: `pip install -r requirements.txt` (se applicabile)
|
||||||
|
|
||||||
|
## Utilizzo
|
||||||
|
Spiega come eseguire e utilizzare l'applicazione.
|
||||||
|
- Per eseguire l'applicazione: `python -m {project_name_original.lower().replace(" ", "_").replace("-", "_")}`
|
||||||
|
- Argomenti da riga di comando (se presenti).
|
||||||
|
- Interazione con la GUI (se presente).
|
||||||
|
|
||||||
|
## Sviluppo
|
||||||
|
Informazioni per gli sviluppatori che contribuiscono al progetto.
|
||||||
|
- Struttura del codice.
|
||||||
|
- Come eseguire i test.
|
||||||
|
|
||||||
|
## Risoluzione dei problemi
|
||||||
|
Problemi comuni e relative soluzioni.
|
||||||
|
"""
|
||||||
0
projectinitializer/core/__init__.py
Normal file
0
projectinitializer/core/__init__.py
Normal file
195
projectinitializer/core/project_creator.py
Normal file
195
projectinitializer/core/project_creator.py
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
# ProjectInitializerTool/project_initializer/core/project_creator.py
|
||||||
|
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
# Importa i settings per accedere ai template e al percorso dell'icona
|
||||||
|
from projectinitializer.config import settings # type: ignore
|
||||||
|
|
||||||
|
# Tkinter messagebox è specifico della GUI.
|
||||||
|
# Per il core, dovremmo restituire successo/fallimento e messaggi,
|
||||||
|
# lasciando che sia il chiamante (GUI o CLI) a visualizzarli.
|
||||||
|
# Tuttavia, per mantenere una certa familiarità con il codice precedente
|
||||||
|
# e dato che l'utente potrebbe voler vedere feedback immediato anche se chiamato
|
||||||
|
# da una GUI che poi mostra un suo popup, lo lascio per ora,
|
||||||
|
# ma idealmente questo modulo non dovrebbe dipendere da tkinter.
|
||||||
|
# Una soluzione migliore sarebbe un sistema di logging o callback.
|
||||||
|
# Per semplicità, al momento, se la GUI è in uso, i messaggi appariranno
|
||||||
|
# anche dalla console se la GUI non sopprime stdout.
|
||||||
|
# Se la GUI non è in uso (CLI), print() è l'output atteso.
|
||||||
|
# Rimuoviamo la dipendenza diretta da messagebox per il core.
|
||||||
|
# Il chiamante (GUI/CLI) gestirà la notifica utente.
|
||||||
|
|
||||||
|
class ProjectCreationError(Exception):
|
||||||
|
"""Custom exception for errors during project creation."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def create_project(root_directory_str: str, project_name_original: str) -> str:
|
||||||
|
"""
|
||||||
|
Creates the project directory structure and initial files.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
root_directory_str (str): The root directory where the project folder will be created.
|
||||||
|
project_name_original (str): The name of the project (can have mixed case).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: A success message.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ProjectCreationError: If any error occurs during project creation.
|
||||||
|
"""
|
||||||
|
root_directory = Path(root_directory_str)
|
||||||
|
if not root_directory.is_dir():
|
||||||
|
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)
|
||||||
|
if not project_name_lower.isidentifier():
|
||||||
|
raise ProjectCreationError(
|
||||||
|
f"The sanitized project name '{project_name_lower}' (derived from '{project_name_original}') "
|
||||||
|
"is not a valid Python identifier. Please use letters, numbers, and underscores, "
|
||||||
|
"not starting with a number."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if project_root_path.exists():
|
||||||
|
raise ProjectCreationError(f"Project directory '{project_root_path}' already exists.")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 1) Create main project folder
|
||||||
|
project_root_path.mkdir(parents=True, exist_ok=False)
|
||||||
|
print(f"Info: Created directory: {project_root_path}")
|
||||||
|
|
||||||
|
# 2) Create source code subfolder (package name)
|
||||||
|
src_package_path = project_root_path / project_name_lower
|
||||||
|
src_package_path.mkdir()
|
||||||
|
print(f"Info: Created directory: {src_package_path}")
|
||||||
|
|
||||||
|
# 3) Create source subdirectories and __init__.py files for the new project
|
||||||
|
core_subpath = src_package_path / "core"
|
||||||
|
core_subpath.mkdir()
|
||||||
|
print(f"Info: Created directory: {core_subpath}")
|
||||||
|
(core_subpath / "__init__.py").touch()
|
||||||
|
(core_subpath / "core.py").touch() # Empty core logic file
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
(src_package_path / "__init__.py").touch() # Make project_name_lower a package
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
print(f"Info: Created file: {src_package_path / '__main__.py'}")
|
||||||
|
|
||||||
|
# 4) Create 'doc' folder and manual files
|
||||||
|
doc_path = project_root_path / "doc"
|
||||||
|
doc_path.mkdir()
|
||||||
|
print(f"Info: Created directory: {doc_path}")
|
||||||
|
|
||||||
|
english_manual_content = settings.get_english_manual_template(project_name_original)
|
||||||
|
with open(doc_path / "English-manual.md", "w", encoding="utf-8") as f:
|
||||||
|
f.write(english_manual_content)
|
||||||
|
print(f"Info: Created file: {doc_path / 'English-manual.md'}")
|
||||||
|
|
||||||
|
italian_manual_content = settings.get_italian_manual_template(project_name_original)
|
||||||
|
with open(doc_path / "Italian-manual.md", "w", encoding="utf-8") as f:
|
||||||
|
f.write(italian_manual_content)
|
||||||
|
print(f"Info: Created file: {doc_path / 'Italian-manual.md'}")
|
||||||
|
|
||||||
|
# 5) Create README.md for the new project
|
||||||
|
readme_content = settings.get_readme_template(project_name_original)
|
||||||
|
with open(project_root_path / "README.md", "w", encoding="utf-8") as f:
|
||||||
|
f.write(readme_content)
|
||||||
|
print(f"Info: Created file: {project_root_path / 'README.md'}")
|
||||||
|
|
||||||
|
# 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
|
||||||
|
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
|
||||||
|
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}")
|
||||||
|
|
||||||
|
|
||||||
|
# 8) Create .gitignore for the new project
|
||||||
|
gitignore_content = settings.get_gitignore_template()
|
||||||
|
with open(project_root_path / ".gitignore", "w", encoding="utf-8") as f:
|
||||||
|
f.write(gitignore_content)
|
||||||
|
print(f"Info: Created file: {project_root_path / '.gitignore'}")
|
||||||
|
|
||||||
|
# 9) Initialize Git repository
|
||||||
|
try:
|
||||||
|
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(
|
||||||
|
["git", "commit", "-m", "Initial project structure created by ProjectInitializerTool"],
|
||||||
|
cwd=project_root_path, check=True, capture_output=True, text=True
|
||||||
|
)
|
||||||
|
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}"
|
||||||
|
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}"
|
||||||
|
)
|
||||||
|
print(f"\n{success_message}")
|
||||||
|
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():
|
||||||
|
try:
|
||||||
|
shutil.rmtree(project_root_path)
|
||||||
|
print(f"Info: Cleaned up partially created project at {project_root_path}")
|
||||||
|
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
|
||||||
|
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
|
||||||
|
try:
|
||||||
|
shutil.rmtree(project_root_path)
|
||||||
|
print(f"Info: Cleaned up partially created project at {project_root_path} due to unexpected error.")
|
||||||
|
except OSError as cleanup_e:
|
||||||
|
print(f"Error: Error during cleanup of '{project_root_path}': {cleanup_e}")
|
||||||
|
raise ProjectCreationError(error_msg) from e
|
||||||
0
projectinitializer/gui/__init__.py
Normal file
0
projectinitializer/gui/__init__.py
Normal file
129
projectinitializer/gui/app_window.py
Normal file
129
projectinitializer/gui/app_window.py
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
# ProjectInitializerTool/project_initializer/gui/app_window.py
|
||||||
|
|
||||||
|
import tkinter as tk
|
||||||
|
from tkinter import filedialog, messagebox
|
||||||
|
from pathlib import Path
|
||||||
|
from projectinitializer.config import settings # type: ignore
|
||||||
|
from projectinitializer.core import project_creator # type: ignore
|
||||||
|
|
||||||
|
class AppWindow:
|
||||||
|
def __init__(self, master: tk.Tk):
|
||||||
|
self.master = master
|
||||||
|
master.title("Project Initializer Tool")
|
||||||
|
# master.geometry("500x200") # Optional: set a default size
|
||||||
|
|
||||||
|
self.app_config = settings.load_app_configuration()
|
||||||
|
self.last_root_dir = self.app_config.get("last_root_directory", str(Path.home()))
|
||||||
|
|
||||||
|
# --- Widgets ---
|
||||||
|
# Root Directory Frame
|
||||||
|
root_dir_frame = tk.Frame(master, padx=5, pady=5)
|
||||||
|
root_dir_frame.pack(fill=tk.X, expand=False)
|
||||||
|
|
||||||
|
tk.Label(root_dir_frame, text="Root Directory:").pack(side=tk.LEFT, padx=(0, 5))
|
||||||
|
self.root_dir_entry = tk.Entry(root_dir_frame)
|
||||||
|
self.root_dir_entry.pack(side=tk.LEFT, fill=tk.X, expand=True)
|
||||||
|
self.root_dir_entry.insert(0, self.last_root_dir)
|
||||||
|
self.browse_button = tk.Button(root_dir_frame, text="Browse...", command=self._browse_root_directory)
|
||||||
|
self.browse_button.pack(side=tk.LEFT, padx=(5, 0))
|
||||||
|
|
||||||
|
# Project Name Frame
|
||||||
|
project_name_frame = tk.Frame(master, padx=5, pady=5)
|
||||||
|
project_name_frame.pack(fill=tk.X, expand=False)
|
||||||
|
|
||||||
|
tk.Label(project_name_frame, text="Project Name:").pack(side=tk.LEFT, padx=(0, 5))
|
||||||
|
self.project_name_entry = tk.Entry(project_name_frame)
|
||||||
|
self.project_name_entry.pack(side=tk.LEFT, fill=tk.X, expand=True)
|
||||||
|
|
||||||
|
# Ensure project name entry also expands if window is resized
|
||||||
|
project_name_frame.grid_columnconfigure(1, weight=1)
|
||||||
|
|
||||||
|
|
||||||
|
# Proceed Button Frame (for centering or specific layout)
|
||||||
|
button_frame = tk.Frame(master, pady=10)
|
||||||
|
button_frame.pack()
|
||||||
|
|
||||||
|
self.proceed_button = tk.Button(button_frame, text="Create Project", command=self._proceed_action, width=20)
|
||||||
|
self.proceed_button.pack()
|
||||||
|
|
||||||
|
# Set focus to project name entry initially
|
||||||
|
self.project_name_entry.focus_set()
|
||||||
|
|
||||||
|
# Configure resizing behavior for the main window content
|
||||||
|
# Let the entry fields expand with the window width
|
||||||
|
# Not strictly necessary with pack, but good practice for grid or more complex layouts
|
||||||
|
# master.grid_columnconfigure(0, weight=1) # if using grid for frames
|
||||||
|
|
||||||
|
def _browse_root_directory(self) -> None:
|
||||||
|
"""Opens a dialog to select the root directory."""
|
||||||
|
initial_dir = self.root_dir_entry.get() or str(Path.home())
|
||||||
|
if not Path(initial_dir).is_dir(): # check if path is valid, otherwise default to home
|
||||||
|
initial_dir = str(Path.home())
|
||||||
|
|
||||||
|
directory = filedialog.askdirectory(
|
||||||
|
initialdir=initial_dir,
|
||||||
|
title="Select Root Directory"
|
||||||
|
)
|
||||||
|
if directory: # If a directory is selected (not cancelled)
|
||||||
|
self.root_dir_entry.delete(0, tk.END)
|
||||||
|
self.root_dir_entry.insert(0, directory)
|
||||||
|
|
||||||
|
def _validate_inputs(self) -> bool:
|
||||||
|
"""Validates user inputs from the GUI."""
|
||||||
|
self.root_dir = self.root_dir_entry.get().strip()
|
||||||
|
self.project_name = self.project_name_entry.get().strip()
|
||||||
|
|
||||||
|
if not self.root_dir:
|
||||||
|
messagebox.showerror("Input Error", "Root directory cannot be empty.")
|
||||||
|
self.root_dir_entry.focus_set()
|
||||||
|
return False
|
||||||
|
if not Path(self.root_dir).is_dir():
|
||||||
|
messagebox.showerror("Input Error", "The selected root directory is not a valid directory.")
|
||||||
|
self.root_dir_entry.focus_set()
|
||||||
|
return False
|
||||||
|
if not self.project_name:
|
||||||
|
messagebox.showerror("Input Error", "Project name cannot be empty.")
|
||||||
|
self.project_name_entry.focus_set()
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Basic check for project name characters.
|
||||||
|
# More complex validation (like isidentifier for the sanitized version)
|
||||||
|
# is handled in the core, but a simple GUI check can be useful.
|
||||||
|
# For now, we rely on the core's validation.
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _proceed_action(self) -> None:
|
||||||
|
"""Handles the 'Create Project' button click."""
|
||||||
|
if not self._validate_inputs():
|
||||||
|
return
|
||||||
|
|
||||||
|
# Save current root_dir for next time if it's valid and different
|
||||||
|
if Path(self.root_dir).is_dir() and self.root_dir != self.last_root_dir:
|
||||||
|
self.app_config["last_root_directory"] = self.root_dir
|
||||||
|
settings.save_app_configuration(self.app_config)
|
||||||
|
self.last_root_dir = self.root_dir # Update internal state
|
||||||
|
|
||||||
|
self.proceed_button.config(state=tk.DISABLED)
|
||||||
|
self.master.update_idletasks() # Ensure button state updates immediately
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Call the core logic
|
||||||
|
success_message = project_creator.create_project(self.root_dir, self.project_name)
|
||||||
|
messagebox.showinfo("Success", success_message)
|
||||||
|
# Optionally clear fields or perform other actions on success
|
||||||
|
self.project_name_entry.delete(0, tk.END) # Clear project name for the next one
|
||||||
|
self.project_name_entry.focus_set()
|
||||||
|
except project_creator.ProjectCreationError as e:
|
||||||
|
messagebox.showerror("Project Creation Failed", str(e))
|
||||||
|
except Exception as e: # Catch any other unexpected errors
|
||||||
|
messagebox.showerror("Unexpected Error", f"An unexpected error occurred: {str(e)}")
|
||||||
|
finally:
|
||||||
|
# Re-enable button
|
||||||
|
self.proceed_button.config(state=tk.NORMAL)
|
||||||
|
|
||||||
|
def launch_gui_application() -> None:
|
||||||
|
"""Initializes and runs the Tkinter GUI application."""
|
||||||
|
root = tk.Tk()
|
||||||
|
app = AppWindow(root)
|
||||||
|
root.mainloop()
|
||||||
0
requirements.txt
Normal file
0
requirements.txt
Normal file
Loading…
Reference in New Issue
Block a user