Skip to content

Commit 656b59a

Browse files
authored
Create PDF-Merger-Preview-Tool.py
1 parent acbf6c3 commit 656b59a

1 file changed

Lines changed: 148 additions & 0 deletions

File tree

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import os
2+
import threading
3+
import tkinter as tk
4+
from tkinter import filedialog, messagebox
5+
from tkinterdnd2 import DND_FILES, TkinterDnD
6+
import ttkbootstrap as tb
7+
from ttkbootstrap.constants import *
8+
from PyPDF2 import PdfMerger, PdfReader
9+
from pdf2image import convert_from_path
10+
from PIL import Image, ImageTk
11+
12+
# ---------------- GLOBALS ---------------- #
13+
pdf_files = []
14+
preview_cache = {}
15+
16+
# ---------------- FUNCTIONS ---------------- #
17+
def add_pdfs(files=None):
18+
if not files:
19+
files = filedialog.askopenfilenames(
20+
filetypes=[("PDF Files", "*.pdf")]
21+
)
22+
for f in files:
23+
f = f.strip("{}")
24+
if f.lower().endswith(".pdf") and f not in pdf_files:
25+
pdf_files.append(f)
26+
listbox.insert(tk.END, os.path.basename(f))
27+
28+
def on_drop(event):
29+
files = app.tk.splitlist(event.data)
30+
add_pdfs(files)
31+
32+
def remove_selected():
33+
sel = listbox.curselection()
34+
if not sel:
35+
return
36+
i = sel[0]
37+
pdf_files.pop(i)
38+
listbox.delete(i)
39+
preview_label.config(image='')
40+
41+
def refresh_list():
42+
listbox.delete(0, tk.END)
43+
for f in pdf_files:
44+
listbox.insert(tk.END, os.path.basename(f))
45+
46+
def merge_pdfs():
47+
if not pdf_files:
48+
messagebox.showwarning("No PDFs", "Add PDF files first.")
49+
return
50+
51+
out = filedialog.asksaveasfilename(
52+
defaultextension=".pdf",
53+
filetypes=[("PDF Files", "*.pdf")]
54+
)
55+
if not out:
56+
return
57+
58+
def task():
59+
merger = PdfMerger()
60+
total = len(pdf_files)
61+
for i, pdf in enumerate(pdf_files):
62+
merger.append(pdf)
63+
progress_var.set((i + 1) / total * 100)
64+
merger.write(out)
65+
merger.close()
66+
messagebox.showinfo("Done", "PDFs merged successfully!")
67+
68+
threading.Thread(target=task, daemon=True).start()
69+
70+
def show_preview(event=None):
71+
sel = listbox.curselection()
72+
if not sel:
73+
return
74+
path = pdf_files[sel[0]]
75+
76+
if path in preview_cache:
77+
preview_label.config(image=preview_cache[path])
78+
preview_label.image = preview_cache[path]
79+
return
80+
81+
try:
82+
img = convert_from_path(path, first_page=1, last_page=1)[0]
83+
img.thumbnail((300, 400))
84+
tk_img = ImageTk.PhotoImage(img)
85+
preview_cache[path] = tk_img
86+
preview_label.config(image=tk_img)
87+
preview_label.image = tk_img
88+
except Exception as e:
89+
messagebox.showerror("Preview Error", str(e))
90+
91+
# Drag reorder
92+
drag_index = None
93+
def drag_start(e):
94+
global drag_index
95+
drag_index = listbox.nearest(e.y)
96+
97+
def drag_release(e):
98+
global drag_index
99+
drop = listbox.nearest(e.y)
100+
if drag_index is not None and drop != drag_index:
101+
pdf_files.insert(drop, pdf_files.pop(drag_index))
102+
refresh_list()
103+
listbox.selection_set(drop)
104+
drag_index = None
105+
106+
# ---------------- GUI ---------------- #
107+
app = TkinterDnD.Tk()
108+
app.title("PDF Merger Tool")
109+
app.geometry("900x550")
110+
tb.Style("darkly")
111+
112+
app.drop_target_register(DND_FILES)
113+
app.dnd_bind("<<Drop>>", on_drop)
114+
115+
main = tb.Frame(app, padding=10)
116+
main.pack(fill=BOTH, expand=True)
117+
118+
# Left
119+
left = tb.Frame(main)
120+
left.pack(side=LEFT, fill=BOTH, expand=True)
121+
122+
listbox = tk.Listbox(left)
123+
listbox.pack(fill=BOTH, expand=True)
124+
listbox.bind("<<ListboxSelect>>", show_preview)
125+
listbox.bind("<Button-1>", drag_start)
126+
listbox.bind("<ButtonRelease-1>", drag_release)
127+
128+
tb.Button(left, text="Add PDFs", command=add_pdfs, bootstyle=PRIMARY).pack(fill=X, pady=5)
129+
tb.Button(left, text="Remove Selected", command=remove_selected, bootstyle=DANGER).pack(fill=X)
130+
131+
# Right
132+
right = tb.Frame(main)
133+
right.pack(side=RIGHT, fill=Y, padx=10)
134+
135+
preview_label = tk.Label(right)
136+
preview_label.pack(pady=10)
137+
138+
progress_var = tk.DoubleVar()
139+
progress = tb.Progressbar(
140+
right, variable=progress_var, maximum=100, bootstyle=SUCCESS
141+
)
142+
progress.pack(fill=X, pady=10)
143+
144+
tb.Button(
145+
right, text="Merge PDFs", command=merge_pdfs, bootstyle=SUCCESS
146+
).pack(fill=X)
147+
148+
app.mainloop()

0 commit comments

Comments
 (0)