|
| 1 | +import os |
| 2 | +import tkinter as tk |
| 3 | +from tkinter import filedialog, messagebox |
| 4 | +import ttkbootstrap as tb |
| 5 | +from ttkbootstrap.constants import * |
| 6 | +from PyPDF2 import PdfMerger |
| 7 | + |
| 8 | +# ---------------- GLOBALS ---------------- # |
| 9 | +pdf_files = [] |
| 10 | + |
| 11 | +# ---------------- FUNCTIONS ---------------- # |
| 12 | +def add_pdfs(): |
| 13 | + files = filedialog.askopenfilenames( |
| 14 | + title="Select PDF files", |
| 15 | + filetypes=[("PDF Files", "*.pdf")] |
| 16 | + ) |
| 17 | + for f in files: |
| 18 | + if f not in pdf_files: |
| 19 | + pdf_files.append(f) |
| 20 | + listbox.insert(tk.END, os.path.basename(f)) |
| 21 | + |
| 22 | +def remove_selected(): |
| 23 | + sel = listbox.curselection() |
| 24 | + if not sel: |
| 25 | + return |
| 26 | + index = sel[0] |
| 27 | + pdf_files.pop(index) |
| 28 | + listbox.delete(index) |
| 29 | + |
| 30 | +def move_up(): |
| 31 | + sel = listbox.curselection() |
| 32 | + if not sel or sel[0] == 0: |
| 33 | + return |
| 34 | + i = sel[0] |
| 35 | + pdf_files[i-1], pdf_files[i] = pdf_files[i], pdf_files[i-1] |
| 36 | + refresh_list() |
| 37 | + listbox.selection_set(i-1) |
| 38 | + |
| 39 | +def move_down(): |
| 40 | + sel = listbox.curselection() |
| 41 | + if not sel or sel[0] == len(pdf_files)-1: |
| 42 | + return |
| 43 | + i = sel[0] |
| 44 | + pdf_files[i+1], pdf_files[i] = pdf_files[i], pdf_files[i+1] |
| 45 | + refresh_list() |
| 46 | + listbox.selection_set(i+1) |
| 47 | + |
| 48 | +def refresh_list(): |
| 49 | + listbox.delete(0, tk.END) |
| 50 | + for f in pdf_files: |
| 51 | + listbox.insert(tk.END, os.path.basename(f)) |
| 52 | + |
| 53 | +def merge_pdfs(): |
| 54 | + if not pdf_files: |
| 55 | + messagebox.showwarning("No Files", "Add PDF files first.") |
| 56 | + return |
| 57 | + |
| 58 | + output_path = filedialog.asksaveasfilename( |
| 59 | + defaultextension=".pdf", |
| 60 | + filetypes=[("PDF Files", "*.pdf")], |
| 61 | + title="Save merged PDF as" |
| 62 | + ) |
| 63 | + if not output_path: |
| 64 | + return |
| 65 | + |
| 66 | + try: |
| 67 | + merger = PdfMerger() |
| 68 | + for pdf in pdf_files: |
| 69 | + merger.append(pdf) |
| 70 | + merger.write(output_path) |
| 71 | + merger.close() |
| 72 | + messagebox.showinfo("Success", "PDFs merged successfully!") |
| 73 | + except Exception as e: |
| 74 | + messagebox.showerror("Error", str(e)) |
| 75 | + |
| 76 | +# Drag & Drop reorder |
| 77 | +drag_data = None |
| 78 | +def on_drag_start(event): |
| 79 | + global drag_data |
| 80 | + drag_data = listbox.nearest(event.y) |
| 81 | + |
| 82 | +def on_drag_release(event): |
| 83 | + global drag_data |
| 84 | + if drag_data is None: |
| 85 | + return |
| 86 | + drop_index = listbox.nearest(event.y) |
| 87 | + if drop_index != drag_data: |
| 88 | + pdf_files.insert(drop_index, pdf_files.pop(drag_data)) |
| 89 | + refresh_list() |
| 90 | + listbox.selection_set(drop_index) |
| 91 | + drag_data = None |
| 92 | + |
| 93 | +# ---------------- GUI ---------------- # |
| 94 | +app = tb.Window( |
| 95 | + title="PDF Merger Tool", |
| 96 | + themename="darkly", |
| 97 | + size=(700, 450) |
| 98 | +) |
| 99 | + |
| 100 | +main_frame = tb.Frame(app) |
| 101 | +main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) |
| 102 | + |
| 103 | +# Listbox |
| 104 | +listbox = tk.Listbox(main_frame, height=15) |
| 105 | +listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) |
| 106 | +listbox.bind("<Button-1>", on_drag_start) |
| 107 | +listbox.bind("<ButtonRelease-1>", on_drag_release) |
| 108 | + |
| 109 | +# Controls |
| 110 | +control_frame = tb.Frame(main_frame) |
| 111 | +control_frame.pack(side=tk.RIGHT, fill=tk.Y, padx=10) |
| 112 | + |
| 113 | +tb.Button(control_frame, text="Add PDFs", bootstyle="primary", command=add_pdfs).pack(fill=tk.X, pady=5) |
| 114 | +tb.Button(control_frame, text="Remove Selected", bootstyle="danger", command=remove_selected).pack(fill=tk.X, pady=5) |
| 115 | +tb.Button(control_frame, text="Move Up", bootstyle="secondary", command=move_up).pack(fill=tk.X, pady=5) |
| 116 | +tb.Button(control_frame, text="Move Down", bootstyle="secondary", command=move_down).pack(fill=tk.X, pady=5) |
| 117 | +tb.Separator(control_frame).pack(fill=tk.X, pady=10) |
| 118 | +tb.Button(control_frame, text="Merge PDFs", bootstyle="success", command=merge_pdfs).pack(fill=tk.X, pady=5) |
| 119 | + |
| 120 | +app.mainloop() |
0 commit comments