import tkinter as tk import customtkinter as ctk from tkinter import filedialog, messagebox, ttk import os import platform import subprocess from src.uml_generator import UMLGenerator from src.code_analyzer import JavaAnalyzer, CSharpAnalyzer from src.project_analyzer import ProjectAnalyzer from src.readme_generator import ReadmeGenerator from src.preview_window import PreviewWindow class UMLGeneratorApp(ctk.CTk): def __init__(self): super().__init__() self.title("Générateur UML et Documentation") self.geometry("800x600") ctk.set_appearance_mode("dark") ctk.set_default_color_theme("blue") self.grid_columnconfigure(0, weight=1) self.grid_rowconfigure(0, weight=1) self.main_frame = ctk.CTkFrame(self) self.main_frame.grid(row=0, column=0, padx=20, pady=20, sticky="nsew") self.main_frame.grid_columnconfigure(0, weight=1) self.title_label = ctk.CTkLabel( self.main_frame, text="Générateur UML et Documentation", font=("Helvetica", 24, "bold") ) self.title_label.grid(row=0, column=0, pady=20) self.tabview = ctk.CTkTabview(self.main_frame) self.tabview.grid(row=1, column=0, padx=20, pady=10, sticky="nsew") self.main_frame.grid_rowconfigure(1, weight=1) self.uml_tab = self.tabview.add("UML") self.uml_tab.grid_columnconfigure(0, weight=1) self.setup_uml_tab() # Onglet README # self.readme_tab = self.tabview.add("README") # self.readme_tab.grid_columnconfigure(0, weight=1) # self.setup_readme_tab() # Onglet Sélection de projet # self.project_tab = self.tabview.add("Sélection de projet") # self.project_tab.grid_columnconfigure(0, weight=1) # self.setup_project_tab() def setup_uml_tab(self): self.project_frame = ctk.CTkFrame(self.uml_tab) self.project_frame.grid(row=0, column=0, padx=20, pady=10, sticky="ew") self.project_label = ctk.CTkLabel( self.project_frame, text="Chemin du projet:", font=("Helvetica", 14) ) self.project_label.grid(row=0, column=0, padx=10, pady=5) self.project_path = ctk.CTkEntry(self.project_frame, width=400) self.project_path.grid(row=0, column=1, padx=10, pady=5) self.browse_button = ctk.CTkButton( self.project_frame, text="Parcourir", command=self.browse_directory ) self.browse_button.grid(row=0, column=2, padx=10, pady=5) self.options_frame = ctk.CTkFrame(self.uml_tab) self.options_frame.grid(row=1, column=0, padx=20, pady=10, sticky="ew") self.language_var = tk.StringVar(value="java") self.java_radio = ctk.CTkRadioButton( self.options_frame, text="Java", variable=self.language_var, value="java" ) self.java_radio.grid(row=0, column=0, padx=20, pady=10) self.csharp_radio = ctk.CTkRadioButton( self.options_frame, text="C#", variable=self.language_var, value="csharp" ) self.csharp_radio.grid(row=0, column=1, padx=20, pady=10) self.php_radio = ctk.CTkRadioButton( self.options_frame, text="PHP", variable=self.language_var, value="php" ) self.php_radio.grid(row=0, column=2, padx=20, pady=10) self.generate_classes = tk.BooleanVar(value=True) self.classes_check = ctk.CTkCheckBox( self.options_frame, text="Diagramme de classes", variable=self.generate_classes ) self.classes_check.grid(row=1, column=0, padx=20, pady=5) self.generate_doc = tk.BooleanVar(value=True) self.doc_check = ctk.CTkCheckBox( self.options_frame, text="Documentation", variable=self.generate_doc ) self.doc_check.grid(row=1, column=1, padx=20, pady=5) self.generate_button = ctk.CTkButton( self.uml_tab, text="Générer UML", command=self.generate, font=("Helvetica", 16), height=40 ) self.generate_button.grid(row=2, column=0, pady=20) self.log_frame = ctk.CTkFrame(self.uml_tab) self.log_frame.grid(row=3, column=0, padx=20, pady=10, sticky="nsew") self.uml_tab.grid_rowconfigure(3, weight=1) self.log_text = ctk.CTkTextbox( self.log_frame, height=200, width=600, wrap="word" ) self.log_text.grid(row=0, column=0, sticky="nsew") self.log_frame.grid_columnconfigure(0, weight=1) self.log_frame.grid_rowconfigure(0, weight=1) def setup_readme_tab(self): self.readme_content_frame = ctk.CTkFrame(self.readme_tab) self.readme_content_frame.grid(row=0, column=0, padx=20, pady=10, sticky="nsew") self.readme_tab.grid_rowconfigure(0, weight=1) self.title_label = ctk.CTkLabel( self.readme_content_frame, text="Titre du projet:", font=("Helvetica", 14) ) self.title_label.grid(row=0, column=0, padx=10, pady=5, sticky="w") self.title_entry = ctk.CTkEntry( self.readme_content_frame, width=400 ) self.title_entry.grid(row=0, column=1, padx=10, pady=5, sticky="ew") self.desc_label = ctk.CTkLabel( self.readme_content_frame, text="Description:", font=("Helvetica", 14) ) self.desc_label.grid(row=1, column=0, padx=10, pady=5, sticky="nw") self.desc_text = ctk.CTkTextbox( self.readme_content_frame, height=100, width=400 ) self.desc_text.grid(row=1, column=1, padx=10, pady=5, sticky="nsew") self.tech_label = ctk.CTkLabel( self.readme_content_frame, text="Technologies détectées:", font=("Helvetica", 14) ) self.tech_label.grid(row=2, column=0, padx=10, pady=5, sticky="nw") self.tech_text = ctk.CTkTextbox( self.readme_content_frame, height=80, width=400, state="disabled" ) self.tech_text.grid(row=2, column=1, padx=10, pady=5, sticky="nsew") self.authors_label = ctk.CTkLabel( self.readme_content_frame, text="Auteurs (Git):", font=("Helvetica", 14) ) self.authors_label.grid(row=3, column=0, padx=10, pady=5, sticky="nw") self.authors_text = ctk.CTkTextbox( self.readme_content_frame, height=80, width=400, state="disabled" ) self.authors_text.grid(row=3, column=1, padx=10, pady=5, sticky="nsew") self.info_label = ctk.CTkLabel( self.readme_content_frame, text="Note: Les technologies et auteurs seront détectés automatiquement\nlors de la génération du README", font=("Helvetica", 12), text_color="gray" ) self.info_label.grid(row=4, column=0, columnspan=2, pady=5) self.license_label = ctk.CTkLabel( self.readme_content_frame, text="Licence:", font=("Helvetica", 14) ) self.license_label.grid(row=5, column=0, padx=10, pady=5, sticky="w") self.license_entry = ctk.CTkEntry( self.readme_content_frame, width=400 ) self.license_entry.insert(0, "MIT") self.license_entry.grid(row=5, column=1, padx=10, pady=5, sticky="ew") self.readme_content_frame.grid_columnconfigure(1, weight=1) for i in range(6): if i == 1: self.readme_content_frame.grid_rowconfigure(i, weight=1) else: self.readme_content_frame.grid_rowconfigure(i, weight=0) self.generate_readme_button = ctk.CTkButton( self.readme_tab, text="Générer README", command=self.generate_readme, font=("Helvetica", 16), height=40 ) self.generate_readme_button.grid(row=1, column=0, pady=20) def setup_project_tab(self): self.project_content_frame = ctk.CTkFrame(self.project_tab) self.project_content_frame.grid(row=0, column=0, padx=20, pady=10, sticky="nsew") self.project_tab.grid_rowconfigure(0, weight=1) self.project_title_label = ctk.CTkLabel( self.project_content_frame, text="Titre du projet:", font=("Helvetica", 14) ) self.project_title_label.grid(row=0, column=0, padx=10, pady=5, sticky="w") self.project_title_entry = ctk.CTkEntry( self.project_content_frame, width=400 ) self.project_title_entry.grid(row=0, column=1, padx=10, pady=5, sticky="ew") self.project_desc_label = ctk.CTkLabel( self.project_content_frame, text="Description:", font=("Helvetica", 14) ) self.project_desc_label.grid(row=1, column=0, padx=10, pady=5, sticky="nw") self.project_desc_text = ctk.CTkTextbox( self.project_content_frame, height=100, width=400 ) self.project_desc_text.grid(row=1, column=1, padx=10, pady=5, sticky="nsew") self.generate_project_button = ctk.CTkButton( self.project_content_frame, text="Générer README et diagramme", command=self.generate_project, font=("Helvetica", 16), height=40 ) self.generate_project_button.grid(row=2, column=0, columnspan=2, pady=20) def log_message(self, message): self.log_text.insert(tk.END, message + "\n") self.log_text.see(tk.END) self.update_idletasks() def browse_directory(self): directory = filedialog.askdirectory() if directory: self.project_path.delete(0, tk.END) self.project_path.insert(0, directory) def preview_and_generate_uml(self, classes, output_dir): from src.uml_generator import UMLGenerator import tempfile temp_dir = tempfile.mkdtemp() generator = UMLGenerator() temp_diagram = generator.generate_class_diagram(classes, temp_dir) from src.preview_window import PreviewWindow preview = PreviewWindow(self, content_type="uml", image_path=temp_diagram) self.wait_window(preview) if preview.result: return generator.generate_class_diagram(classes, output_dir) return None def preview_and_generate_readme(self, project_info, output_dir): from src.readme_generator import ReadmeGenerator generator = ReadmeGenerator() preview_content = generator.generate_readme_content(project_info) from src.preview_window import PreviewWindow preview = PreviewWindow(self, content_type="readme", content=preview_content) self.wait_window(preview) if preview.result: return generator.generate_readme(project_info, output_dir) return None def open_output_folder(self, output_dir): if os.path.isdir(output_dir): if platform.system() == "Windows": os.startfile(output_dir) elif platform.system() == "Darwin": subprocess.call(["open", output_dir]) else: subprocess.call(["xdg-open", output_dir]) else: print(f"Error: Directory '{output_dir}' does not exist.") def generate(self): project_path = self.project_path.get() if not project_path: messagebox.showerror("Erreur", "Veuillez sélectionner un dossier de projet") return if not os.path.exists(project_path): messagebox.showerror("Erreur", "Le dossier spécifié n'existe pas") return self.log_text.delete("1.0", tk.END) self.log_message("=== Début de l'analyse ===\n") self.log_message("📁 Dossier projet: " + project_path) self.log_message("🔧 Langage: " + self.language_var.get()) self.log_message("📊 Options sélectionnées:") self.log_message(f" - Diagramme de classes: {'✓' if self.generate_classes.get() else '✗'}") self.log_message(f" - Documentation: {'✓' if self.generate_doc.get() else '✗'}\n") try: output_dir = os.path.join(project_path, "uml_output") if not os.path.exists(output_dir): os.makedirs(output_dir) self.log_message("📁 Création du dossier de sortie: " + output_dir) self.log_message("🔍 Initialisation de l'analyseur de code...") if self.language_var.get() == "java": from src.code_analyzer import JavaAnalyzer analyzer = JavaAnalyzer() elif self.language_var.get() == "csharp": from src.code_analyzer import CSharpAnalyzer analyzer = CSharpAnalyzer() elif self.language_var.get() == "php": from src.code_analyzer import PHPAnalyzer analyzer = PHPAnalyzer() self.log_message("🔍 Analyse du code source en cours...") classes, relationships = analyzer.analyze_directory(project_path) self.log_message(f"✓ {len(classes)} classes trouvées") self.log_message("\n📋 Classes détectées:") for cls in classes: self.log_message(f" - {cls['name']}") if cls['extends']: self.log_message(f" ↳ Hérite de: {cls['extends']}") if cls['implements']: self.log_message(f" ↳ Implémente: {', '.join(cls['implements'])}") if 'associations' in cls and cls['associations']: self.log_message(f" ↳ Associations: {', '.join(cls['associations'])}") if 'dependencies' in cls and cls['dependencies']: self.log_message(f" ↳ Dépendances: {', '.join(cls['dependencies'])}") if 'aggregations' in cls and cls['aggregations']: self.log_message(f" ↳ Agrégations: {', '.join(cls['aggregations'])}") if 'compositions' in cls and cls['compositions']: self.log_message(f" ↳ Compositions: {', '.join(cls['compositions'])}") if self.generate_classes.get(): self.log_message("\n📊 Génération du diagramme de classes...") diagram_file = self.preview_and_generate_uml(classes, output_dir) if diagram_file: self.log_message(f"✓ Diagramme généré: {diagram_file}") else: self.log_message("❌ Génération du diagramme annulée") return if self.generate_doc.get(): self.log_message("\n📝 Génération de la documentation...") from src.uml_generator import UMLGenerator generator = UMLGenerator() doc_file = generator.generate_documentation(classes, output_dir) self.log_message(f"✓ Documentation générée: {doc_file}") self.log_message("\n=== Génération terminée avec succès ===") self.log_message(f"📂 Les fichiers ont été générés dans: {output_dir}") self.after(1000, lambda: self.open_output_folder(output_dir)) except Exception as e: self.log_message(f"\n❌ ERREUR: {str(e)}") self.log_message("=== Génération interrompue ===") messagebox.showerror("Erreur", f"Une erreur est survenue: {str(e)}") def generate_readme(self): from src.readme_generator import ReadmeGenerator from src.project_analyzer import ProjectAnalyzer project_path = self.project_path.get() if not project_path: messagebox.showerror("Erreur", "Veuillez sélectionner un dossier de projet") return if not os.path.exists(project_path): messagebox.showerror("Erreur", "Le dossier spécifié n'existe pas") return try: analyzer = ProjectAnalyzer() self.log_message("🔍 Analyse des technologies utilisées...") technologies = analyzer.analyze_technologies(project_path) self.log_message("🔍 Analyse des prérequis...") prerequisites = analyzer.get_prerequisites(project_path) self.log_message("🔍 Génération des étapes d'installation...") installation_steps = analyzer.get_installation_steps(project_path) self.log_message("🔍 Récupération des auteurs depuis Git...") authors = analyzer.get_git_authors(project_path) project_info = { 'title': self.title_entry.get(), 'description': self.desc_text.get("1.0", tk.END).strip(), 'license': self.license_entry.get(), 'technologies': technologies, 'prerequisites': prerequisites, 'installation_steps': installation_steps, 'authors': authors } if not project_info['title']: messagebox.showerror("Erreur", "Le titre du projet est obligatoire") return if not project_info['description']: messagebox.showerror("Erreur", "La description du projet est obligatoire") return readme_file = self.preview_and_generate_readme(project_info, project_path) if readme_file: messagebox.showinfo("Succès", f"README.md généré avec succès dans {project_path}") self.open_output_folder(project_path) except Exception as e: messagebox.showerror("Erreur", f"Une erreur est survenue lors de la génération du README: {str(e)}") def generate_project(self): project_title = self.project_title_entry.get() project_description = self.project_desc_text.get("1.0", tk.END).strip() print(f'Generating README for: {project_title} - {project_description}') if __name__ == "__main__": app = UMLGeneratorApp() app.mainloop()