213 lines
9.3 KiB
Python
213 lines
9.3 KiB
Python
# File: pyinstallerguiwrapper/gui/project_selector.py
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
Manages project directory selection and derives key paths for the PyInstaller GUI Wrapper.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import pathlib
|
|
import tkinter as tk
|
|
from tkinter import filedialog, ttk, messagebox
|
|
from typing import Optional, Callable, Dict, Any, Tuple, Union
|
|
|
|
# Importamos config desde el paquete principal, no desde gui/
|
|
from pyinstallerguiwrapper import config
|
|
|
|
|
|
# Definizione di un logger di default per ProjectManager se non ne viene passato uno
|
|
def _default_logger(message: str, level: str = "INFO") -> None:
|
|
"""Default logger for ProjectManager if none is provided."""
|
|
# Sostituire con un vero logger di logging.Logger se necessario
|
|
# print(f"[{level}] ProjectManager: {message}")
|
|
pass
|
|
|
|
|
|
class ProjectManager:
|
|
"""
|
|
Handles the selection of the main project directory and derivation of key paths
|
|
(main script, .spec file, icon). Manages associated Tkinter variables and GUI labels.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
parent_gui: tk.Tk, # Riferimento alla finestra principale per i dialoghi
|
|
project_dir_var: tk.StringVar,
|
|
derived_script_label_val: ttk.Label,
|
|
derived_spec_label_val: ttk.Label,
|
|
derived_icon_label_val: ttk.Label,
|
|
icon_path_var: tk.StringVar, # La variabile Tkinter per il path dell'icona (che viene auto-riempita)
|
|
logger_func: Optional[Callable[..., None]] = None,
|
|
on_project_selected_callback: Optional[
|
|
Callable[[str, Dict[str, Any]], None]
|
|
] = None,
|
|
):
|
|
"""
|
|
Initializes the ProjectManager.
|
|
|
|
Args:
|
|
parent_gui: The main Tkinter window (for dialogs).
|
|
project_dir_var: The StringVar for the selected project directory.
|
|
derived_script_label_val: The Label widget for displaying the main script path.
|
|
derived_spec_label_val: The Label widget for displaying the .spec file path.
|
|
derived_icon_label_val: The Label widget for displaying the icon path.
|
|
icon_path_var: The StringVar for the user-selected icon path (auto-filled on project select).
|
|
logger_func: A function to use for logging messages.
|
|
on_project_selected_callback: A callback function (project_root_str: str, derived_paths: Dict[str, Any]) -> None
|
|
to be called after a project is successfully selected and paths derived.
|
|
"""
|
|
self.parent_gui = parent_gui
|
|
self.project_directory_path = project_dir_var
|
|
self.derived_script_label_val = derived_script_label_val
|
|
self.derived_spec_label_val = derived_spec_label_val
|
|
self.derived_icon_label_val = derived_icon_label_val
|
|
self.icon_path_var = icon_path_var
|
|
self.logger = logger_func if logger_func else _default_logger
|
|
self.on_project_selected_callback = on_project_selected_callback
|
|
|
|
# Store derived paths as attributes for easy access within the class
|
|
self.derived_main_script_path: Optional[str] = None
|
|
self.derived_spec_path: Optional[str] = None
|
|
self.derived_icon_path: Optional[str] = None
|
|
self.derived_source_dir_path: Optional[path.Path] = None # pathlib.Path object
|
|
self.project_root_name: Optional[str] = None
|
|
|
|
def _log(self, message: str, level: str = "INFO") -> None:
|
|
"""Helper for logging messages with a consistent prefix."""
|
|
try:
|
|
self.logger(f"[ProjectManager] {message}", level=level)
|
|
except TypeError:
|
|
self.logger(f"[{level}][ProjectManager] {message}")
|
|
|
|
def select_project_directory(self) -> None:
|
|
"""
|
|
Opens a file dialog for the user to select the main project directory.
|
|
Derives and updates paths for the main script, .spec file, and icon.
|
|
"""
|
|
self._log(
|
|
"=" * 20 + " PROJECT DIRECTORY SELECTION STARTED " + "=" * 20, level="INFO"
|
|
)
|
|
dir_path = filedialog.askdirectory(
|
|
title="Select Main Project Directory", parent=self.parent_gui
|
|
)
|
|
if not dir_path:
|
|
self._log("Directory selection cancelled.", level="INFO")
|
|
return
|
|
|
|
self.project_directory_path.set(dir_path)
|
|
self._log(f"Selected project directory: {dir_path}", level="INFO")
|
|
|
|
project_root = pathlib.Path(dir_path)
|
|
self.project_root_name = project_root.name
|
|
project_name_lower = self.project_root_name.lower()
|
|
|
|
# Derived paths as strings for direct assignment to Tkinter variables/attributes
|
|
self.derived_source_dir_path = project_root / project_name_lower
|
|
self.derived_main_script_path = str(
|
|
self.derived_source_dir_path / "__main__.py"
|
|
)
|
|
self.derived_spec_path = str(project_root / f"{project_name_lower}.spec")
|
|
|
|
# Default icon filename based on platform
|
|
default_icon_filename = f"{project_name_lower}.ico"
|
|
if sys.platform == "darwin":
|
|
default_icon_filename = f"{project_name_lower}.icns"
|
|
elif sys.platform != "win32":
|
|
default_icon_filename = f"{project_name_lower}.png"
|
|
self.derived_icon_path = str(project_root / default_icon_filename)
|
|
|
|
self._log(f"Derived project name: {self.project_root_name}", level="DEBUG")
|
|
self._log(
|
|
f" Expected source path: {self.derived_source_dir_path}", level="DEBUG"
|
|
)
|
|
self._log(
|
|
f" Expected main script: {self.derived_main_script_path}", level="DEBUG"
|
|
)
|
|
self._log(f" Expected spec file: {self.derived_spec_path}", level="DEBUG")
|
|
self._log(
|
|
f" Expected icon file: {self.derived_icon_path} (platform default)",
|
|
level="DEBUG",
|
|
)
|
|
|
|
# Update GUI labels for derived paths (use pathlib for checks)
|
|
main_script_path_obj = pathlib.Path(self.derived_main_script_path)
|
|
spec_path_obj = pathlib.Path(self.derived_spec_path)
|
|
icon_path_obj = pathlib.Path(self.derived_icon_path)
|
|
|
|
if main_script_path_obj.is_file():
|
|
self.derived_script_label_val.config(
|
|
text=str(main_script_path_obj), foreground="black"
|
|
)
|
|
else:
|
|
self.derived_script_label_val.config(text="NOT FOUND!", foreground="red")
|
|
self._log(
|
|
f"Error: Main script not found in the expected location: {main_script_path_obj}",
|
|
level="ERROR",
|
|
)
|
|
messagebox.showerror(
|
|
"Invalid Project Structure",
|
|
f"Main script not found in expected location:\n{main_script_path_obj}",
|
|
parent=self.parent_gui,
|
|
)
|
|
|
|
if spec_path_obj.is_file():
|
|
self.derived_spec_label_val.config(
|
|
text=str(spec_path_obj), foreground="black"
|
|
)
|
|
else:
|
|
self.derived_spec_label_val.config(
|
|
text="NOT FOUND (will be generated)", foreground="orange"
|
|
)
|
|
|
|
if icon_path_obj.is_file():
|
|
self.derived_icon_label_val.config(
|
|
text=str(icon_path_obj), foreground="black"
|
|
)
|
|
self.icon_path_var.set(
|
|
str(icon_path_obj)
|
|
) # Auto-fill icon path if default found
|
|
else:
|
|
self.derived_icon_label_val.config(
|
|
text="Not found (optional)", foreground="grey"
|
|
)
|
|
self.icon_path_var.set("") # Clear icon path if default not found
|
|
|
|
# Check if derived source directory actually exists
|
|
if (
|
|
isinstance(self.derived_source_dir_path, pathlib.Path)
|
|
and not self.derived_source_dir_path.is_dir()
|
|
):
|
|
self._log(
|
|
f"Warning: Expected source directory '{self.derived_source_dir_path.name}' does not exist. This might be an issue if it's essential for the project structure.",
|
|
level="WARNING",
|
|
)
|
|
|
|
# Call the callback if project selection and initial path derivation are successful
|
|
if main_script_path_obj.is_file() and self.on_project_selected_callback:
|
|
# Prepare a dictionary of derived paths to pass to the callback
|
|
derived_paths_info: Dict[str, Any] = {
|
|
"project_root_path": dir_path,
|
|
"project_root_name": self.project_root_name,
|
|
"main_script_path": self.derived_main_script_path,
|
|
"derived_source_dir_path": self.derived_source_dir_path, # pathlib.Path object
|
|
"spec_file_path": self.derived_spec_path,
|
|
"derived_icon_path": self.derived_icon_path,
|
|
}
|
|
self.on_project_selected_callback(dir_path, derived_paths_info)
|
|
else:
|
|
self._log(
|
|
"Project selection failed or main script not found. Callback not invoked.",
|
|
level="INFO",
|
|
)
|
|
|
|
def get_derived_paths(self) -> Dict[str, Optional[Union[str, pathlib.Path]]]:
|
|
"""Returns a dictionary of currently derived paths."""
|
|
return {
|
|
"main_script_path": self.derived_main_script_path,
|
|
"derived_source_dir_path": self.derived_source_dir_path,
|
|
"spec_file_path": self.derived_spec_path,
|
|
"derived_icon_path": self.derived_icon_path,
|
|
"project_root_name": self.project_root_name,
|
|
"project_directory_path": self.project_directory_path.get(),
|
|
}
|