Spaces:
Running
Running
| #!/usr/bin/env python3 | |
| """ | |
| Scan2Doc Pro v6.0 β GUI | |
| Cyberpunk Red-Dark Theme | |
| Created by Us.Abolfazl | |
| """ | |
| import os | |
| import sys | |
| import threading | |
| import tkinter as tk | |
| from tkinter import filedialog, messagebox, ttk | |
| sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) | |
| from scan2doc_pro_v6 import process_file, SUPPORTED_EXTS, find_tesseract | |
| # βββ Theme Colors βββββββββββββββββββββββββββββββββββββββββββββββββ | |
| BG_DARK = "#0d0d0d" | |
| BG_CARD = "#1a1a1a" | |
| BG_INPUT = "#252525" | |
| RED = "#ff1744" | |
| RED_HOVER = "#ff5252" | |
| RED_DARK = "#b71c1c" | |
| WHITE = "#ffffff" | |
| GRAY = "#b0b0b0" | |
| DIM = "#666666" | |
| GREEN = "#00e676" | |
| BORDER = "#333333" | |
| class Scan2DocGUI: | |
| def __init__(self): | |
| self.root = tk.Tk() | |
| self.root.title("Scan2Doc Pro v6.0 β Created by Us.Abolfazl") | |
| self.root.geometry("850x700") | |
| self.root.minsize(700, 600) | |
| self.root.configure(bg=BG_DARK) | |
| # Icon | |
| icon_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "icon.ico") | |
| if os.path.exists(icon_path): | |
| try: | |
| self.root.iconbitmap(icon_path) | |
| except Exception: | |
| pass | |
| self.files = [] | |
| self.processing = False | |
| self._build_ui() | |
| def _build_ui(self): | |
| # βββ Header βββββββββββββββββββββββββββββββββββββββββββββ | |
| hdr = tk.Frame(self.root, bg=BG_CARD, bd=0, highlightthickness=0) | |
| hdr.pack(fill=tk.X, padx=10, pady=(10, 5)) | |
| tk.Label(hdr, text="β‘ Scan2Doc Pro v6.0", | |
| font=("Arial", 20, "bold"), fg=RED, bg=BG_CARD).pack(anchor="w", padx=15, pady=8) | |
| tk.Label(hdr, text="Created by Us.Abolfazl", | |
| font=("Arial", 11), fg=DIM, bg=BG_CARD).pack(anchor="w", padx=15, pady=(0, 8)) | |
| # βββ File List ββββββββββββββββββββββββββββββββββββββββββ | |
| file_frame = tk.Frame(self.root, bg=BG_CARD, bd=0, highlightthickness=0) | |
| file_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) | |
| tk.Label(file_frame, text="π Input Files", | |
| font=("Arial", 13, "bold"), fg=RED, bg=BG_CARD).pack(anchor="w", padx=10, pady=(8, 0)) | |
| list_frame = tk.Frame(file_frame, bg=BG_INPUT, bd=0) | |
| list_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) | |
| scrollbar = tk.Scrollbar(list_frame) | |
| scrollbar.pack(side=tk.RIGHT, fill=tk.Y) | |
| self.file_listbox = tk.Listbox(list_frame, bg=BG_INPUT, fg=WHITE, | |
| selectbackground=RED, selectforeground=WHITE, | |
| font=("Consolas", 11), bd=0, highlightthickness=0, | |
| yscrollcommand=scrollbar.set) | |
| self.file_listbox.pack(fill=tk.BOTH, expand=True) | |
| scrollbar.config(command=self.file_listbox.yview) | |
| # Buttons | |
| btn_frame = tk.Frame(file_frame, bg=BG_CARD) | |
| btn_frame.pack(fill=tk.X, padx=10, pady=5) | |
| self._make_btn(btn_frame, "β Add Files", self._add_files, RED).pack(side=tk.LEFT, padx=3) | |
| self._make_btn(btn_frame, "π Add Folder", self._add_folder, RED_DARK).pack(side=tk.LEFT, padx=3) | |
| self._make_btn(btn_frame, "β Remove", self._remove, "#444444").pack(side=tk.LEFT, padx=3) | |
| self._make_btn(btn_frame, "ποΈ Clear All", self._clear, "#444444").pack(side=tk.LEFT, padx=3) | |
| # βββ Settings βββββββββββββββββββββββββββββββββββββββββββ | |
| settings = tk.Frame(self.root, bg=BG_CARD, bd=0) | |
| settings.pack(fill=tk.X, padx=10, pady=5) | |
| tk.Label(settings, text="βοΈ Settings", | |
| font=("Arial", 13, "bold"), fg=RED, bg=BG_CARD).pack(anchor="w", padx=10, pady=(8, 0)) | |
| grid = tk.Frame(settings, bg=BG_CARD) | |
| grid.pack(fill=tk.X, padx=10, pady=5) | |
| # Language | |
| tk.Label(grid, text="Language:", fg=GRAY, bg=BG_CARD).grid(row=0, column=0, sticky="w", padx=5, pady=3) | |
| self.lang_var = tk.StringVar(value="eng+fas") | |
| tk.Entry(grid, textvariable=self.lang_var, width=15, bg=BG_INPUT, fg=WHITE, | |
| insertbackground=WHITE, bd=0).grid(row=0, column=1, sticky="w", padx=5, pady=3) | |
| # DPI | |
| tk.Label(grid, text="DPI:", fg=GRAY, bg=BG_CARD).grid(row=0, column=2, sticky="w", padx=15, pady=3) | |
| self.dpi_var = tk.StringVar(value="300") | |
| tk.Entry(grid, textvariable=self.dpi_var, width=8, bg=BG_INPUT, fg=WHITE, | |
| insertbackground=WHITE, bd=0).grid(row=0, column=3, sticky="w", padx=5, pady=3) | |
| # Title | |
| tk.Label(grid, text="Title:", fg=GRAY, bg=BG_CARD).grid(row=1, column=0, sticky="w", padx=5, pady=3) | |
| self.title_var = tk.StringVar(value="Scan2Doc Output") | |
| tk.Entry(grid, textvariable=self.title_var, width=15, bg=BG_INPUT, fg=WHITE, | |
| insertbackground=WHITE, bd=0).grid(row=1, column=1, sticky="w", padx=5, pady=3) | |
| # Font | |
| tk.Label(grid, text="Font:", fg=GRAY, bg=BG_CARD).grid(row=1, column=2, sticky="w", padx=15, pady=3) | |
| self.font_var = tk.StringVar(value="B Nazanin") | |
| tk.Entry(grid, textvariable=self.font_var, width=15, bg=BG_INPUT, fg=WHITE, | |
| insertbackground=WHITE, bd=0).grid(row=1, column=3, sticky="w", padx=5, pady=3) | |
| # βββ Progress βββββββββββββββββββββββββββββββββββββββββββ | |
| prog_frame = tk.Frame(self.root, bg=BG_CARD, bd=0) | |
| prog_frame.pack(fill=tk.X, padx=10, pady=5) | |
| self.progress_label = tk.Label(prog_frame, text="Ready", | |
| font=("Arial", 12), fg=GRAY, bg=BG_CARD) | |
| self.progress_label.pack(anchor="w", padx=10, pady=(5, 0)) | |
| style = ttk.Style() | |
| style.theme_use("default") | |
| style.configure("Red.Horizontal.TProgressbar", | |
| troughcolor=BG_INPUT, background=RED, borderwidth=0) | |
| self.progress_bar = ttk.Progressbar(prog_frame, style="Red.Horizontal.TProgressbar", | |
| length=400, mode="determinate") | |
| self.progress_bar.pack(fill=tk.X, padx=10, pady=5) | |
| # βββ Log ββββββββββββββββββββββββββββββββββββββββββββββββ | |
| log_frame = tk.Frame(self.root, bg=BG_CARD, bd=0) | |
| log_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) | |
| tk.Label(log_frame, text="π Log", | |
| font=("Arial", 13, "bold"), fg=RED, bg=BG_CARD).pack(anchor="w", padx=10, pady=(8, 0)) | |
| log_inner = tk.Frame(log_frame, bg=BG_INPUT, bd=0) | |
| log_inner.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) | |
| log_scroll = tk.Scrollbar(log_inner) | |
| log_scroll.pack(side=tk.RIGHT, fill=tk.Y) | |
| self.log_text = tk.Text(log_inner, bg=BG_INPUT, fg=WHITE, font=("Consolas", 11), | |
| bd=0, highlightthickness=0, wrap=tk.WORD, | |
| yscrollcommand=log_scroll.set) | |
| self.log_text.pack(fill=tk.BOTH, expand=True) | |
| log_scroll.config(command=self.log_text.yview) | |
| # βββ Bottom βββββββββββββββββββββββββββββββββββββββββββββ | |
| bottom = tk.Frame(self.root, bg=BG_DARK) | |
| bottom.pack(fill=tk.X, padx=10, pady=5) | |
| tk.Label(bottom, text="β‘ Created by Us.Abolfazl", | |
| font=("Arial", 10), fg=RED, bg=BG_DARK).pack(side=tk.LEFT, padx=10) | |
| self.process_btn = tk.Button(bottom, text="π Process", width=18, | |
| font=("Arial", 14, "bold"), | |
| bg=RED, fg=WHITE, activebackground=RED_HOVER, | |
| activeforeground=WHITE, bd=0, cursor="hand2", | |
| command=self._start_processing) | |
| self.process_btn.pack(side=tk.RIGHT, padx=10) | |
| def _make_btn(self, parent, text, cmd, bg_color): | |
| return tk.Button(parent, text=text, command=cmd, bg=bg_color, fg=WHITE, | |
| activebackground=RED_HOVER, activeforeground=WHITE, | |
| bd=0, font=("Arial", 10), cursor="hand2", padx=8, pady=4) | |
| def _add_files(self): | |
| filetypes = [ | |
| ("All supported", " ".join(f"*{e}" for e in SUPPORTED_EXTS)), | |
| ("Images", "*.jpg *.jpeg *.png *.tiff *.tif *.bmp *.gif *.webp"), | |
| ("PDFs", "*.pdf"), | |
| ] | |
| paths = filedialog.askopenfilenames(filetypes=filetypes) | |
| for p in paths: | |
| if p not in self.files: | |
| self.files.append(p) | |
| self.file_listbox.insert(tk.END, p) | |
| self._update_label() | |
| def _add_folder(self): | |
| folder = filedialog.askdirectory() | |
| if not folder: | |
| return | |
| for f in sorted(os.listdir(folder)): | |
| ext = os.path.splitext(f)[1].lower() | |
| if ext in SUPPORTED_EXTS: | |
| full = os.path.join(folder, f) | |
| if full not in self.files: | |
| self.files.append(full) | |
| self.file_listbox.insert(tk.END, full) | |
| self._update_label() | |
| def _remove(self): | |
| sel = self.file_listbox.curselection() | |
| for idx in reversed(sel): | |
| self.file_listbox.delete(idx) | |
| del self.files[idx] | |
| self._update_label() | |
| def _clear(self): | |
| self.files.clear() | |
| self.file_listbox.delete(0, tk.END) | |
| self._update_label() | |
| def _update_label(self): | |
| self.progress_label.config(text=f"Files: {len(self.files)}") | |
| def _log(self, msg): | |
| self.root.after(0, lambda: ( | |
| self.log_text.insert(tk.END, msg + "\n"), | |
| self.log_text.see(tk.END) | |
| )) | |
| def _start_processing(self): | |
| if self.processing: | |
| messagebox.showinfo("Info", "Processing is already running.") | |
| return | |
| if not self.files: | |
| messagebox.showwarning("Warning", "No files to process. Add files first.") | |
| return | |
| output_path = filedialog.asksaveasfilename( | |
| defaultextension=".docx", | |
| filetypes=[("Word Document", "*.docx")], | |
| initialfile="output.docx" | |
| ) | |
| if not output_path: | |
| return | |
| self.processing = True | |
| self.process_btn.config(state="disabled", bg=DIM) | |
| self.log_text.delete("1.0", tk.END) | |
| lang = self.lang_var.get() | |
| title = self.title_var.get() | |
| font = self.font_var.get() | |
| try: | |
| dpi = int(self.dpi_var.get()) | |
| except ValueError: | |
| dpi = 300 | |
| def worker(): | |
| try: | |
| self._log("β‘ Starting...") | |
| # Process each file | |
| for idx, fpath in enumerate(self.files): | |
| self._log(f" [{idx+1}/{len(self.files)}] {os.path.basename(fpath)}") | |
| out = os.path.join(os.path.dirname(output_path) or ".", | |
| f"output_{idx+1}.docx") if len(self.files) > 1 else output_path | |
| success = process_file(fpath, out, lang, title, font, dpi) | |
| if success: | |
| self._log(f" β Saved: {out}") | |
| else: | |
| self._log(f" β Failed: {fpath}") | |
| self._log(f"\nβ All done! Check: {output_path}") | |
| self.root.after(0, lambda: self.progress_label.config(text="Done!", fg=GREEN)) | |
| except Exception as e: | |
| import traceback | |
| self._log(f"β Error: {e}") | |
| self._log(traceback.format_exc()) | |
| self.root.after(0, lambda: self.progress_label.config(text="Error!", fg="#ff1744")) | |
| finally: | |
| self.processing = False | |
| self.root.after(0, lambda: self.process_btn.config(state="normal", bg=RED)) | |
| threading.Thread(target=worker, daemon=True).start() | |
| def run(self): | |
| self.root.mainloop() | |
| def main(): | |
| gui = Scan2DocGUI() | |
| gui.run() | |
| if __name__ == "__main__": | |
| main() | |