Skip to content

Commit ab198bc

Browse files
authored
Create Image_Convert_BUSINESS_PRO.py
1 parent 62f424f commit ab198bc

1 file changed

Lines changed: 291 additions & 0 deletions

File tree

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
import os, sys, sqlite3, csv, time
2+
from threading import Thread
3+
from PIL import Image, ImageTk
4+
5+
try:
6+
from PIL import ImageResampling
7+
RESAMPLE = ImageResampling.LANCZOS
8+
except:
9+
RESAMPLE = Image.LANCZOS
10+
11+
import ttkbootstrap as tb
12+
from ttkbootstrap.constants import *
13+
from tkinter import filedialog, messagebox, Listbox, Canvas, Scrollbar, simpledialog
14+
from tkinterdnd2 import TkinterDnD, DND_FILES
15+
16+
# ---------------- APP INFO ----------------
17+
APP_NAME = "Image Convert BUSINESS PRO"
18+
APP_VERSION = "2.0"
19+
20+
# ---------------- PATHS ----------------
21+
BASE_DIR = os.path.dirname(sys.argv[0])
22+
DB_NAME = os.path.join(BASE_DIR, "snapconvert.db")
23+
OUTPUT_DIR = os.path.join(BASE_DIR, "converted")
24+
LOG_FILE = os.path.join(BASE_DIR, "conversion.log")
25+
LICENSE_FILE = os.path.join(BASE_DIR, "license.key")
26+
27+
VALID_LICENSES = ["PRO-2026-XYZ123", "PRO-2026-ABC789"] # Example valid licenses
28+
29+
# ---------------- DATABASE ----------------
30+
def init_db():
31+
conn = sqlite3.connect(DB_NAME)
32+
c = conn.cursor()
33+
c.execute("""CREATE TABLE IF NOT EXISTS history(
34+
id INTEGER PRIMARY KEY,
35+
name TEXT,
36+
original TEXT,
37+
converted TEXT)""")
38+
conn.commit()
39+
conn.close()
40+
41+
def insert_db(name, orig, conv):
42+
conn = sqlite3.connect(DB_NAME)
43+
c = conn.cursor()
44+
c.execute("INSERT INTO history(name, original, converted) VALUES(?,?,?)",(name,orig,conv))
45+
conn.commit()
46+
conn.close()
47+
log_event(f"Converted {name} -> {conv}")
48+
49+
def fetch_db():
50+
conn = sqlite3.connect(DB_NAME)
51+
c = conn.cursor()
52+
c.execute("SELECT name, original, converted FROM history ORDER BY id DESC")
53+
rows = c.fetchall()
54+
conn.close()
55+
return rows
56+
57+
def clear_history():
58+
conn = sqlite3.connect(DB_NAME)
59+
c = conn.cursor()
60+
c.execute("DELETE FROM history")
61+
conn.commit()
62+
conn.close()
63+
log_event("History cleared")
64+
65+
# ---------------- LOGGING ----------------
66+
def log_event(msg):
67+
with open(LOG_FILE, "a") as f:
68+
f.write(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] {msg}\n")
69+
70+
def export_history_csv():
71+
rows = fetch_db()
72+
if not rows:
73+
messagebox.showinfo("Export History","No history to export")
74+
return
75+
path = filedialog.asksaveasfilename(defaultextension=".csv", filetypes=[("CSV file","*.csv")])
76+
if path:
77+
with open(path,"w", newline="", encoding="utf-8") as f:
78+
writer = csv.writer(f)
79+
writer.writerow(["Name","Original Path","Converted Path"])
80+
writer.writerows(rows)
81+
messagebox.showinfo("Export History", f"Exported {len(rows)} records")
82+
83+
# ---------------- LICENSE SYSTEM ----------------
84+
def check_license():
85+
if os.path.exists(LICENSE_FILE):
86+
with open(LICENSE_FILE,"r") as f:
87+
key = f.read().strip()
88+
if key in VALID_LICENSES:
89+
return True
90+
# Prompt user for license
91+
key = simpledialog.askstring("License Required","Enter your license key:")
92+
if key and key in VALID_LICENSES:
93+
with open(LICENSE_FILE,"w") as f:
94+
f.write(key)
95+
messagebox.showinfo("License Activated","License activated successfully!")
96+
return True
97+
else:
98+
messagebox.showerror("Invalid License","The license key is invalid. App will exit.")
99+
return False
100+
101+
# ---------------- ABOUT ----------------
102+
def show_about():
103+
messagebox.showinfo(
104+
f"About {APP_NAME}",
105+
f"{APP_NAME} v{APP_VERSION}\n\n"
106+
"Professional Image Converter Business Edition\n"
107+
"© 2026 Mate Technologies\n"
108+
"https://matetools.gumroad.com"
109+
)
110+
111+
# ---------------- WORKER ----------------
112+
def worker(images, fmt, out, quality, resize, keep, progress, finish):
113+
os.makedirs(out, exist_ok=True)
114+
total = len(images)
115+
count = 0
116+
117+
for i, path in enumerate(images):
118+
try:
119+
with Image.open(path) as img:
120+
if resize>0:
121+
img = img.resize((resize,resize),RESAMPLE)
122+
if fmt=="JPEG" and img.mode in ("RGBA","P"):
123+
img=img.convert("RGB")
124+
name=os.path.splitext(os.path.basename(path))[0]
125+
if not keep:
126+
name+=f"_{i+1}"
127+
out_path=os.path.join(out,f"{name}.{fmt.lower()}")
128+
c=1
129+
while os.path.exists(out_path):
130+
out_path=os.path.join(out,f"{name}_{c}.{fmt.lower()}")
131+
c+=1
132+
params={"quality":quality} if fmt=="JPEG" else {}
133+
img.save(out_path, fmt, **params)
134+
insert_db(name,path,out_path)
135+
count+=1
136+
except Exception as e:
137+
log_event(f"Error converting {path}: {e}")
138+
progress(int((i+1)/total*100))
139+
finish(count)
140+
141+
# ---------------- APP ----------------
142+
class App:
143+
def __init__(self):
144+
if not check_license():
145+
sys.exit()
146+
147+
self.root = TkinterDnD.Tk()
148+
self.root.title(APP_NAME)
149+
self.root.geometry("1250x750")
150+
self.style = tb.Style("darkly")
151+
152+
self.images=[]
153+
self.thumbs=[]
154+
155+
self.create_menu()
156+
self.build_ui()
157+
self.load_history()
158+
159+
self.root.drop_target_register(DND_FILES)
160+
self.root.dnd_bind("<<Drop>>", self.drop)
161+
162+
def create_menu(self):
163+
menubar = tb.Menu(self.root)
164+
help_menu = tb.Menu(menubar, tearoff=0)
165+
help_menu.add_command(label="About",command=show_about)
166+
menubar.add_cascade(label="Help",menu=help_menu)
167+
self.root.config(menu=menubar)
168+
169+
def build_ui(self):
170+
main=tb.Frame(self.root)
171+
main.pack(fill=BOTH,expand=True)
172+
173+
# LEFT
174+
left=tb.Frame(main,width=250,padding=5)
175+
left.pack(side=LEFT,fill=Y)
176+
tb.Label(left,text="📂 Files", font=("Arial",12)).pack(pady=5)
177+
self.listbox=Listbox(left,bg="#1e1e1e",fg="white")
178+
self.listbox.pack(fill=BOTH,expand=True,pady=5)
179+
tb.Button(left,text="Add Images",command=self.add_files,bootstyle=SUCCESS).pack(fill=X,pady=2)
180+
tb.Button(left,text="Add Folder",command=self.add_folder,bootstyle=INFO).pack(fill=X,pady=2)
181+
tb.Button(left,text="Remove",command=self.remove_selected,bootstyle=DANGER).pack(fill=X,pady=2)
182+
tb.Button(left,text="Clear All",command=self.clear_all,bootstyle=SECONDARY).pack(fill=X,pady=2)
183+
184+
# CENTER
185+
center=tb.Frame(main,padding=5)
186+
center.pack(side=LEFT,fill=BOTH,expand=True)
187+
self.canvas=Canvas(center,bg="#121212")
188+
self.scroll=Scrollbar(center,command=self.canvas.yview)
189+
self.inner=tb.Frame(self.canvas)
190+
self.inner.bind("<Configure>",lambda e:self.canvas.configure(scrollregion=self.canvas.bbox("all")))
191+
self.canvas.create_window((0,0),window=self.inner,anchor="nw")
192+
self.canvas.configure(yscrollcommand=self.scroll.set)
193+
self.canvas.pack(side=LEFT,fill=BOTH,expand=True)
194+
self.scroll.pack(side=RIGHT,fill=Y)
195+
196+
# RIGHT
197+
right=tb.Frame(main,width=260,padding=10)
198+
right.pack(side=RIGHT,fill=Y)
199+
tb.Label(right,text="⚙ Settings", font=("Arial",12)).pack(pady=5)
200+
tb.Label(right,text="Format").pack(anchor="w")
201+
self.format=tb.Combobox(right,values=["PNG","JPEG","WEBP","BMP","TIFF"])
202+
self.format.current(0)
203+
self.format.pack(fill=X,pady=5)
204+
tb.Label(right,text="JPEG Quality").pack(anchor="w")
205+
self.quality=tb.Spinbox(right,from_=10,to=100)
206+
self.quality.set(90)
207+
self.quality.pack(fill=X,pady=5)
208+
tb.Label(right,text="Resize (px)").pack(anchor="w")
209+
self.resize=tb.Spinbox(right,from_=0,to=5000)
210+
self.resize.set(0)
211+
self.resize.pack(fill=X,pady=5)
212+
self.keep=tb.Checkbutton(right,text="Keep original filename")
213+
self.keep.invoke()
214+
self.keep.pack(pady=5)
215+
tb.Separator(right).pack(fill=X,pady=10)
216+
tb.Button(right,text="🚀 Convert",command=self.convert,bootstyle=WARNING).pack(fill=X,pady=5)
217+
self.progress=tb.Progressbar(right)
218+
self.progress.pack(fill=X,pady=5)
219+
self.status=tb.Label(right,text="Ready")
220+
self.status.pack(pady=5)
221+
tb.Separator(right).pack(fill=X,pady=10)
222+
tb.Button(right,text="🧹 Delete History",command=self.delete_history,bootstyle=DANGER).pack(fill=X,pady=5)
223+
tb.Button(right,text="📤 Export History CSV",command=export_history_csv,bootstyle=INFO).pack(fill=X,pady=5)
224+
225+
# HISTORY TABLE
226+
self.table=tb.Treeview(self.root,columns=("n","o","c"),show="headings")
227+
self.table.heading("n",text="Name")
228+
self.table.heading("o",text="Original")
229+
self.table.heading("c",text="Converted")
230+
self.table.pack(fill=BOTH,expand=True)
231+
232+
# FUNCTIONS
233+
def drop(self,e): self.add_images(self.root.tk.splitlist(e.data))
234+
def add_files(self): self.add_images(filedialog.askopenfilenames())
235+
def add_folder(self):
236+
folder=filedialog.askdirectory()
237+
imgs=[]
238+
for r,_,f in os.walk(folder):
239+
for x in f:
240+
if x.lower().endswith(("png","jpg","jpeg","bmp","gif")):
241+
imgs.append(os.path.join(r,x))
242+
self.add_images(imgs)
243+
def add_images(self,paths):
244+
for p in paths:
245+
if p not in self.images:
246+
self.images.append(p)
247+
self.listbox.insert(END,os.path.basename(p))
248+
self.render_gallery()
249+
def remove_selected(self):
250+
sel=list(self.listbox.curselection());sel.reverse()
251+
for i in sel:
252+
self.images.pop(i)
253+
self.listbox.delete(i)
254+
self.render_gallery()
255+
def clear_all(self):
256+
self.images=[];self.listbox.delete(0,END)
257+
for w in self.inner.winfo_children(): w.destroy()
258+
def render_gallery(self):
259+
for w in self.inner.winfo_children(): w.destroy()
260+
self.thumbs.clear()
261+
cols=4
262+
for i,path in enumerate(self.images[:50]):
263+
try:
264+
img=Image.open(path)
265+
img.thumbnail((150,150))
266+
tkimg=ImageTk.PhotoImage(img)
267+
self.thumbs.append(tkimg)
268+
frame=tb.Frame(self.inner)
269+
frame.grid(row=i//cols,column=i%cols,padx=10,pady=10)
270+
tb.Label(frame,image=tkimg).pack()
271+
tb.Label(frame,text=os.path.basename(path),wraplength=140).pack()
272+
except: pass
273+
def convert(self):
274+
if not self.images: messagebox.showwarning("No images","Add images first"); return
275+
fmt=self.format.get();q=int(self.quality.get());r=int(self.resize.get());keep=self.keep.instate(["selected"])
276+
def prog(v): self.root.after(0,lambda:(self.progress.config(value=v),self.status.config(text=f"{v}%")))
277+
def done(c): self.root.after(0,lambda:(self.status.config(text=f"Done: {c} images"),self.progress.config(value=0),self.load_history()))
278+
Thread(target=worker,args=(self.images,fmt,OUTPUT_DIR,q,r,keep,prog,done),daemon=True).start()
279+
def delete_history(self):
280+
if messagebox.askyesno("Confirm","Delete all history?"):
281+
clear_history()
282+
self.load_history()
283+
def load_history(self):
284+
for i in self.table.get_children(): self.table.delete(i)
285+
for row in fetch_db(): self.table.insert("",END,values=row)
286+
def run(self): self.root.mainloop()
287+
288+
# ---------------- RUN ----------------
289+
if __name__=="__main__":
290+
init_db()
291+
App().run()

0 commit comments

Comments
 (0)