commit 9bc37aeae804327978d31edb0ef2e46e0dd03a9c Author: Ivann LARUELLE Date: Mon Jan 29 01:39:20 2024 +0100 first commit diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..50be741 --- /dev/null +++ b/Pipfile @@ -0,0 +1,12 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +pyperclip = "*" + +[dev-packages] + +[requires] +python_version = "3.12" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..9ce1a56 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,28 @@ +{ + "_meta": { + "hash": { + "sha256": "8ceead135ee6991bd49b68802a159c7b7d7cfe2f2218d7a71d7a5d55b1d4cd6e" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.12" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "pyperclip": { + "hashes": [ + "sha256:105254a8b04934f0bc84e9c24eb360a591aaf6535c9def5f29d92af107a9bf57" + ], + "index": "pypi", + "version": "==1.8.2" + } + }, + "develop": {} +} diff --git a/main.py b/main.py new file mode 100644 index 0000000..b4af3de --- /dev/null +++ b/main.py @@ -0,0 +1,146 @@ +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()