From af94f256af5c650b9c4de95c39e8d2c07e4e9b35 Mon Sep 17 00:00:00 2001 From: VALLONGOL Date: Wed, 12 Nov 2025 15:50:29 +0100 Subject: [PATCH] =?UTF-8?q?aggiunta=20la=20nuova=20funzione=20per=20unire?= =?UTF-8?q?=20pi=C3=B9=20file=20markdown=20in=20un=20unico=20file=20per=20?= =?UTF-8?q?creare=20il=20manuale=20completo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/app_config.json | 18 +- convert.py | 147 ++++++++++ doc/BATCH_CONVERTER.md | 78 ++++++ markdownconverter/core/core.py | 65 ++++- markdownconverter/gui/batch_converter.py | 332 +++++++++++++++++++++++ markdownconverter/gui/gui.py | 20 +- test_integration.py | 38 +++ 7 files changed, 687 insertions(+), 11 deletions(-) create mode 100644 convert.py create mode 100644 doc/BATCH_CONVERTER.md create mode 100644 markdownconverter/gui/batch_converter.py create mode 100644 test_integration.py diff --git a/config/app_config.json b/config/app_config.json index f0736c5..10ae2e2 100644 --- a/config/app_config.json +++ b/config/app_config.json @@ -3,7 +3,7 @@ "last_selected_profile": "radar_data_reader", "profiles": { "cpp_python_debug": { - "template_path": "C:/src/____GitProjects/cpp_python_debug/doc/TemplateSumSample.docx", + "template_path": "C:/src/____GitProjects/SXXXXXXX_CppPythonDebug /doc/TemplateSumSample.docx", "values": { "%%DOC_CUSTOMER%%": "INTERNO", "%%DOC_DATE%%": "18/06/2025", @@ -14,18 +14,18 @@ } }, "radar_data_reader": { - "template_path": "C:/src/____GitProjects/MarkdownConverter/TemplateSumSample.docx", + "template_path": "C:/src/____GitProjects/SXXXXXXX_MarkdownConverter/TemplateSumSample.docx", "values": { - "%%DOC_CUSTOMER%%": "INTERNAL", - "%%DOC_DATE%%": "22/07/2025", - "%%DOC_NUMBER%%": "90000002", - "%%DOC_PROJECT%%": "RADAR DATA READER", - "%%DOC_REV%%": "A", - "%%DOC_SECURITY%%": "INTERNAL" + "%%DOC_CUSTOMER%%": "", + "%%DOC_DATE%%": "", + "%%DOC_NUMBER%%": "", + "%%DOC_PROJECT%%": "", + "%%DOC_REV%%": "", + "%%DOC_SECURITY%%": "" } }, "mark_converter": { - "template_path": "C:/src/____GitProjects/MarkdownConverter/TemplateSumSample.docx", + "template_path": "C:/src/____GitProjects/SXXXXXXX_ MarkdownConverter/TemplateSumSample.docx", "values": { "%%DOC_CUSTOMER%%": "INTERNO", "%%DOC_DATE%%": "22/07/2025", diff --git a/convert.py b/convert.py new file mode 100644 index 0000000..d905daa --- /dev/null +++ b/convert.py @@ -0,0 +1,147 @@ +import os +import subprocess +import tkinter as tk +from tkinter import filedialog, messagebox, scrolledtext +from pathlib import Path + +class MarkdownToPDFApp: + def __init__(self, root): + self.root = root + self.root.title("Markdown → DOCX/PDF Converter") + self.root.geometry("650x480") + self.root.resizable(False, False) + + self.folder_path = tk.StringVar() + self.output_name = tk.StringVar(value="manuale") + self.template_path = tk.StringVar() + self.use_template = tk.BooleanVar(value=False) + self.generate_pdf = tk.BooleanVar(value=True) + + # --- UI --- + tk.Label(root, text="Cartella Markdown:").pack(anchor="w", padx=10, pady=(10, 0)) + frame1 = tk.Frame(root) + frame1.pack(fill="x", padx=10) + tk.Entry(frame1, textvariable=self.folder_path, width=50).pack(side="left", fill="x", expand=True) + tk.Button(frame1, text="Sfoglia...", command=self.choose_folder).pack(side="right", padx=5) + + tk.Label(root, text="Nome base file output (senza estensione):").pack(anchor="w", padx=10, pady=(10, 0)) + tk.Entry(root, textvariable=self.output_name, width=40).pack(fill="x", padx=10) + + tk.Checkbutton(root, text="Usa template DOCX", variable=self.use_template, command=self.toggle_template).pack(anchor="w", padx=10, pady=(10, 0)) + frame2 = tk.Frame(root) + frame2.pack(fill="x", padx=10) + tk.Entry(frame2, textvariable=self.template_path, width=50, state="disabled").pack(side="left", fill="x", expand=True) + tk.Button(frame2, text="Seleziona template", command=self.choose_template, state="disabled").pack(side="right", padx=5) + self.template_frame = frame2 + + tk.Checkbutton(root, text="Genera anche PDF finale", variable=self.generate_pdf).pack(anchor="w", padx=10, pady=(10, 0)) + + tk.Button(root, text="Genera Documento", command=self.generate_output, bg="#3c9", fg="white").pack(pady=10) + + tk.Label(root, text="Log:").pack(anchor="w", padx=10) + self.log_box = scrolledtext.ScrolledText(root, height=13, state="disabled") + self.log_box.pack(fill="both", expand=True, padx=10, pady=(0, 10)) + + # --- Utility methods --- + def log(self, text): + self.log_box.configure(state="normal") + self.log_box.insert(tk.END, text + "\n") + self.log_box.configure(state="disabled") + self.log_box.see(tk.END) + self.root.update_idletasks() + + def choose_folder(self): + folder = filedialog.askdirectory(title="Seleziona la cartella Markdown") + if folder: + self.folder_path.set(folder) + + def toggle_template(self): + state = "normal" if self.use_template.get() else "disabled" + for widget in self.template_frame.winfo_children(): + widget.configure(state=state) + + def choose_template(self): + file = filedialog.askopenfilename(title="Seleziona template DOCX", filetypes=[("Word Template", "*.docx")]) + if file: + self.template_path.set(file) + + def generate_output(self): + folder = self.folder_path.get().strip() + output_name = self.output_name.get().strip() + template = self.template_path.get().strip() + use_template = self.use_template.get() + make_pdf = self.generate_pdf.get() + + if not folder: + messagebox.showwarning("Attenzione", "Seleziona una cartella contenente i file Markdown.") + return + + folder_path = Path(folder) + output_docx = folder_path / f"{output_name}.docx" + output_pdf = folder_path / f"{output_name}.pdf" + + # Trova i file Markdown numerati + md_files = sorted(folder_path.glob("[0-9][0-9]_*.md")) + if not md_files: + messagebox.showerror("Errore", "Nessun file Markdown numerato trovato nella cartella.") + return + + self.log(f"Trovati {len(md_files)} file Markdown:") + for f in md_files: + self.log(f" - {f.name}") + + combined_md = folder_path / "_manuale_unico_temp.md" + self.log(f"\nUnione dei file in {combined_md.name}...") + + try: + with open(combined_md, "w", encoding="utf-8") as out: + for f in md_files: + out.write(f"\n\n# --- {f.name} ---\n\n") + out.write(f.read_text(encoding="utf-8")) + out.write("\n\n") + self.log("File unito con successo.") + except Exception as e: + messagebox.showerror("Errore durante unione", str(e)) + return + + # Comando Pandoc per generare DOCX + cmd_docx = ["pandoc", str(combined_md), "-o", str(output_docx)] + if use_template: + if not Path(template).exists(): + messagebox.showerror("Template non trovato", f"Il file {template} non esiste.") + return + cmd_docx.extend(["--reference-doc", str(template)]) + + self.log("\nEsecuzione Pandoc (DOCX)...") + result = subprocess.run(cmd_docx, capture_output=True, text=True) + if result.returncode != 0: + self.log("❌ Errore Pandoc (DOCX):") + self.log(result.stderr) + messagebox.showerror("Errore Pandoc", result.stderr) + return + + self.log(f"✅ DOCX generato correttamente: {output_docx}") + + # Generazione PDF opzionale + if make_pdf: + self.log("\nGenerazione PDF dal DOCX...") + cmd_pdf = ["pandoc", str(output_docx), "-o", str(output_pdf)] + result = subprocess.run(cmd_pdf, capture_output=True, text=True) + if result.returncode == 0: + self.log(f"✅ PDF generato: {output_pdf}") + messagebox.showinfo("Completato", f"PDF generato: {output_pdf}") + else: + self.log("❌ Errore durante conversione PDF:") + self.log(result.stderr) + messagebox.showerror("Errore PDF", result.stderr) + else: + messagebox.showinfo("Completato", f"DOCX generato: {output_docx}") + + # Pulisce file temporaneo + if combined_md.exists(): + combined_md.unlink() + +if __name__ == "__main__": + root = tk.Tk() + app = MarkdownToPDFApp(root) + root.mainloop() diff --git a/doc/BATCH_CONVERTER.md b/doc/BATCH_CONVERTER.md new file mode 100644 index 0000000..cb98cb4 --- /dev/null +++ b/doc/BATCH_CONVERTER.md @@ -0,0 +1,78 @@ +# MarkdownConverter - Aggiornamenti + +## Nuova Funzionalità: Conversione Batch + +### Descrizione +È stata aggiunta una nuova funzionalità che permette di convertire automaticamente più file Markdown in un unico documento DOCX e/o PDF. + +### Interfaccia a Tab +L'applicazione ora presenta due tab: + +#### 1. **Conversione Singola** +- La funzionalità originale per convertire singoli file Markdown +- Supporta l'uso di template con placeholder personalizzati +- Gestione dei profili per diversi tipi di documenti +- Conversione diretta a DOCX o PDF +- Possibilità di convertire DOCX in PDF + +#### 2. **Conversione Batch** +- Conversione automatica di più file Markdown da una cartella +- I file devono seguire il pattern di numerazione: `01_*.md`, `02_*.md`, ecc. +- Combinazione automatica dei file in ordine alfabetico +- Generazione di un singolo DOCX con tutti i contenuti +- Opzione per generare anche il PDF finale +- Supporto per template DOCX (senza placeholder, come riferimento di stile) + +### Utilizzo della Conversione Batch + +1. **Seleziona la Cartella** + - Clicca su "Sfoglia..." per selezionare la cartella contenente i file Markdown + - I file devono essere numerati: `01_introduzione.md`, `02_capitolo1.md`, ecc. + +2. **Configura le Opzioni** + - **Nome base output**: Il nome del file risultante (senza estensione) + - **Usa template DOCX**: Attiva per usare un template come riferimento di stile + - **Genera anche PDF finale**: Attiva per convertire automaticamente il DOCX in PDF + +3. **Genera il Documento** + - Clicca su "Genera Documento" + - Controlla il log per vedere i progressi + - I file verranno salvati nella stessa cartella dei file sorgente + +### Funzioni Core Aggiunte + +Nel modulo `markdownconverter/core/core.py` sono state aggiunte due nuove funzioni: + +#### `combine_markdown_files(markdown_files, output_path)` +Combina più file Markdown in un unico file. + +**Parametri:** +- `markdown_files`: Lista di Path o stringhe che puntano ai file markdown +- `output_path`: Percorso dove salvare il file combinato + +**Ritorna:** +- Percorso del file combinato + +#### `convert_markdown_to_docx_with_pandoc(input_file, output_path, template_path=None)` +Converte Markdown in DOCX usando Pandoc con template opzionale. +Questa è una conversione più semplice senza sostituzione di placeholder. + +**Parametri:** +- `input_file`: Percorso del file markdown +- `output_path`: Percorso dove salvare il DOCX +- `template_path`: Percorso opzionale a un template DOCX (reference-doc) + +**Ritorna:** +- Percorso del file DOCX generato + +### Note Tecniche + +- La funzionalità batch è completamente isolata nel modulo `batch_converter.py` +- Utilizza le stesse funzioni core per la conversione PDF +- Mantiene piena compatibilità con le funzionalità esistenti +- Il sistema a tab permette di passare facilmente tra le due modalità + +### Migrazione da convert.py + +Il file originale `convert.py` può essere mantenuto per compatibilità o rimosso. +Tutte le sue funzionalità sono ora integrate nell'applicazione principale. diff --git a/markdownconverter/core/core.py b/markdownconverter/core/core.py index 0c60ac2..1b59a9c 100644 --- a/markdownconverter/core/core.py +++ b/markdownconverter/core/core.py @@ -4,12 +4,12 @@ import os import re import sys import tempfile +import subprocess from datetime import date import docx import pypandoc import pdfkit import markdown -import subprocess from docx.enum.text import WD_BREAK from docx2pdf import convert as convert_word from ..utils.logger import get_logger @@ -444,6 +444,69 @@ def convert_docx_to_pdf(input_docx_path: str, output_pdf_path: str) -> str: raise +def combine_markdown_files(markdown_files: list, output_path: str) -> str: + """ + Combines multiple markdown files into a single file. + + Args: + markdown_files: List of Path objects or strings pointing to markdown files + output_path: Path where the combined markdown file will be saved + + Returns: + Path to the combined markdown file + """ + log.info(f"Combining {len(markdown_files)} markdown files into {output_path}") + + with open(output_path, "w", encoding="utf-8") as out: + for md_file in markdown_files: + file_name = os.path.basename(md_file) + log.debug(f"Adding file: {file_name}") + out.write(f"\n\n# --- {file_name} ---\n\n") + with open(md_file, "r", encoding="utf-8") as f: + out.write(f.read()) + out.write("\n\n") + + log.info(f"Successfully combined files into: {output_path}") + return output_path + + +def convert_markdown_to_docx_with_pandoc( + input_file: str, + output_path: str, + template_path: str = None +) -> str: + """ + Converts markdown to DOCX using Pandoc with optional template. + This is a simpler conversion without placeholder replacement. + + Args: + input_file: Path to the markdown file + output_path: Path where the DOCX will be saved + template_path: Optional path to a DOCX template (reference-doc) + + Returns: + Path to the generated DOCX file + """ + log.info(f"Converting '{os.path.basename(input_file)}' to DOCX using Pandoc.") + + if not os.path.exists(input_file): + raise FileNotFoundError(f"Input file not found: {input_file}") + + cmd = ["pandoc", str(input_file), "-o", str(output_path)] + + if template_path and os.path.exists(template_path): + log.info(f"Using template: {os.path.basename(template_path)}") + cmd.extend(["--reference-doc", str(template_path)]) + + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + log.info(f"DOCX successfully generated: {output_path}") + return output_path + except subprocess.CalledProcessError as e: + log.error(f"Pandoc conversion failed: {e.stderr}") + raise RuntimeError(f"Pandoc conversion failed: {e.stderr}") + + def convert_markdown( input_file: str, output_path: str, diff --git a/markdownconverter/gui/batch_converter.py b/markdownconverter/gui/batch_converter.py new file mode 100644 index 0000000..f8f46cb --- /dev/null +++ b/markdownconverter/gui/batch_converter.py @@ -0,0 +1,332 @@ +# markdownconverter/gui/batch_converter.py + +import os +import tkinter as tk +from pathlib import Path +from tkinter import filedialog, messagebox, StringVar, BooleanVar +import ttkbootstrap as tb +from tkinter.scrolledtext import ScrolledText +from ttkbootstrap.constants import * + +from ..core.core import ( + combine_markdown_files, + convert_markdown_to_docx_with_pandoc, + convert_docx_to_pdf +) +from ..utils.logger import get_logger + +log = get_logger(__name__) + + +class BatchConverterTab(tb.Frame): + """ + Tab per la conversione batch di file markdown da una cartella. + Trova tutti i file markdown numerati (es. 01_*.md, 02_*.md), + li combina in ordine alfabetico e genera DOCX e/o PDF. + """ + + def __init__(self, parent): + super().__init__(parent, padding=10) + + self.folder_path = StringVar() + self.output_name = StringVar(value="manuale") + self.template_path = StringVar() + self.use_template = BooleanVar(value=False) + self.generate_pdf = BooleanVar(value=True) + + self._build_ui() + + def _build_ui(self): + """Costruisce l'interfaccia utente del tab batch.""" + + # Frame per la selezione della cartella + folder_frame = tb.Labelframe(self, text="Cartella Sorgente", padding=10) + folder_frame.pack(fill=tk.X, pady=(0, 10)) + folder_frame.columnconfigure(1, weight=1) + + tb.Label(folder_frame, text="Cartella Markdown:").grid( + row=0, column=0, padx=5, pady=5, sticky="w" + ) + tb.Entry(folder_frame, textvariable=self.folder_path).grid( + row=0, column=1, padx=5, pady=5, sticky="ew" + ) + tb.Button( + folder_frame, + text="Sfoglia...", + command=self._choose_folder, + bootstyle=PRIMARY + ).grid(row=0, column=2, padx=5, pady=5) + + # Frame per le opzioni + options_frame = tb.Labelframe(self, text="Opzioni di Conversione", padding=10) + options_frame.pack(fill=tk.X, pady=(0, 10)) + options_frame.columnconfigure(1, weight=1) + + tb.Label(options_frame, text="Nome base output:").grid( + row=0, column=0, padx=5, pady=5, sticky="w" + ) + tb.Entry(options_frame, textvariable=self.output_name).grid( + row=0, column=1, padx=5, pady=5, sticky="ew" + ) + + # Checkbox per template + tb.Checkbutton( + options_frame, + text="Usa template DOCX", + variable=self.use_template, + command=self._toggle_template, + bootstyle="primary-round-toggle" + ).grid(row=1, column=0, padx=5, pady=5, sticky="w") + + # Frame per template + self.template_entry = tb.Entry( + options_frame, + textvariable=self.template_path, + state="disabled" + ) + self.template_entry.grid(row=1, column=1, padx=5, pady=5, sticky="ew") + + self.template_button = tb.Button( + options_frame, + text="Seleziona template", + command=self._choose_template, + state="disabled" + ) + self.template_button.grid(row=1, column=2, padx=5, pady=5) + + # Checkbox per PDF + tb.Checkbutton( + options_frame, + text="Genera anche PDF finale", + variable=self.generate_pdf, + bootstyle="primary-round-toggle" + ).grid(row=2, column=0, columnspan=2, padx=5, pady=5, sticky="w") + + # Pulsante di conversione + tb.Button( + self, + text="Genera Documento", + command=self._generate_output, + bootstyle=SUCCESS, + width=20 + ).pack(pady=10) + # Frame che mostra la lista dei file trovati nella cartella selezionata + files_frame = tb.Labelframe(self, text="File trovati (in ordine alfabetico)", padding=10) + files_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 10)) + files_frame.rowconfigure(0, weight=1); files_frame.columnconfigure(0, weight=1) + self.files_listbox = tk.Listbox(files_frame, height=8, exportselection=False) + self.files_listbox.grid(row=0, column=0, sticky="nsew") + files_scroll = tb.Scrollbar(files_frame, orient=tk.VERTICAL, command=self.files_listbox.yview) + files_scroll.grid(row=0, column=1, sticky="ns") + self.files_listbox.config(yscrollcommand=files_scroll.set) + + # Log area + log_frame = tb.Labelframe(self, text="Log", padding=10) + log_frame.pack(fill=tk.BOTH, expand=True) + + self.log_box = ScrolledText(log_frame, height=15, state="disabled", wrap=tk.WORD) + self.log_box.pack(fill=tk.BOTH, expand=True) + + def _log(self, text): + """Aggiunge un messaggio all'area log.""" + self.log_box.configure(state="normal") + self.log_box.insert(tk.END, text + "\n") + self.log_box.configure(state="disabled") + self.log_box.see(tk.END) + self.update_idletasks() + + def _choose_folder(self): + """Apre il dialogo per selezionare la cartella.""" + folder = filedialog.askdirectory(title="Seleziona la cartella Markdown") + if folder: + self.folder_path.set(folder) + self._log(f"Cartella selezionata: {folder}") + # Aggiorna immediatamente la lista dei file trovati + try: + self._scan_and_display_files(folder) + except Exception as e: + log.error(f"Errore durante la scansione della cartella: {e}", exc_info=True) + + def _toggle_template(self): + """Attiva/disattiva i controlli del template.""" + state = "normal" if self.use_template.get() else "disabled" + self.template_entry.configure(state=state) + self.template_button.configure(state=state) + + def _choose_template(self): + """Apre il dialogo per selezionare il template.""" + file = filedialog.askopenfilename( + title="Seleziona template DOCX", + filetypes=[("Word Documents", "*.docx"), ("Word Templates", "*.dotx")] + ) + if file: + self.template_path.set(file) + self._log(f"Template selezionato: {os.path.basename(file)}") + + def _generate_output(self): + """Esegue la conversione batch dei file markdown.""" + folder = self.folder_path.get().strip() + output_name = self.output_name.get().strip() + template = self.template_path.get().strip() + use_template = self.use_template.get() + make_pdf = self.generate_pdf.get() + + # Validazione input + if not folder: + messagebox.showwarning( + "Attenzione", + "Seleziona una cartella contenente i file Markdown." + ) + return + + if not output_name: + messagebox.showwarning( + "Attenzione", + "Inserisci un nome per il file di output." + ) + return + + folder_path = Path(folder) + if not folder_path.exists(): + messagebox.showerror("Errore", "La cartella selezionata non esiste.") + return + + # Trova i file markdown numerati + md_files = sorted(folder_path.glob("[0-9][0-9]_*.md")) + if not md_files: + messagebox.showerror( + "Errore", + "Nessun file Markdown numerato trovato nella cartella.\n" + "I file devono seguire il pattern: 01_nome.md, 02_nome.md, ecc." + ) + return + + # Mostra una finestra di conferma con la lista dei file trovati + if not self._confirm_file_list(md_files): + self._log("Operazione annullata dall'utente dopo la visualizzazione dei file.") + return + + self._log(f"\n{'='*60}") + self._log(f"INIZIO CONVERSIONE BATCH") + self._log(f"{'='*60}") + self._log(f"Trovati {len(md_files)} file Markdown:") + for f in md_files: + self._log(f" - {f.name}") + + # Prepara i percorsi di output + output_docx = folder_path / f"{output_name}.docx" + output_pdf = folder_path / f"{output_name}.pdf" + combined_md = folder_path / "_manuale_unico_temp.md" + + try: + # Combina i file markdown + self._log(f"\nUnione dei file in {combined_md.name}...") + combine_markdown_files(md_files, combined_md) + self._log("✅ File unito con successo.") + + # Genera DOCX + self._log("\nGenerazione DOCX...") + if use_template: + if not Path(template).exists(): + messagebox.showerror( + "Template non trovato", + f"Il file {template} non esiste." + ) + return + convert_markdown_to_docx_with_pandoc( + str(combined_md), + str(output_docx), + template_path=template + ) + else: + convert_markdown_to_docx_with_pandoc( + str(combined_md), + str(output_docx) + ) + + self._log(f"✅ DOCX generato: {output_docx.name}") + + # Genera PDF opzionale + if make_pdf: + self._log("\nGenerazione PDF dal DOCX...") + convert_docx_to_pdf(str(output_docx), str(output_pdf)) + self._log(f"✅ PDF generato: {output_pdf.name}") + messagebox.showinfo( + "Completato", + f"Conversione completata con successo!\n\n" + f"DOCX: {output_docx.name}\n" + f"PDF: {output_pdf.name}" + ) + else: + messagebox.showinfo( + "Completato", + f"Conversione completata con successo!\n\n" + f"DOCX: {output_docx.name}" + ) + + self._log(f"\n{'='*60}") + self._log("CONVERSIONE COMPLETATA CON SUCCESSO") + self._log(f"{'='*60}\n") + + except Exception as e: + log.error(f"Errore durante la conversione batch: {e}", exc_info=True) + self._log(f"\n❌ ERRORE: {str(e)}") + messagebox.showerror( + "Errore", + f"Si è verificato un errore durante la conversione:\n\n{str(e)}" + ) + finally: + # Pulisce il file temporaneo + if combined_md.exists(): + combined_md.unlink() + self._log(f"File temporaneo {combined_md.name} rimosso.") + + def _scan_and_display_files(self, folder: str): + """Scansiona la cartella e popola la listbox con i file markdown trovati. + + Il pattern usato corrisponde ai file numerati: 01_nome.md, 02_nome.md, ... + """ + folder_path = Path(folder) + if not folder_path.exists(): + raise FileNotFoundError("La cartella selezionata non esiste.") + md_files = sorted(folder_path.glob("[0-9][0-9]_*.md")) + # Pulisce la listbox + self.files_listbox.delete(0, tk.END) + for f in md_files: + self.files_listbox.insert(tk.END, f.name) + self._log(f"Scansionati {len(md_files)} file markdown nella cartella.") + + def _confirm_file_list(self, md_files: list) -> bool: + """Apre una finestra modale che mostra la lista dei file trovati e chiede conferma all'utente. + + Restituisce True se l'utente conferma, False se annulla. + """ + dlg = tb.Toplevel(self, title="Conferma file trovati") + dlg.transient(self) + dlg.grab_set() + dlg.geometry("600x400") + tb.Label(dlg, text="I seguenti file sono stati trovati e saranno uniti in questo ordine:", bootstyle=INFO).pack(anchor="w", padx=10, pady=(10, 0)) + list_frame = tb.Frame(dlg); list_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) + listbox = tk.Listbox(list_frame) + listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) + scrollbar = tb.Scrollbar(list_frame, orient=tk.VERTICAL, command=listbox.yview) + scrollbar.pack(side=tk.RIGHT, fill=tk.Y) + listbox.config(yscrollcommand=scrollbar.set) + for f in md_files: + listbox.insert(tk.END, f.name) + + btn_frame = tb.Frame(dlg); btn_frame.pack(fill=tk.X, pady=(0, 10), padx=10) + confirmed = {"value": False} + + def _on_confirm(): + confirmed["value"] = True + dlg.grab_release(); dlg.destroy() + + def _on_cancel(): + dlg.grab_release(); dlg.destroy() + + tb.Button(btn_frame, text="Conferma e procedi", bootstyle=SUCCESS, command=_on_confirm).pack(side=tk.RIGHT, padx=5) + tb.Button(btn_frame, text="Annulla", bootstyle=SECONDARY, command=_on_cancel).pack(side=tk.RIGHT) + + self.wait_window(dlg) + return confirmed["value"] diff --git a/markdownconverter/gui/gui.py b/markdownconverter/gui/gui.py index 6e707fd..eded855 100644 --- a/markdownconverter/gui/gui.py +++ b/markdownconverter/gui/gui.py @@ -27,6 +27,7 @@ from ..utils.logger import ( setup_basic_logging, add_tkinter_handler, shutdown_logging_system, get_logger ) +from .batch_converter import BatchConverterTab # EditorWindow non viene usato in questo file, ma lo lasciamo per coerenza # from .editor import EditorWindow @@ -169,7 +170,24 @@ class MarkdownConverterApp: setup_basic_logging(root_tk_instance_for_processor=self.root, logging_config_dict=self.log_config) def _build_ui(self): - container = tb.Frame(self.root, padding=10); container.pack(fill=tk.BOTH, expand=True) + # Crea il Notebook (sistema a tab) + self.notebook = tb.Notebook(self.root) + self.notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) + + # Tab 1: Conversione Singola (funzionalità originale) + self.single_converter_tab = tb.Frame(self.notebook, padding=10) + self.notebook.add(self.single_converter_tab, text="Conversione Singola") + + # Tab 2: Conversione Batch + self.batch_converter_tab = BatchConverterTab(self.notebook) + self.notebook.add(self.batch_converter_tab, text="Conversione Batch") + + # Costruisce la UI del tab singolo nel container + self._build_single_converter_ui() + + def _build_single_converter_ui(self): + """Costruisce l'interfaccia per la conversione singola.""" + container = self.single_converter_tab profile_frame = tb.Labelframe(container, text="Profile & Source File", padding=10) profile_frame.pack(fill=tk.X, pady=(0, 10)); profile_frame.columnconfigure(1, weight=1) tb.Label(profile_frame, text="Active Profile:").grid(row=0, column=0, padx=5, pady=5, sticky="w") diff --git a/test_integration.py b/test_integration.py new file mode 100644 index 0000000..22de46f --- /dev/null +++ b/test_integration.py @@ -0,0 +1,38 @@ +# Test script per verificare l'integrazione del batch converter + +import sys +import os + +# Aggiungi il percorso del modulo al path +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +try: + # Test import dei moduli principali + print("Testing imports...") + from markdownconverter.gui.gui import MarkdownConverterApp + from markdownconverter.gui.batch_converter import BatchConverterTab + from markdownconverter.core.core import ( + combine_markdown_files, + convert_markdown_to_docx_with_pandoc, + convert_markdown, + convert_docx_to_pdf + ) + print("✅ All imports successful!") + + # Test avvio GUI (opzionale - commentato per non bloccare) + print("\nTo test the GUI, uncomment the following lines:") + print("# import ttkbootstrap as tb") + print("# root = tb.Window(themename='sandstone')") + print("# app = MarkdownConverterApp(root)") + print("# root.mainloop()") + + print("\n✅ Integration test passed!") + print("\nYou can now run the application with:") + print(" python -m markdownconverter") + +except ImportError as e: + print(f"❌ Import error: {e}") + sys.exit(1) +except Exception as e: + print(f"❌ Error: {e}") + sys.exit(1)