feat: Add GUI v1.1 features (drag-and-drop, persistence, export logs, open output)

This commit is contained in:
najeeb 2026-05-09 01:51:13 +05:00
parent 04512877e9
commit 08c3d373e3
3 changed files with 154 additions and 7 deletions

View File

@ -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

View File

@ -2,12 +2,12 @@
<h1 align="center">🔐 KoBackup Decryptor</h1>
<p align="center">
<strong>Decrypt Huawei HiSuite &amp; KoBackup encrypted backups</strong><br>
CLI + Modern GUI &bull; Selective Folder Decryption &bull; Password Verification
CLI + Modern GUI &bull; Selective Folder Decryption &bull; Drag & Drop &bull; Password Verification
</p>
<p align="center">
<a href="#-installation"><img src="https://img.shields.io/badge/python-3.7%2B-blue?logo=python&logoColor=white" alt="Python 3.7+"></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="MIT License"></a>
<a href="#-changelog"><img src="https://img.shields.io/badge/version-20200705--GUI-purple" alt="Version"></a>
<a href="#-changelog"><img src="https://img.shields.io/badge/version-GUI_v1.1-purple" alt="Version"></a>
</p>
</p>
@ -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.

View File

@ -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("<Configure>", 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('<<Drop>>', 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