Skip to content

Commit b18b77d

Browse files
authored
Create FileCryptor.py
1 parent b814952 commit b18b77d

1 file changed

Lines changed: 225 additions & 0 deletions

File tree

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
import threading
2+
import tkinter as tk
3+
from tkinter import filedialog, messagebox, scrolledtext
4+
import ttkbootstrap as tb
5+
from cryptography.fernet import Fernet
6+
import os
7+
import sys
8+
import base64
9+
import hashlib
10+
11+
def resource_path(file_name):
12+
base_path = getattr(sys, '_MEIPASS', os.path.dirname(os.path.abspath(__file__)))
13+
return os.path.join(base_path, file_name)
14+
15+
def derive_key(password: str) -> bytes:
16+
"""Derive a Fernet key from a password using SHA256"""
17+
return base64.urlsafe_b64encode(hashlib.sha256(password.encode()).digest())
18+
19+
class FileEncryptor:
20+
def __init__(self, root):
21+
self.root = root
22+
self.root.title("FileCryptor - File Encryption Tool")
23+
self.root.geometry("1100x660")
24+
# self.root.iconbitmap(resource_path("logo.ico"))
25+
26+
# ===== Buttons Frame =====
27+
button_frame = tb.Frame(self.root)
28+
button_frame.pack(pady=10)
29+
30+
self.encrypt_btn = tb.Button(
31+
button_frame, text="🔒 Encrypt Files", bootstyle="success-outline", width=25,
32+
command=lambda: self.start_process(encrypt=True)
33+
)
34+
self.encrypt_btn.grid(row=0, column=0, padx=5)
35+
36+
self.decrypt_btn = tb.Button(
37+
button_frame, text="🔓 Decrypt Files", bootstyle="info-outline", width=25,
38+
command=lambda: self.start_process(encrypt=False)
39+
)
40+
self.decrypt_btn.grid(row=0, column=1, padx=5)
41+
42+
self.cancel_event = threading.Event()
43+
44+
self.cancel_btn = tb.Button(
45+
button_frame, text="❌ Cancel", bootstyle="danger-outline", width=20,
46+
state=tk.DISABLED, command=self.cancel_process
47+
)
48+
self.cancel_btn.grid(row=0, column=2, padx=5)
49+
50+
self.clear_btn = tb.Button(
51+
button_frame, text="🧹 Clear Log", bootstyle="warning-outline", width=20,
52+
command=self.clear_log
53+
)
54+
self.clear_btn.grid(row=0, column=3, padx=5)
55+
56+
self.about_btn = tb.Button(
57+
button_frame, text="ℹ️ About", bootstyle="secondary-outline", width=20,
58+
command=self.show_about_guide
59+
)
60+
self.about_btn.grid(row=0, column=4, padx=5)
61+
62+
# ===== Log Area =====
63+
self.log_area = scrolledtext.ScrolledText(self.root, wrap=tk.WORD, font=("Arial", 12))
64+
self.log_area.pack(expand=True, fill=tk.BOTH, padx=10, pady=10)
65+
66+
# ===== Progress Bar Frame =====
67+
progress_frame = tb.Frame(self.root)
68+
progress_frame.pack(fill=tk.X, padx=10, pady=(0, 10))
69+
70+
self.progress_label = tb.Label(progress_frame, text="Ready")
71+
self.progress_label.pack(side=tk.LEFT, padx=(0, 10))
72+
73+
self.progress = tb.Progressbar(progress_frame, bootstyle="success-striped", mode="determinate")
74+
self.progress.pack(side=tk.RIGHT, fill=tk.X, expand=True)
75+
76+
def cancel_process(self):
77+
self.cancel_event.set()
78+
self.progress_label.config(text="Cancelling...")
79+
80+
def _reset_ui(self):
81+
self.progress["value"] = 0
82+
self.progress_label.config(text="Ready")
83+
self.encrypt_btn.config(state=tk.NORMAL)
84+
self.decrypt_btn.config(state=tk.NORMAL)
85+
self.cancel_btn.config(state=tk.DISABLED)
86+
self.cancel_event.clear()
87+
88+
def _update_progress(self, value):
89+
self.progress["value"] = value
90+
self.progress_label.config(text=f"Processing... {int(value)}%")
91+
92+
def _log(self, message):
93+
self.log_area.insert(tk.END, message + "\n")
94+
self.log_area.see(tk.END)
95+
96+
def clear_log(self):
97+
self.log_area.delete(1.0, tk.END)
98+
99+
# ===== Worker =====
100+
def _process_worker(self, file_paths, password, encrypt=True):
101+
try:
102+
key = derive_key(password)
103+
fernet = Fernet(key)
104+
total_files = len(file_paths)
105+
step = 100 / total_files
106+
current_progress = 0
107+
108+
for file_path in file_paths:
109+
if self.cancel_event.is_set():
110+
self.root.after(0, self._reset_ui)
111+
self._log("Process cancelled.")
112+
return
113+
114+
filename = os.path.basename(file_path)
115+
try:
116+
with open(file_path, "rb") as f:
117+
data = f.read()
118+
119+
if encrypt:
120+
processed_data = fernet.encrypt(data)
121+
out_file = file_path + ".enc"
122+
else:
123+
processed_data = fernet.decrypt(data)
124+
if file_path.endswith(".enc"):
125+
out_file = file_path[:-4]
126+
else:
127+
out_file = file_path + ".dec"
128+
129+
with open(out_file, "wb") as f:
130+
f.write(processed_data)
131+
132+
self._log(f"{'Encrypted' if encrypt else 'Decrypted'}: {filename}")
133+
134+
except Exception as e:
135+
self._log(f"Error processing {filename}: {str(e)}")
136+
137+
current_progress += step
138+
self.root.after(0, self._update_progress, current_progress)
139+
140+
self.root.after(0, self._reset_ui)
141+
self._log("Process completed.")
142+
143+
except Exception as e:
144+
self.root.after(0, self._reset_ui)
145+
self._log(f"Fatal error: {str(e)}")
146+
147+
# ===== Start Process =====
148+
def start_process(self, encrypt=True):
149+
action = "Encrypt" if encrypt else "Decrypt"
150+
file_paths = filedialog.askopenfilenames(title=f"Select files to {action}")
151+
if not file_paths:
152+
return
153+
154+
password = tk.simpledialog.askstring("Password", f"Enter password to {action} files:", show="*")
155+
if not password:
156+
messagebox.showwarning("Password required", "Operation cancelled. Password is required!")
157+
return
158+
159+
self.cancel_event.clear()
160+
self.encrypt_btn.config(state=tk.DISABLED)
161+
self.decrypt_btn.config(state=tk.DISABLED)
162+
self.cancel_btn.config(state=tk.NORMAL)
163+
self.progress_label.config(text="Preparing...")
164+
self.progress["value"] = 0
165+
166+
threading.Thread(target=self._process_worker, args=(file_paths, password, encrypt), daemon=True).start()
167+
168+
# ===== About / Guide =====
169+
def show_about_guide(self):
170+
guide_window = tb.Toplevel(self.root)
171+
guide_window.title("📘 About / Guide")
172+
guide_window.geometry("600x480")
173+
guide_window.resizable(False, False)
174+
guide_window.grab_set()
175+
guide_window.attributes("-toolwindow", True)
176+
177+
self.root.update_idletasks()
178+
root_x = self.root.winfo_x()
179+
root_y = self.root.winfo_y()
180+
root_width = self.root.winfo_width()
181+
root_height = self.root.winfo_height()
182+
win_width = 600
183+
win_height = 480
184+
pos_x = root_x + (root_width // 2) - (win_width // 2)
185+
pos_y = root_y + (root_height // 2) - (win_height // 2)
186+
guide_window.geometry(f"{win_width}x{win_height}+{pos_x}+{pos_y}")
187+
188+
frame = tb.Frame(guide_window, padding=10)
189+
frame.pack(fill="both", expand=True)
190+
191+
sections = {
192+
"About FileCryptor": (
193+
"FileCryptor is a secure and easy-to-use desktop tool for encrypting and decrypting files.\n"
194+
"It uses password-based encryption (Fernet symmetric encryption) to protect your data."
195+
),
196+
"Key Features": (
197+
"- Encrypt and decrypt multiple files\n"
198+
"- Password protected\n"
199+
"- Cancel operations anytime\n"
200+
"- Real-time progress bar\n"
201+
"- Modern, clean interface"
202+
),
203+
"Built With": (
204+
"- Python\n"
205+
"- Tkinter & ttkbootstrap\n"
206+
"- cryptography library\n"
207+
"- Multithreading for responsive UI"
208+
),
209+
"Developer": (
210+
"Developed by MateTools\n"
211+
"https://matetools.gumroad.com"
212+
)
213+
}
214+
215+
for title, text in sections.items():
216+
tb.Label(frame, text=title, font=("Segoe UI", 12, "bold")).pack(anchor="w", pady=(10, 0))
217+
tb.Label(frame, text=text, font=("Segoe UI", 10), wraplength=600, justify="left").pack(anchor="w", pady=(2, 5))
218+
219+
tb.Button(frame, text="Close", bootstyle="danger-outline", width=15,
220+
command=guide_window.destroy).pack(pady=10)
221+
222+
if __name__ == "__main__":
223+
app = tb.Window(themename="cosmo")
224+
FileEncryptor(app)
225+
app.mainloop()

0 commit comments

Comments
 (0)