147 lines
6.9 KiB
Python
147 lines
6.9 KiB
Python
|
import tkinter as tk
|
||
|
from tkinter import ttk
|
||
|
import random
|
||
|
import json
|
||
|
import pyperclip
|
||
|
from tkinter import messagebox
|
||
|
from tkinter import filedialog
|
||
|
|
||
|
# Constants for password characters
|
||
|
UPPERCASE_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||
|
LOWERCASE_CHARS = 'abcdefghijklmnopqrstuvwxyz'
|
||
|
DIGITS = '0123456789'
|
||
|
SPECIAL_CHARS = '!@#$%^&*()-_=+[]{}|;:",.<>?/~'
|
||
|
AMBIGUOUS_CHARS = 'lI1oO0'
|
||
|
|
||
|
class PasswordGeneratorApp:
|
||
|
def __init__(self, root):
|
||
|
self.root = root
|
||
|
self.root.title("Password Generator")
|
||
|
|
||
|
# Variables for password generation
|
||
|
self.include_uppercase = tk.BooleanVar(value=True)
|
||
|
self.include_lowercase = tk.BooleanVar(value=True)
|
||
|
self.include_digits = tk.BooleanVar(value=True)
|
||
|
self.include_special = tk.BooleanVar(value=True)
|
||
|
self.exclude_ambiguous = tk.BooleanVar(value=False)
|
||
|
self.include_every_category = tk.BooleanVar(value=False)
|
||
|
self.password_length = tk.IntVar(value=12)
|
||
|
|
||
|
# Configure the main grid
|
||
|
self.root.columnconfigure(0, weight=1)
|
||
|
|
||
|
# Create and place widgets
|
||
|
self.create_widgets()
|
||
|
|
||
|
def create_widgets(self):
|
||
|
# Password entry
|
||
|
self.password_entry = ttk.Entry(self.root, font=('Arial', 14), width=40)
|
||
|
self.password_entry.grid(row=0, column=0, padx=10, pady=10, sticky="ew")
|
||
|
|
||
|
# Password settings frame
|
||
|
settings_frame = ttk.LabelFrame(self.root, text="Settings", padding=10)
|
||
|
settings_frame.grid(row=1, column=0, padx=10, pady=10, sticky="ew")
|
||
|
|
||
|
# Character type checkboxes
|
||
|
ttk.Checkbutton(settings_frame, text="A-Z", variable=self.include_uppercase, command=self.update_password).grid(row=0, column=0, sticky="w")
|
||
|
ttk.Checkbutton(settings_frame, text="a-z", variable=self.include_lowercase, command=self.update_password).grid(row=0, column=1, sticky="w")
|
||
|
ttk.Checkbutton(settings_frame, text="0-9", variable=self.include_digits, command=self.update_password).grid(row=0, column=2, sticky="w")
|
||
|
ttk.Checkbutton(settings_frame, text="Special", variable=self.include_special, command=self.update_password).grid(row=0, column=3, sticky="w")
|
||
|
ttk.Checkbutton(settings_frame, text="Exclude look-alike (lI1oO0)", variable=self.exclude_ambiguous, command=self.update_password).grid(row=1, column=0, columnspan=2, sticky="w")
|
||
|
ttk.Checkbutton(settings_frame, text="Include every category", variable=self.include_every_category, command=self.update_password).grid(row=1, column=2, columnspan=2, sticky="w")
|
||
|
|
||
|
# Password length slider
|
||
|
ttk.Label(settings_frame, text="Password Length:").grid(row=2, column=0, columnspan=2, sticky="w")
|
||
|
self.length_slider = ttk.Scale(settings_frame, from_=4, to_=32, orient='horizontal', variable=self.password_length, command=lambda event: self.update_password())
|
||
|
self.length_slider.grid(row=2, column=2, columnspan=2, sticky="ew")
|
||
|
|
||
|
# Buttons
|
||
|
button_frame = ttk.Frame(self.root)
|
||
|
button_frame.grid(row=2, column=0, padx=10, pady=10, sticky="ew")
|
||
|
button_frame.columnconfigure((0, 1, 2), weight=1)
|
||
|
|
||
|
ttk.Button(button_frame, text="Copy", command=self.copy_to_clipboard).grid(row=0, column=0, padx=5, pady=5, sticky="ew")
|
||
|
ttk.Button(button_frame, text="Save Settings", command=self.save_settings).grid(row=0, column=1, padx=5, pady=5, sticky="ew")
|
||
|
ttk.Button(button_frame, text="Load Settings", command=self.load_settings).grid(row=0, column=2, padx=5, pady=5, sticky="ew")
|
||
|
|
||
|
# Initial password generation
|
||
|
self.update_password()
|
||
|
|
||
|
def generate_password(self):
|
||
|
# Collect selected character types
|
||
|
chars = ''
|
||
|
if self.include_uppercase.get():
|
||
|
chars += UPPERCASE_CHARS
|
||
|
if self.include_lowercase.get():
|
||
|
chars += LOWERCASE_CHARS
|
||
|
if self.include_digits.get():
|
||
|
chars += DIGITS
|
||
|
if self.include_special.get():
|
||
|
chars += SPECIAL_CHARS
|
||
|
if self.exclude_ambiguous.get():
|
||
|
chars = ''.join(filter(lambda x: x not in AMBIGUOUS_CHARS, chars))
|
||
|
|
||
|
# Ensure the password includes at least one character from each selected category if required
|
||
|
password = ''
|
||
|
if self.include_every_category.get():
|
||
|
if self.include_uppercase.get():
|
||
|
password += random.choice(UPPERCASE_CHARS)
|
||
|
if self.include_lowercase.get():
|
||
|
password += random.choice(LOWERCASE_CHARS)
|
||
|
if self.include_digits.get():
|
||
|
password += random.choice(DIGITS)
|
||
|
if self.include_special.get():
|
||
|
password += random.choice(SPECIAL_CHARS)
|
||
|
random_chars = [random.choice(chars) for _ in range(self.password_length.get() - len(password))]
|
||
|
password += ''.join(random_chars)
|
||
|
else:
|
||
|
password = ''.join(random.choice(chars) for _ in range(self.password_length.get()))
|
||
|
|
||
|
return ''.join(random.sample(password, len(password)))
|
||
|
|
||
|
def update_password(self):
|
||
|
new_password = self.generate_password()
|
||
|
self.password_entry.delete(0, tk.END)
|
||
|
self.password_entry.insert(0, new_password)
|
||
|
|
||
|
def copy_to_clipboard(self):
|
||
|
password = self.password_entry.get()
|
||
|
pyperclip.copy(password)
|
||
|
messagebox.showinfo("Password Generator", "Password copied to clipboard!")
|
||
|
|
||
|
def save_settings(self):
|
||
|
settings = {
|
||
|
'include_uppercase': self.include_uppercase.get(),
|
||
|
'include_lowercase': self.include_lowercase.get(),
|
||
|
'include_digits': self.include_digits.get(),
|
||
|
'include_special': self.include_special.get(),
|
||
|
'exclude_ambiguous': self.exclude_ambiguous.get(),
|
||
|
'include_every_category': self.include_every_category.get(),
|
||
|
'password_length': self.password_length.get()
|
||
|
}
|
||
|
filename = filedialog.asksaveasfilename(defaultextension='.json', filetypes=[("JSON files", "*.json")])
|
||
|
if filename:
|
||
|
with open(filename, 'w') as f:
|
||
|
json.dump(settings, f)
|
||
|
messagebox.showinfo("Password Generator", "Settings saved successfully!")
|
||
|
|
||
|
def load_settings(self):
|
||
|
filename = filedialog.askopenfilename(filetypes=[("JSON files", "*.json")])
|
||
|
if filename:
|
||
|
with open(filename, 'r') as f:
|
||
|
settings = json.load(f)
|
||
|
self.include_uppercase.set(settings['include_uppercase'])
|
||
|
self.include_lowercase.set(settings['include_lowercase'])
|
||
|
self.include_digits.set(settings['include_digits'])
|
||
|
self.include_special.set(settings['include_special'])
|
||
|
self.exclude_ambiguous.set(settings['exclude_ambiguous'])
|
||
|
self.include_every_category.set(settings['include_every_category'])
|
||
|
self.password_length.set(settings['password_length'])
|
||
|
self.update_password()
|
||
|
messagebox.showinfo("Password Generator", "Settings loaded successfully!")
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
root = tk.Tk()
|
||
|
app = PasswordGeneratorApp(root)
|
||
|
root.mainloop()
|