aggiunta la possibilità di gestire repository che hanno nomi diversi rispetto la cartella del sorgente

This commit is contained in:
VALLONGOL 2025-11-12 14:28:33 +01:00
parent 07423ab4d0
commit 42aa62a1b2

View File

@ -69,7 +69,7 @@ class ProjectManager:
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.derived_source_dir_path: Optional[pathlib.Path] = None # pathlib.Path object
self.project_root_name: Optional[str] = None
def _log(self, message: str, level: str = "INFO") -> None:
@ -79,10 +79,128 @@ class ProjectManager:
except TypeError:
self.logger(f"[{level}][ProjectManager] {message}")
def _find_source_folders_with_main(self, project_root: pathlib.Path) -> list[pathlib.Path]:
"""
Finds all direct subdirectories of the project root that contain a __main__.py file.
Args:
project_root: The root directory of the project.
Returns:
A list of Path objects representing subdirectories containing __main__.py.
"""
candidates = []
try:
for item in project_root.iterdir():
if item.is_dir() and not item.name.startswith('.') and not item.name.startswith('_'):
main_script = item / "__main__.py"
if main_script.is_file():
candidates.append(item)
self._log(f"Found candidate source folder: {item.name}", level="DEBUG")
except Exception as e:
self._log(f"Error scanning directory: {e}", level="ERROR")
return candidates
def _ask_user_for_source_folder(self, project_root: pathlib.Path, candidates: list[pathlib.Path]) -> Optional[pathlib.Path]:
"""
Asks the user to select the correct source folder either from candidates or by manual selection.
Args:
project_root: The root directory of the project.
candidates: List of candidate folders found automatically.
Returns:
The selected source folder Path, or None if cancelled.
"""
dialog = tk.Toplevel(self.parent_gui)
dialog.title("Select Source Folder")
dialog.geometry("600x400")
dialog.transient(self.parent_gui)
dialog.grab_set()
selected_folder = None
def on_select():
nonlocal selected_folder
selection = listbox.curselection()
if selection:
idx = selection[0]
if idx < len(candidates):
selected_folder = candidates[idx]
dialog.destroy()
def on_browse():
nonlocal selected_folder
folder = filedialog.askdirectory(
title="Select the source folder containing __main__.py",
initialdir=str(project_root),
parent=dialog
)
if folder:
folder_path = pathlib.Path(folder)
# Verify it contains __main__.py
if (folder_path / "__main__.py").is_file():
selected_folder = folder_path
dialog.destroy()
else:
messagebox.showerror(
"Invalid Selection",
f"The selected folder does not contain a __main__.py file.",
parent=dialog
)
def on_cancel():
dialog.destroy()
# Message label
msg_frame = ttk.Frame(dialog, padding=10)
msg_frame.pack(fill=tk.X)
if candidates:
msg_text = "Multiple source folders found. Please select the correct one:"
else:
msg_text = "No source folder found automatically. Please browse to select the folder containing __main__.py:"
ttk.Label(msg_frame, text=msg_text, wraplength=560).pack()
# Listbox with candidates
if candidates:
list_frame = ttk.Frame(dialog, padding=10)
list_frame.pack(fill=tk.BOTH, expand=True)
scrollbar = ttk.Scrollbar(list_frame)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
listbox = tk.Listbox(list_frame, yscrollcommand=scrollbar.set)
listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.config(command=listbox.yview)
for candidate in candidates:
listbox.insert(tk.END, candidate.name)
listbox.bind('<Double-Button-1>', lambda e: on_select())
else:
listbox = None
# Buttons
btn_frame = ttk.Frame(dialog, padding=10)
btn_frame.pack(fill=tk.X)
if candidates:
ttk.Button(btn_frame, text="Select", command=on_select).pack(side=tk.LEFT, padx=5)
ttk.Button(btn_frame, text="Browse...", command=on_browse).pack(side=tk.LEFT, padx=5)
ttk.Button(btn_frame, text="Cancel", command=on_cancel).pack(side=tk.RIGHT, padx=5)
dialog.wait_window()
return selected_folder
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.
If the expected source folder is not found, searches for alternatives and asks the user.
"""
self._log(
"=" * 20 + " PROJECT DIRECTORY SELECTION STARTED " + "=" * 20, level="INFO"
@ -101,19 +219,61 @@ class ProjectManager:
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
# Try to find the source directory - first check the expected location
expected_source_dir = project_root / project_name_lower
source_folder_name = project_name_lower # Default to expected name
if (expected_source_dir / "__main__.py").is_file():
# Expected location found - use it
self.derived_source_dir_path = expected_source_dir
self._log(f"Using expected source folder: {source_folder_name}", level="INFO")
else:
# Expected location not found - search for alternatives
self._log(f"Expected source folder '{project_name_lower}' not found or does not contain __main__.py", level="WARNING")
self._log("Searching for alternative source folders...", level="INFO")
candidates = self._find_source_folders_with_main(project_root)
if len(candidates) == 1:
# Only one candidate found - use it automatically
self.derived_source_dir_path = candidates[0]
source_folder_name = candidates[0].name
self._log(f"Automatically selected single candidate: {source_folder_name}", level="INFO")
elif len(candidates) > 1:
# Multiple candidates - ask user to choose
self._log(f"Found {len(candidates)} candidate folders, asking user to select...", level="INFO")
selected = self._ask_user_for_source_folder(project_root, candidates)
if selected:
self.derived_source_dir_path = selected
source_folder_name = selected.name
self._log(f"User selected source folder: {source_folder_name}", level="INFO")
else:
self._log("User cancelled source folder selection.", level="INFO")
return
else:
# No candidates found - ask user to browse
self._log("No source folders with __main__.py found, asking user to browse...", level="WARNING")
selected = self._ask_user_for_source_folder(project_root, [])
if selected:
self.derived_source_dir_path = selected
source_folder_name = selected.name
self._log(f"User selected source folder: {source_folder_name}", level="INFO")
else:
self._log("User cancelled source folder selection.", level="INFO")
return
# Now derive paths using the determined source folder name
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")
self.derived_spec_path = str(project_root / f"{source_folder_name}.spec")
# Default icon filename based on platform
default_icon_filename = f"{project_name_lower}.ico"
default_icon_filename = f"{source_folder_name}.ico"
if sys.platform == "darwin":
default_icon_filename = f"{project_name_lower}.icns"
default_icon_filename = f"{source_folder_name}.icns"
elif sys.platform != "win32":
default_icon_filename = f"{project_name_lower}.png"
default_icon_filename = f"{source_folder_name}.png"
self.derived_icon_path = str(project_root / default_icon_filename)
self._log(f"Derived project name: {self.project_root_name}", level="DEBUG")
@ -134,21 +294,25 @@ class ProjectManager:
spec_path_obj = pathlib.Path(self.derived_spec_path)
icon_path_obj = pathlib.Path(self.derived_icon_path)
# At this point, main script should always exist since we verified it during folder selection
if main_script_path_obj.is_file():
self.derived_script_label_val.config(
text=str(main_script_path_obj), foreground="black"
)
self._log(f"Main script confirmed: {main_script_path_obj}", level="INFO")
else:
# This shouldn't happen anymore, but keep as safeguard
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}",
f"Error: Main script not found: {main_script_path_obj}",
level="ERROR",
)
messagebox.showerror(
"Invalid Project Structure",
f"Main script not found in expected location:\n{main_script_path_obj}",
f"Main script not found:\n{main_script_path_obj}",
parent=self.parent_gui,
)
return
if spec_path_obj.is_file():
self.derived_spec_label_val.config(
@ -172,18 +336,18 @@ class ProjectManager:
)
self.icon_path_var.set("") # Clear icon path if default not found
# Check if derived source directory actually exists
# Source directory should always exist at this point
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.",
f"Warning: Source directory '{self.derived_source_dir_path.name}' does not exist.",
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:
# Call the callback - main script existence is guaranteed at this point
if 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,