Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import tkinter as tk
- from tkinter import ttk, filedialog, messagebox
- import os
- import sys
- import threading
- import shutil
- import hashlib
- import json
- from Crypto.Cipher import AES
- from Crypto.Protocol.KDF import PBKDF2
- from Crypto.Random import get_random_bytes
- from cryptography.hazmat.primitives import serialization
- from cryptography.hazmat.backends import default_backend
- # Configuration
- SALT_SIZE = 32
- KEY_SIZE = 32 # AES-256
- IV_SIZE = 16
- ITERATIONS = 100000
- CHUNK_SIZE = 64 * 1024 # 64KB to reduce memory usage during large file processing
- class SecureProtectedDisc:
- def derive_key(self, password: str, salt: bytes) -> bytes:
- return PBKDF2(password.encode(), salt, dkLen=KEY_SIZE, count=ITERATIONS)
- def load_key_file(self, key_path: str) -> bytes:
- """Load key from PEM file or raw key file."""
- try:
- # Try to load as PEM private key
- with open(key_path, "rb") as f:
- private_key = serialization.load_pem_private_key(
- f.read(),
- password=None,
- backend=default_backend()
- )
- private_bytes = private_key.private_bytes(
- encoding=serialization.Encoding.DER,
- format=serialization.PrivateFormat.PKCS8,
- encryption_algorithm=serialization.NoEncryption()
- )
- return hashlib.sha256(private_bytes).digest()[:KEY_SIZE]
- except:
- # Try as raw key file
- with open(key_path, "rb") as f:
- key = f.read()
- if len(key) not in (16, 24, 32):
- raise ValueError("Invalid key size. Must be 16, 24, or 32 bytes")
- return key
- def encrypt_with_key(self, input_path: str, output_path: str, key: bytes) -> None:
- """
- Encrypt a file with a given raw key using chunk-based encryption (AES-CBC).
- """
- iv = get_random_bytes(IV_SIZE)
- cipher = AES.new(key, AES.MODE_CBC, iv)
- with open(input_path, 'rb') as f_in, open(output_path, 'wb') as f_out:
- # Write IV first
- f_out.write(iv)
- while True:
- chunk = f_in.read(CHUNK_SIZE)
- if len(chunk) == 0:
- # No more data: final padded block
- padded_block = self._pad(b'')
- f_out.write(cipher.encrypt(padded_block))
- break
- elif len(chunk) < CHUNK_SIZE:
- # Last (incomplete) chunk: pad then encrypt
- chunk = self._pad(chunk)
- f_out.write(cipher.encrypt(chunk))
- break
- else:
- # Full chunk
- f_out.write(cipher.encrypt(chunk))
- def decrypt_with_key(self, input_path: str, output_path: str, key: bytes) -> None:
- """
- Decrypt a file with a given raw key using chunk-based decryption (AES-CBC).
- """
- with open(input_path, 'rb') as f_in:
- # Read IV first
- iv = f_in.read(IV_SIZE)
- cipher = AES.new(key, AES.MODE_CBC, iv)
- with open(output_path, 'wb') as f_out:
- next_chunk = b''
- while True:
- chunk = f_in.read(CHUNK_SIZE)
- if len(chunk) == 0:
- # Final decrypt, unpad
- final_block = cipher.decrypt(next_chunk)
- final_block = self._unpad(final_block)
- f_out.write(final_block)
- break
- decrypted = cipher.decrypt(next_chunk)
- f_out.write(decrypted)
- next_chunk = chunk
- def encrypt_with_password(self, input_path: str, output_path: str, password: str) -> tuple:
- """
- Encrypt using password-based key derivation, chunk-based approach.
- """
- salt = get_random_bytes(SALT_SIZE)
- key = self.derive_key(password, salt)
- iv = get_random_bytes(IV_SIZE)
- cipher = AES.new(key, AES.MODE_CBC, iv)
- with open(input_path, 'rb') as f_in, open(output_path, 'wb') as f_out:
- # Write salt + iv
- f_out.write(salt + iv)
- while True:
- chunk = f_in.read(CHUNK_SIZE)
- if len(chunk) == 0:
- # Final padded block
- padded_block = self._pad(b'')
- f_out.write(cipher.encrypt(padded_block))
- break
- elif len(chunk) < CHUNK_SIZE:
- # Last chunk
- chunk = self._pad(chunk)
- f_out.write(cipher.encrypt(chunk))
- break
- else:
- # Full chunk
- f_out.write(cipher.encrypt(chunk))
- return salt, iv
- def decrypt_with_password(self, input_path: str, output_path: str, password: str) -> bool:
- """
- Decrypt using password-based key derivation, chunk-based approach.
- """
- with open(input_path, 'rb') as f_in:
- # Read salt + iv
- header = f_in.read(SALT_SIZE + IV_SIZE)
- salt = header[:SALT_SIZE]
- iv = header[SALT_SIZE:]
- key = self.derive_key(password, salt)
- cipher = AES.new(key, AES.MODE_CBC, iv)
- with open(output_path, 'wb') as f_out:
- next_chunk = b''
- while True:
- chunk = f_in.read(CHUNK_SIZE)
- if len(chunk) == 0:
- final_block = cipher.decrypt(next_chunk)
- final_block = self._unpad(final_block)
- f_out.write(final_block)
- break
- decrypted = cipher.decrypt(next_chunk)
- f_out.write(decrypted)
- next_chunk = chunk
- return True
- def _pad(self, data: bytes) -> bytes:
- """
- Standard PKCS#7 padding to make data a multiple of AES.block_size
- """
- block_size = AES.block_size
- padding_length = block_size - (len(data) % block_size)
- return data + bytes([padding_length] * padding_length)
- @staticmethod
- def _unpad(data: bytes) -> bytes:
- """
- Remove PKCS#7 padding.
- """
- padding_length = data[-1]
- return data[:-padding_length]
- def create_checksum(self, file_path: str) -> str:
- """
- Create SHA-256 checksum in a chunk-based manner.
- """
- sha256 = hashlib.sha256()
- with open(file_path, 'rb') as f:
- while True:
- chunk = f.read(4096)
- if not chunk:
- break
- sha256.update(chunk)
- return sha256.hexdigest()
- class SecureProtectedDiscGUI(tk.Tk):
- def __init__(self):
- super().__init__()
- self.title("Secure Protected Disc Creator")
- self.geometry("900x600")
- self.configure(padx=20, pady=20)
- self.spd = SecureProtectedDisc()
- self.key_type = tk.StringVar(value="password")
- self.mode = tk.StringVar(value="encrypt")
- self.file_paths = []
- self.output_dir = ""
- self.password = ""
- self.confirm_password = ""
- self.key_file = ""
- self.create_widgets()
- def create_widgets(self):
- # --- Mode Selection ---
- ttk.Label(self, text="Mode:").grid(row=0, column=0, sticky="w")
- encrypt_radio = ttk.Radiobutton(self, text="Encrypt", variable=self.mode, value="encrypt", command=self.update_ui)
- decrypt_radio = ttk.Radiobutton(self, text="Decrypt", variable=self.mode, value="decrypt", command=self.update_ui)
- encrypt_radio.grid(row=0, column=1, sticky="w")
- decrypt_radio.grid(row=0, column=2, sticky="w")
- # --- Key Type Selection ---
- ttk.Label(self, text="Encryption Type:").grid(row=1, column=0, sticky="w")
- ttk.Radiobutton(self, text="Password", variable=self.key_type, value="password", command=self.update_key_ui).grid(row=1, column=1, sticky="w")
- ttk.Radiobutton(self, text="Key File", variable=self.key_type, value="keyfile", command=self.update_key_ui).grid(row=1, column=2, sticky="w")
- # --- Password Fields ---
- self.pw_label = ttk.Label(self, text="Password:")
- self.pw_label.grid(row=2, column=0, sticky="w", pady=(10,0))
- self.pw_entry = ttk.Entry(self, show="*", width=80)
- self.pw_entry.grid(row=3, column=0, columnspan=3, padx=5)
- self.confirm_pw_label = ttk.Label(self, text="Confirm Password:")
- self.confirm_pw_label.grid(row=4, column=0, sticky="w", pady=(10,0))
- self.confirm_pw_entry = ttk.Entry(self, show="*", width=80)
- self.confirm_pw_entry.grid(row=5, column=0, columnspan=3, padx=5)
- # --- Key File Fields ---
- self.key_file_frame = ttk.Frame(self)
- self.key_file_frame.grid(row=6, column=0, columnspan=4, sticky="ew")
- self.key_file_button = ttk.Button(self.key_file_frame, text="Select Key File", command=self.select_key_file)
- self.key_file_button.pack(side=tk.LEFT)
- self.key_file_label = ttk.Label(self.key_file_frame, text="No key file selected")
- self.key_file_label.pack(side=tk.LEFT, padx=5)
- # --- File Selection ---
- ttk.Label(self, text="Select Files:").grid(row=7, column=0, sticky="w", pady=(10,0))
- self.file_list = tk.Listbox(self, width=80, height=5, selectmode=tk.EXTENDED)
- self.file_list.grid(row=8, column=0, columnspan=4, padx=5, pady=5)
- btn_frame = ttk.Frame(self)
- btn_frame.grid(row=9, column=0, columnspan=4, pady=5)
- self.add_button = ttk.Button(btn_frame, text="Add Files", command=self.add_files)
- self.add_button.pack(side=tk.LEFT)
- self.remove_button = ttk.Button(btn_frame, text="Remove Selected", command=self.remove_files)
- self.remove_button.pack(side=tk.LEFT, padx=10)
- # 1) Button to remove all entries from the file list
- self.remove_all_button = ttk.Button(btn_frame, text="Clear All", command=self.clear_all_files)
- self.remove_all_button.pack(side=tk.LEFT, padx=10)
- # --- Output Directory ---
- ttk.Label(self, text="Output Directory:").grid(row=10, column=0, sticky="w", pady=(10,0))
- self.output_dir_entry = tk.Entry(self, width=80)
- self.output_dir_entry.grid(row=11, column=0, columnspan=3, padx=5)
- self.browse_button = ttk.Button(self, text="Browse", command=self.browse_output_dir)
- self.browse_button.grid(row=11, column=3, padx=5)
- # --- Progress & Status ---
- self.progress = ttk.Progressbar(self, orient=tk.HORIZONTAL, length=760, mode='determinate')
- self.progress.grid(row=12, column=0, columnspan=4, pady=20)
- self.status = ttk.Label(self, text="Ready")
- self.status.grid(row=13, column=0, columnspan=4)
- # --- Action Buttons ---
- btn_frame2 = ttk.Frame(self)
- btn_frame2.grid(row=14, column=0, columnspan=4, pady=20)
- self.start_button = ttk.Button(btn_frame2, text="Start", command=self.start_process)
- self.start_button.pack(side=tk.LEFT)
- self.exit_button = ttk.Button(btn_frame2, text="Exit", command=self.destroy)
- self.exit_button.pack(side=tk.LEFT, padx=10)
- self.update_key_ui()
- def update_ui(self):
- """
- Called whenever the Mode RadioButton changes (Encrypt or Decrypt).
- We also add the logic to automatically add all .DAT files in the same
- directory as this script if 'Decrypt' is selected.
- """
- is_encrypt = self.mode.get() == "encrypt"
- if is_encrypt:
- self.add_button.config(text="Add Files")
- else:
- self.add_button.config(text="Select Meta File/Encrypted Files")
- # ----------------- AUTO ADD ALL *.DAT FILES -----------------
- # Clear the list box first to avoid duplicates
- self.file_list.delete(0, tk.END)
- # Get the directory of this script
- script_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
- # Find all *.DAT files in the script directory and add them
- for f in os.listdir(script_dir):
- if f.lower().endswith(".dat"):
- full_path = os.path.join(script_dir, f)
- self.file_list.insert(tk.END, full_path)
- # -----------------------------------------------------------
- def update_key_ui(self):
- if self.key_type.get() == "password":
- self.pw_label.config(state="normal")
- self.pw_entry.config(state="normal")
- self.confirm_pw_label.config(state="normal")
- self.confirm_pw_entry.config(state="normal")
- self.key_file_frame.grid_remove()
- else:
- self.pw_label.config(state="disabled")
- self.pw_entry.config(state="disabled")
- self.confirm_pw_label.config(state="disabled")
- self.confirm_pw_entry.config(state="disabled")
- self.key_file_frame.grid()
- def select_key_file(self):
- key_path = filedialog.askopenfilename(
- title="Select Key File",
- filetypes=[("PEM Files", "*.pem"), ("All Files", "*.*")]
- )
- if key_path:
- self.key_file = key_path
- self.key_file_label.config(text=os.path.basename(key_path))
- def add_files(self):
- if self.mode.get() == "encrypt":
- files = filedialog.askopenfilenames()
- if files:
- for f in files:
- if f not in self.file_list.get(0, tk.END):
- self.file_list.insert(tk.END, f)
- else:
- # Allow selection of either ENCRYPTED_META.DAT or encrypted files
- files = filedialog.askopenfilenames(
- title="Select ENCRYPTED_META.DAT and/or Encrypted Files",
- filetypes=[("Meta File", "*.DAT"), ("Encrypted Files", "*.DAT"), ("All Files", "*.*")]
- )
- if files:
- for f in files:
- if f not in self.file_list.get(0, tk.END):
- self.file_list.insert(tk.END, f)
- def remove_files(self):
- selected = self.file_list.curselection()
- for index in reversed(selected):
- self.file_list.delete(index)
- def clear_all_files(self):
- """Remove all items from the file list."""
- self.file_list.delete(0, tk.END)
- def browse_output_dir(self):
- directory = filedialog.askdirectory()
- self.output_dir_entry.delete(0, tk.END)
- self.output_dir_entry.insert(0, directory)
- def start_process(self):
- """Disable the Start button, then start encryption/decryption in a thread."""
- if not self.validate_inputs():
- return
- # Disable the start button until done
- self.start_button.config(state="disabled")
- self.output_dir = self.output_dir_entry.get()
- items = self.file_list.get(0, tk.END)
- try:
- if self.key_type.get() == "password":
- if self.pw_entry.get() != self.confirm_pw_entry.get():
- messagebox.showerror("Error", "Passwords do not match!")
- self.start_button.config(state="normal")
- return
- self.password = self.pw_entry.get()
- threading.Thread(target=self.process_password_based, args=(items,)).start()
- else:
- if not self.key_file:
- messagebox.showerror("Error", "Please select a key file!")
- self.start_button.config(state="normal")
- return
- threading.Thread(target=self.process_keyfile_based, args=(items,)).start()
- except Exception as e:
- messagebox.showerror("Error", f"Initialization failed: {str(e)}")
- # Re-enable start button on failure
- self.start_button.config(state="normal")
- def validate_inputs(self):
- if self.file_list.size() == 0:
- messagebox.showerror("Error", "Please select at least one file/directory!")
- return False
- if self.key_type.get() == "password":
- if len(self.pw_entry.get()) < 8 and self.mode.get() == "encrypt":
- # Only enforce password length check in encryption mode
- messagebox.showerror("Error", "Password must be at least 8 characters!")
- return False
- if not self.output_dir_entry.get():
- messagebox.showerror("Error", "Please select output directory!")
- return False
- return True
- def update_progress(self, value):
- self.progress['value'] = value
- self.update_idletasks()
- #
- # --------------- Encryption Process (Password-based) ---------------
- #
- def process_password_based(self, items):
- try:
- os.makedirs(self.output_dir, exist_ok=True)
- total_items = len(items)
- if self.mode.get() == "encrypt":
- meta_data = {"files": []}
- for idx, file_path in enumerate(items, 1):
- self.status.config(text=f"Encrypting file {idx}/{total_items}")
- self.update_progress((idx-1)*80/total_items)
- # Compute original file checksum
- original_checksum = self.spd.create_checksum(file_path)
- # Encrypt file
- enc_name = f"ENCRYPTED{idx}.DAT"
- enc_path = os.path.join(self.output_dir, enc_name)
- self.spd.encrypt_with_password(file_path, enc_path, self.password)
- # Add info to metadata
- meta_data["files"].append({
- "original_name": os.path.basename(file_path),
- "encrypted_name": enc_name,
- "size": os.path.getsize(file_path),
- "original_checksum": original_checksum
- })
- # Encrypt metadata
- self.status.config(text="Creating metadata...")
- temp_meta_path = os.path.join(self.output_dir, "temp_metadata.json")
- with open(temp_meta_path, 'w') as f:
- json.dump(meta_data, f)
- enc_meta_path = os.path.join(self.output_dir, "ENCRYPTED_META.DAT")
- self.spd.encrypt_with_password(temp_meta_path, enc_meta_path, self.password)
- os.remove(temp_meta_path)
- # Create checksum for the encrypted metadata
- checksum = self.spd.create_checksum(enc_meta_path)
- with open(os.path.join(self.output_dir, "CHECKSUM.TXT"), "w") as f:
- f.write(checksum)
- # Copy this script to output
- script_copy = os.path.join(self.output_dir, "SECPROTDISC_GUI.PY")
- shutil.copy(sys.argv[0], script_copy)
- self.update_progress(100)
- self.status.config(text=f"Files ready in: {self.output_dir}")
- messagebox.showinfo("Success", "Disc files prepared successfully!\nBurn all files to a disc.")
- else:
- self.decrypt_with_metadata(items, self.password)
- except Exception as e:
- messagebox.showerror("Error", f"Process failed: {str(e)}")
- finally:
- # Re-enable start button whether success or fail
- self.start_button.config(state="normal")
- self.progress['value'] = 0
- self.status.config(text="Ready")
- #
- # --------------- Encryption Process (Key File-based) ---------------
- #
- def process_keyfile_based(self, items):
- try:
- key = self.spd.load_key_file(self.key_file)
- os.makedirs(self.output_dir, exist_ok=True)
- if self.mode.get() == "encrypt":
- meta_data = {"files": []}
- for idx, file_path in enumerate(items, 1):
- output_path = os.path.join(self.output_dir, f"ENCRYPTED{idx}.DAT")
- # Compute original file checksum
- original_checksum = self.spd.create_checksum(file_path)
- self.spd.encrypt_with_key(file_path, output_path, key)
- meta_data["files"].append({
- "original_name": os.path.basename(file_path),
- "encrypted_name": f"ENCRYPTED{idx}.DAT",
- "size": os.path.getsize(file_path),
- "original_checksum": original_checksum
- })
- # Encrypt metadata
- temp_meta_path = os.path.join(self.output_dir, "temp_metadata.json")
- with open(temp_meta_path, 'w') as f:
- json.dump(meta_data, f)
- enc_meta_path = os.path.join(self.output_dir, "ENCRYPTED_META.DAT")
- self.spd.encrypt_with_key(temp_meta_path, enc_meta_path, key)
- os.remove(temp_meta_path)
- # Create checksum
- checksum = self.spd.create_checksum(enc_meta_path)
- with open(os.path.join(self.output_dir, "CHECKSUM.TXT"), "w") as f:
- f.write(checksum)
- # Copy the script
- script_copy = os.path.join(self.output_dir, "SECPROTDISC_GUI.PY")
- shutil.copy(sys.argv[0], script_copy)
- messagebox.showinfo("Success", "Files encrypted successfully!")
- else:
- self.decrypt_with_metadata_key(items, key)
- except Exception as e:
- messagebox.showerror("Error", f"Key-based process failed: {str(e)}")
- finally:
- # Re-enable start button
- self.start_button.config(state="normal")
- self.progress['value'] = 0
- self.status.config(text="Ready")
- #
- # --------------- Decryption (Password-based) ---------------
- #
- def decrypt_with_metadata(self, items, password):
- try:
- self.status.config(text="Decrypting with metadata...")
- meta_file_path = next((item for item in items if os.path.basename(item) == "ENCRYPTED_META.DAT"), None)
- if not meta_file_path:
- raise FileNotFoundError("ENCRYPTED_META.DAT not found in selected files.")
- # 1) Decrypt the metadata to a temporary file
- temp_meta_path = os.path.join(self.output_dir, "temp_metadata.json")
- self.spd.decrypt_with_password(meta_file_path, temp_meta_path, password)
- # 2) Load the metadata
- with open(temp_meta_path, 'r') as f:
- meta_data = json.load(f)
- os.remove(temp_meta_path)
- # 3) Let user choose which file(s) to decrypt
- selected_files = self.choose_files_to_decrypt(meta_data["files"])
- if not selected_files:
- # If user cancelled or selected nothing, just return
- return
- total_files = len(selected_files)
- file_paths = {os.path.basename(item): item for item in items}
- for idx, file_info in enumerate(selected_files, 1):
- self.status.config(text=f"Decrypting file {idx}/{total_files}")
- self.update_progress(idx * 90 / total_files)
- enc_name = file_info["encrypted_name"]
- if enc_name not in file_paths:
- messagebox.showerror(
- "Error",
- f"Encrypted file {enc_name} not selected.\nPlease select all encrypted files and ENCRYPTED_META.DAT."
- )
- return
- enc_path = file_paths[enc_name]
- output_path = os.path.join(self.output_dir, file_info["original_name"])
- # Decrypt
- self.spd.decrypt_with_password(enc_path, output_path, password)
- # Checksum verify
- decrypted_checksum = self.spd.create_checksum(output_path)
- if decrypted_checksum != file_info["original_checksum"]:
- messagebox.showerror(
- "Checksum Mismatch",
- f"Decrypted file {file_info['original_name']} does not match the original checksum!"
- )
- return
- self.update_progress(100)
- messagebox.showinfo("Success", f"Files decrypted to:\n{self.output_dir}")
- except FileNotFoundError as e:
- messagebox.showerror("Error", str(e))
- except Exception as e:
- messagebox.showerror("Error", f"Decryption failed: {str(e)}")
- finally:
- self.update_progress(0)
- self.status.config(text="")
- #
- # --------------- Decryption (Key-based) ---------------
- #
- def decrypt_with_metadata_key(self, items, key):
- try:
- self.status.config(text="Decrypting with metadata...")
- meta_file_path = next((item for item in items if os.path.basename(item) == "ENCRYPTED_META.DAT"), None)
- if not meta_file_path:
- raise FileNotFoundError("ENCRYPTED_META.DAT not found in selected files.")
- # 1) Decrypt the metadata
- temp_meta_path = os.path.join(self.output_dir, "temp_metadata.json")
- self.spd.decrypt_with_key(meta_file_path, temp_meta_path, key)
- # 2) Load
- with open(temp_meta_path, 'r') as f:
- meta_data = json.load(f)
- os.remove(temp_meta_path)
- # 3) Let user choose which file(s) to decrypt
- selected_files = self.choose_files_to_decrypt(meta_data["files"])
- if not selected_files:
- return
- total_files = len(selected_files)
- file_paths = {os.path.basename(item): item for item in items}
- for idx, file_info in enumerate(selected_files, 1):
- self.status.config(text=f"Decrypting file {idx}/{total_files}")
- self.update_progress(idx * 90 / total_files)
- enc_name = file_info["encrypted_name"]
- if enc_name not in file_paths:
- messagebox.showerror(
- "Error",
- f"Encrypted file {enc_name} not selected.\nPlease select all encrypted files and ENCRYPTED_META.DAT."
- )
- return
- enc_path = file_paths[enc_name]
- output_path = os.path.join(self.output_dir, file_info["original_name"])
- # Decrypt
- self.spd.decrypt_with_key(enc_path, output_path, key)
- # Checksum verify
- decrypted_checksum = self.spd.create_checksum(output_path)
- if decrypted_checksum != file_info["original_checksum"]:
- messagebox.showerror(
- "Checksum Mismatch",
- f"Decrypted file {file_info['original_name']} does not match the original checksum!"
- )
- return
- self.update_progress(100)
- messagebox.showinfo("Success", f"Files decrypted to:\n{self.output_dir}")
- except FileNotFoundError as e:
- messagebox.showerror("Error", str(e))
- except Exception as e:
- messagebox.showerror("Error", f"Decryption failed: {str(e)}")
- finally:
- self.update_progress(0)
- self.status.config(text="")
- #
- # --------------- Popup for Selecting Which Files to Decrypt ---------------
- #
- def choose_files_to_decrypt(self, files_meta):
- """
- Presents a popup GUI to let the user choose which files from the metadata
- are to be decrypted. Returns a list of the selected file metadata objects.
- """
- window = tk.Toplevel(self)
- window.title("Select Files to Decrypt")
- window.geometry("400x300")
- window.grab_set() # Makes this window modal
- lbl = ttk.Label(window, text="Choose which files you want to decrypt:")
- lbl.pack(pady=5)
- # Scrollable list of files with multiple selection
- listbox = tk.Listbox(window, height=10, selectmode=tk.MULTIPLE)
- listbox.pack(expand=True, fill=tk.BOTH, padx=10, pady=5)
- # Populate with original_name from each metadata entry
- for i, f_meta in enumerate(files_meta):
- listbox.insert(tk.END, f_meta["original_name"])
- chosen_files = []
- def on_ok():
- """Callback to finalize user selection."""
- selected_indices = listbox.curselection()
- for idx in selected_indices:
- chosen_files.append(files_meta[idx])
- window.destroy()
- def on_cancel():
- # No files selected
- window.destroy()
- # Buttons
- btn_frame = ttk.Frame(window)
- btn_frame.pack(pady=5)
- ok_btn = ttk.Button(btn_frame, text="OK", command=on_ok)
- ok_btn.pack(side=tk.LEFT, padx=5)
- cancel_btn = ttk.Button(btn_frame, text="Cancel", command=on_cancel)
- cancel_btn.pack(side=tk.LEFT, padx=5)
- # Wait for user to close the dialog
- self.wait_window(window)
- return chosen_files
- if __name__ == "__main__":
- app = SecureProtectedDiscGUI()
- app.mainloop()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement