148 lines
6.5 KiB
Python
148 lines
6.5 KiB
Python
# 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
|
|
|
|
# --- 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(f"Project Initializer Tool - {WRAPPER_APP_VERSION_STRING}")
|
|
# 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() |