From 08c3d373e3a2a418e6a657bf66557be93169f867 Mon Sep 17 00:00:00 2001
From: najeeb
Date: Sat, 9 May 2026 01:51:13 +0500
Subject: [PATCH] feat: Add GUI v1.1 features (drag-and-drop, persistence,
export logs, open output)
---
CHANGELOG.md | 12 ++++-
README.md | 20 +++++--
kobackupdec_gui.py | 129 ++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 154 insertions(+), 7 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9f612c7..74afe1c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,7 +2,17 @@
All notable changes to this project will be documented in this file.
-## [GUI Edition] — 2026-05-08
+## [GUI_v1.1] — 2026-05-08
+
+### Added
+- **Drag-and-Drop:** Added `tkinterdnd2` support allowing users to drag backup folders directly into the application.
+- **Settings Persistence:** Created `config.json` integration to save and automatically load user preferences and selected directories across sessions.
+- **Log Exporting:** Added an "Export Log" button to save the current terminal output to a text file for auditing.
+- **Output Folder Quick Access:** Added an "Open Output" button that becomes clickable after a successful decryption to instantly view the extracted files.
+
+---
+
+## [GUI_v1.0] — 2026-05-08
### Added
- **Full GUI application** (`kobackupdec_gui.py`) with modern dark theme
diff --git a/README.md b/README.md
index f7092d4..3fdec46 100644
--- a/README.md
+++ b/README.md
@@ -2,12 +2,12 @@
🔐 KoBackup Decryptor
Decrypt Huawei HiSuite & KoBackup encrypted backups
- CLI + Modern GUI • Selective Folder Decryption • Password Verification
+ CLI + Modern GUI • Selective Folder Decryption • Drag & Drop • Password Verification
-
+
@@ -39,7 +39,11 @@ This fork adds a **full-featured graphical interface** built with tkinter, bring
| Decrypt full backups | ✅ | ✅ |
| Password verification before decrypt | — | ✅ |
| **Selective folder decryption** | — | ✅ |
+| **Drag and Drop support** | — | ✅ |
+| **Settings persistence** | — | ✅ |
| Pause / Resume / Stop controls | — | ✅ |
+| Export decryption logs | — | ✅ |
+| Open Output folder button | — | ✅ |
| Real-time color-coded log output | — | ✅ |
| Progress tracking with status updates | — | ✅ |
| Responsive dark-themed interface | — | ✅ |
@@ -69,6 +73,7 @@ pip install -r requirements.txt
| Package | Purpose |
|---|---|
| `pycryptodome` | AES / PBKDF2 / HMAC cryptographic operations |
+| `tkinterdnd2` | Drag and drop functionality for the GUI |
| `tkinter` | GUI framework (bundled with Python on most platforms) |
> **Note:** On some Linux distributions, tkinter may need to be installed separately:
@@ -95,11 +100,12 @@ python kobackupdec_gui.py
#### GUI Workflow
1. **Enter Password** — Type your backup password (toggle visibility with 👁)
-2. **Select Backup Folder** — Browse to the Huawei backup directory
+2. **Select Backup Folder** — Drag and drop your Huawei backup directory right into the application, or click **Browse**
3. **Select Destination** — Choose where to save decrypted files (pick a parent, name the output folder)
4. **Configure Options** — Toggle TAR expansion, writable permissions, and log verbosity
-5. **Select Folders** — After setting the backup path, click **🔍 Scan** or it auto-scans to show available folders. Check/uncheck individual folders (pictures, video, audios, etc.)
+5. **Select Folders** — After setting the backup path, check/uncheck individual folders (pictures, video, audios, etc.) to decrypt only what you need
6. **Start Decryption** — Click **🔓 Start Decryption**
+7. **Open Output** — When finished, click **📂 Open Output** to view your files immediately
#### GUI Controls
@@ -108,6 +114,8 @@ python kobackupdec_gui.py
| 🔓 **Start Decryption** | Verifies password first, then begins decryption |
| ⏸ **Pause / ▶ Resume** | Temporarily halt and resume the process |
| ⏹ **Stop** | Cancel the decryption (partially decrypted files are kept) |
+| 📂 **Open Output** | Opens destination folder in Windows Explorer (enabled after success) |
+| **Export Log** | Save decryption logs to a text file for auditing |
| **Select All / Deselect All** | Quickly toggle all folder checkboxes |
| **🔍 Scan** | Re-scan backup directory for available folders |
| **Clear Log** | Clear the log output panel |
@@ -116,7 +124,9 @@ python kobackupdec_gui.py
- **🔑 Password Verification** — Validates the password against `info.xml` before starting decryption. Wrong passwords are caught instantly.
- **📂 Selective Folder Decryption** — Only decrypt what you need (e.g., just pictures and contacts, skip video and apps).
-- **📊 Real-Time Progress** — Status bar shows current phase and folder being processed.
+- **💾 Settings Persistence** — The app remembers your selected folders and checkboxes across launches via `config.json`.
+- **🖱️ Drag and Drop** — Seamlessly drop backup folders into the app instead of browsing manually.
+- **📊 Real-Time Progress & Logs** — Status bar shows current phase. Export logs anytime.
- **🎨 Dark Theme** — Modern, responsive dark interface with color-coded log levels (green=info, yellow=warning, red=error).
- **📐 Responsive Layout** — Resizes gracefully from 600×500 to fullscreen. Folder checkboxes reflow automatically.
diff --git a/kobackupdec_gui.py b/kobackupdec_gui.py
index 3be4cd6..a26df63 100644
--- a/kobackupdec_gui.py
+++ b/kobackupdec_gui.py
@@ -16,6 +16,9 @@ import tkinter as tk
from tkinter import filedialog, messagebox, simpledialog, ttk
import queue
import time
+import json
+import subprocess
+from tkinterdnd2 import TkinterDnD, DND_FILES
# ---------------------------------------------------------------------------
# Logging handler that forwards records into a thread-safe queue
@@ -36,7 +39,7 @@ class QueueHandler(logging.Handler):
# Main GUI Application
# ---------------------------------------------------------------------------
-class KoBackupDecGUI(tk.Tk):
+class KoBackupDecGUI(TkinterDnD.Tk):
"""Tkinter-based GUI for the Huawei KoBackup decryptor."""
# -- Colour palette (dark mode) ----------------------------------------
@@ -112,6 +115,12 @@ class KoBackupDecGUI(tk.Tk):
# Bind resize to reflow folder checkboxes
self.bind("", self._on_resize)
+ # Load saved settings
+ self._load_config()
+
+ # Save config on exit
+ self.protocol("WM_DELETE_WINDOW", self._on_closing)
+
# Start polling the log queue
self._poll_log_queue()
@@ -245,6 +254,10 @@ class KoBackupDecGUI(tk.Tk):
highlightcolor=self.ACCENT)
entry.pack(side="left", fill="x", expand=True, ipady=7, padx=(0, 8))
+ # Enable Drag and Drop
+ entry.drop_target_register(DND_FILES)
+ entry.dnd_bind('<>', lambda e, v=var, t=tag: self._on_drop(e, v, t))
+
btn = tk.Button(row, text="Browse", font=("Segoe UI", 9, "bold"),
bg=self.ACCENT, fg="#ffffff",
activebackground=self.ACCENT_HOVER,
@@ -517,6 +530,37 @@ class KoBackupDecGUI(tk.Tk):
)
self.stop_btn.pack(side="left", padx=(10, 0))
+ self.open_output_btn = tk.Button(
+ btn_row,
+ text="📂 Open Output",
+ font=("Segoe UI", 9),
+ bg=self.SUCCESS,
+ fg="#1a1a2e",
+ activebackground="#10b981",
+ activeforeground="#1a1a2e",
+ relief="flat", bd=0,
+ cursor="hand2",
+ padx=14, pady=8,
+ state="disabled",
+ command=self._open_output_folder
+ )
+ self.open_output_btn.pack(side="right", padx=(10, 0))
+
+ self.export_log_btn = tk.Button(
+ btn_row,
+ text="Export Log",
+ font=("Segoe UI", 9),
+ bg=self.BG_INPUT,
+ fg=self.FG_DIM,
+ activebackground=self.BG_HOVER,
+ activeforeground=self.FG,
+ relief="flat", bd=0,
+ cursor="hand2",
+ padx=14, pady=8,
+ command=self._export_log
+ )
+ self.export_log_btn.pack(side="right", padx=(10, 0))
+
self.clear_log_btn = tk.Button(
btn_row,
text="Clear Log",
@@ -919,12 +963,14 @@ class KoBackupDecGUI(tk.Tk):
self._running = False
self.progress.stop()
self._set_controls_running(False)
+ self.open_output_btn.configure(state="disabled")
if stopped:
self.status_var.set('⏹ Decryption stopped by user')
self._append_log('')
self._append_log('WARNING: ⏹ Decryption was stopped by the user.')
elif success:
+ self.open_output_btn.configure(state="normal")
self.status_var.set('✅ Decryption completed successfully!')
self._append_log('')
self._append_log('=' * 50)
@@ -939,6 +985,87 @@ class KoBackupDecGUI(tk.Tk):
'Decryption failed.\n'
'Check the log output for details.')
+ # -----------------------------------------------------------------
+ # New Utilities
+ # -----------------------------------------------------------------
+
+ def _on_drop(self, event, var, tag):
+ path = event.data
+ if path.startswith('{') and path.endswith('}'):
+ path = path[1:-1]
+ var.set(path)
+ if tag == "backup":
+ self._scan_backup_folders()
+
+ def _export_log(self):
+ log_content = self.log_text.get("1.0", "end-1c")
+ if not log_content.strip():
+ messagebox.showinfo("Export Log", "The log is empty.")
+ return
+ filepath = filedialog.asksaveasfilename(
+ defaultextension=".txt",
+ filetypes=[("Text files", "*.txt"), ("All files", "*.*")],
+ title="Save Log Output"
+ )
+ if filepath:
+ try:
+ with open(filepath, "w", encoding="utf-8") as f:
+ f.write(log_content)
+ messagebox.showinfo("Success", "Log exported successfully.")
+ except Exception as e:
+ messagebox.showerror("Error", f"Failed to save log:\n{e}")
+
+ def _open_output_folder(self):
+ path = self.dest_var.get().strip()
+ if os.path.isdir(path):
+ if sys.platform == "win32":
+ os.startfile(path)
+ elif sys.platform == "darwin":
+ subprocess.Popen(["open", path])
+ else:
+ subprocess.Popen(["xdg-open", path])
+
+ def _get_config_path(self):
+ return os.path.join(os.path.dirname(os.path.abspath(__file__)), "config.json")
+
+ def _load_config(self):
+ config_path = self._get_config_path()
+ if os.path.exists(config_path):
+ try:
+ with open(config_path, "r", encoding="utf-8") as f:
+ data = json.load(f)
+ if "backup_path" in data:
+ self.backup_var.set(data["backup_path"])
+ self._scan_backup_folders()
+ if "dest_path" in data:
+ self.dest_var.set(data["dest_path"])
+ if "expandtar" in data:
+ self.expandtar_var.set(data["expandtar"])
+ if "writable" in data:
+ self.writable_var.set(data["writable"])
+ if "verbose" in data:
+ self.verbose_var.set(data["verbose"])
+ except Exception as e:
+ logging.error(f"Failed to load config: {e}")
+
+ def _save_config(self):
+ config_path = self._get_config_path()
+ data = {
+ "backup_path": self.backup_var.get(),
+ "dest_path": self.dest_var.get(),
+ "expandtar": self.expandtar_var.get(),
+ "writable": self.writable_var.get(),
+ "verbose": self.verbose_var.get()
+ }
+ try:
+ with open(config_path, "w", encoding="utf-8") as f:
+ json.dump(data, f, indent=4)
+ except Exception as e:
+ logging.error(f"Failed to save config: {e}")
+
+ def _on_closing(self):
+ self._save_config()
+ self.destroy()
# ---------------------------------------------------------------------------
# Entry point