aggiunta la nuova funzione per unire più file markdown in un unico file per creare il manuale completo
This commit is contained in:
parent
f7f6286930
commit
af94f256af
@ -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",
|
||||
|
||||
147
convert.py
Normal file
147
convert.py
Normal file
@ -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()
|
||||
78
doc/BATCH_CONVERTER.md
Normal file
78
doc/BATCH_CONVERTER.md
Normal file
@ -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.
|
||||
@ -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,
|
||||
|
||||
332
markdownconverter/gui/batch_converter.py
Normal file
332
markdownconverter/gui/batch_converter.py
Normal file
@ -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"]
|
||||
@ -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")
|
||||
|
||||
38
test_integration.py
Normal file
38
test_integration.py
Normal file
@ -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)
|
||||
Loading…
Reference in New Issue
Block a user