You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

518 lines
18 KiB

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.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):
"""Ajoute un message dans la zone de log avec auto-scroll"""
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):
"""Prévisualise et génère le diagramme UML si validé"""
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):
"""Prévisualise et génère le README si validé"""
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()
else:
from src.code_analyzer import CSharpAnalyzer
analyzer = CSharpAnalyzer()
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 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()