
import tkinter as tk from tkinter import ttk, messagebox, filedialog import random import string import os import threading import time class PasswordGeneratorApp: def __init__(self, root): self.root root self.root.title(密码字典生成器 - Ye8位数字标点符号) self.root.geometry(760x680) self.root.resizable(False, False) self.generated_passwords [] self.generating False self.gen_thread None self._last_update 0 # 固定结构配置 self.PREFIX Ye # 固定开头 self.DIGIT_LEN 8 # 数字固定 8 位 self.SYMBOLS !#$%^*()_-[]{};:,.?/|~ self.create_widgets() # UI 构建 def create_widgets(self): title_label tk.Label(self.root, text 固定结构密码字典生成器, font(Arial, 18, bold), fg#1976D2) title_label.pack(pady8) subtitle tk.Label( self.root, text结构Ye 8位数字 1位标点符号 例Ye12345678!, font(Arial, 10), fggray) subtitle.pack(pady2) # 生成配置区 config_frame tk.LabelFrame(self.root, text⚙️ 生成配置, padx10, pady8) config_frame.pack(fillx, padx20, pady8) # 生成数量 row1 tk.Frame(config_frame) row1.pack(fillx, pady4) tk.Label(row1, text生成数量:, width12, anchore).pack(sidetk.LEFT) self.count_var tk.IntVar(value100000) tk.Entry(row1, textvariableself.count_var, width12, font(Consolas, 11)).pack(sidetk.LEFT, padx5) tk.Label(row1, text建议 100,000 或更多, fggray).pack(sidetk.LEFT, padx5) # 固定结构说明 row2 tk.Frame(config_frame) row2.pack(fillx, pady4) tk.Label(row2, text固定结构:, width12, anchore).pack(sidetk.LEFT) tk.Label(row2, textYe [8位数字] [1位标点符号], fg#1976D2, font(Consolas, 10, bold)).pack(sidetk.LEFT, padx5) # 固定前缀 row3 tk.Frame(config_frame) row3.pack(fillx, pady4) tk.Label(row3, text固定开头:, width12, anchore).pack(sidetk.LEFT) self.prefix_var tk.StringVar(valueself.PREFIX) tk.Entry(row3, textvariableself.prefix_var, width12, font(Consolas, 11)).pack(sidetk.LEFT, padx5) tk.Label(row3, text默认 Ye可修改, fggray).pack(sidetk.LEFT, padx5) # 数字位数 row4 tk.Frame(config_frame) row4.pack(fillx, pady4) tk.Label(row4, text数字位数:, width12, anchore).pack(sidetk.LEFT) self.digit_len_var tk.IntVar(valueself.DIGIT_LEN) tk.Spinbox(row4, from_1, to16, textvariableself.digit_len_var, width5).pack(sidetk.LEFT, padx5) tk.Label(row4, text位默认 8 位不足自动补零, fggray).pack(sidetk.LEFT, padx5) # 标点符号 row5 tk.Frame(config_frame) row5.pack(fillx, pady4) tk.Label(row5, text标点符号:, width12, anchore).pack(sidetk.LEFT) self.symbol_var tk.StringVar(valueself.SYMBOLS) tk.Entry(row5, textvariableself.symbol_var, width40, font(Consolas, 10)).pack(sidetk.LEFT, padx5) tk.Label(row5, text可自定义, fggray).pack(sidetk.LEFT) # 生成策略 row6 tk.Frame(config_frame) row6.pack(fillx, pady4) tk.Label(row6, text生成策略:, width12, anchore).pack(sidetk.LEFT) self.strategy_var tk.StringVar(valuemixed) tk.Radiobutton(row6, text混合策略推荐, variableself.strategy_var, valuemixed).pack(sidetk.LEFT, padx5) tk.Radiobutton(row6, text纯随机, variableself.strategy_var, valuerandom).pack(sidetk.LEFT, padx5) tk.Radiobutton(row6, text顺序枚举, variableself.strategy_var, valuesequential).pack(sidetk.LEFT, padx5) # 去重 row7 tk.Frame(config_frame) row7.pack(fillx, pady4) self.dedup_var tk.BooleanVar(valueTrue) tk.Checkbutton(row7, text自动去重保证每条唯一, variableself.dedup_var).pack(sidetk.LEFT, padx5) # 输出文件区 output_frame tk.LabelFrame(self.root, text 输出文件, padx10, pady8) output_frame.pack(fillx, padx20, pady5) tk.Label(output_frame, text保存路径:).pack(sidetk.LEFT, padx3) self.output_path_var tk.StringVar( valueos.path.join(os.getcwd(), password_dict.txt)) tk.Entry(output_frame, textvariableself.output_path_var, width48, font(Consolas, 9)).pack(sidetk.LEFT, padx3) tk.Button(output_frame, text 选择路径, commandself.choose_output_path, bg#2196F3, fgwhite).pack(sidetk.LEFT, padx3) # 操作按钮 button_frame tk.Frame(self.root) button_frame.pack(pady8) self.generate_btn tk.Button(button_frame, text⚡ 开始生成, commandself.start_generate, width15, bg#4CAF50, fgwhite, font(Arial, 11, bold)) self.generate_btn.pack(sidetk.LEFT, padx5) self.stop_btn tk.Button(button_frame, text⏹ 停止, commandself.stop_generate, width15, bg#9E9E9E, fgwhite, font(Arial, 11, bold), statetk.DISABLED) self.stop_btn.pack(sidetk.LEFT, padx5) tk.Button(button_frame, text 预览, commandself.preview, width10, bg#FF9800, fgwhite).pack(sidetk.LEFT, padx5) # 进度区 progress_frame tk.LabelFrame(self.root, text 生成进度, padx10, pady8) progress_frame.pack(fillboth, expandTrue, padx20, pady5) self.progress ttk.Progressbar(progress_frame, modedeterminate) self.progress.pack(fillx, pady5) stats_frame tk.Frame(progress_frame) stats_frame.pack(fillx, pady3) self.progress_var tk.StringVar(value就绪) tk.Label(stats_frame, textvariableself.progress_var, font(Consolas, 10, bold), fg#1976D2).pack(sidetk.LEFT) self.count_var_ui tk.StringVar(value已生成: 0 / 0) tk.Label(stats_frame, textvariableself.count_var_ui, font(Consolas, 10, bold), fg#4CAF50).pack(sidetk.RIGHT) tk.Label(progress_frame, text实时预览最近生成的密码:, anchorw).pack(fillx, pady(5, 0)) preview_container tk.Frame(progress_frame) preview_container.pack(fillboth, expandTrue) self.preview_text tk.Text(preview_container, height8, font(Consolas, 10), bg#1e1e1e, fg#00ff00) self.preview_text.pack(sidetk.LEFT, fillboth, expandTrue) prev_scroll tk.Scrollbar(preview_container, commandself.preview_text.yview) prev_scroll.pack(sidetk.RIGHT, filltk.Y) self.preview_text.config(yscrollcommandprev_scroll.set) # 文件操作 def choose_output_path(self): path filedialog.asksaveasfilename( title保存密码字典, defaultextension.txt, filetypes[(文本文件, *.txt), (所有文件, *.*)], initialfilepassword_dict.txt ) if path: self.output_path_var.set(path) # 参数获取 def _get_symbol_charset(self): symbols self.symbol_var.get().strip() symbols .join(ch for ch in symbols if not ch.isspace()) return symbols def _get_prefix(self): return self.prefix_var.get().strip() # 生成控制 def start_generate(self): if self.generating: messagebox.showwarning(提示, 正在生成中...) return try: count int(self.count_var.get()) digit_len int(self.digit_len_var.get()) except Exception: messagebox.showerror(错误, 请输入有效的数字) return if count 0: messagebox.showerror(错误, 生成数量必须大于 0) return if digit_len 1 or digit_len 16: messagebox.showerror(错误, 数字位数必须在 1~16 之间) return prefix self._get_prefix() if not prefix: messagebox.showerror(错误, 固定开头不能为空) return symbol_charset self._get_symbol_charset() if not symbol_charset: messagebox.showerror(错误, 标点符号不能为空) return output_path self.output_path_var.get().strip() if not output_path: messagebox.showerror(错误, 请指定输出文件路径) return self.generating True self.generated_passwords [] self.generate_btn.config(statetk.DISABLED) self.stop_btn.config(statetk.NORMAL) self.progress[value] 0 self.progress[maximum] count self.progress_var.set(正在生成...) self.count_var_ui.set(f已生成: 0 / {count:,}) self.preview_text.delete(1.0, tk.END) self.gen_thread threading.Thread( targetself.generate_worker, args(count, prefix, digit_len, symbol_charset, output_path), daemonTrue ) self.gen_thread.start() def generate_worker(self, count, prefix, digit_len, symbol_charset, output_path): start_time time.time() strategy self.strategy_var.get() dedup self.dedup_var.get() seen set() passwords [] def add_password(pwd): if dedup: if pwd in seen: return False seen.add(pwd) passwords.append(pwd) return True try: if strategy random: self._gen_random(add_password, count, prefix, digit_len, symbol_charset, seen) elif strategy sequential: self._gen_sequential(add_password, count, prefix, digit_len, symbol_charset, seen) else: # mixed self._gen_mixed(add_password, count, prefix, digit_len, symbol_charset, seen) # 写入文件 self._update_progress_msg(正在写入文件...) with open(output_path, w, encodingutf-8) as f: for pwd in passwords: f.write(pwd \n) elapsed time.time() - start_time self.root.after(0, lambda: self._generate_done( len(passwords), output_path, elapsed)) except Exception as e: self.root.after(0, lambda errstr(e): self._generate_error(err)) # 结构生成核心 def _make_password(self, prefix, digit_len, symbol_charset, numNone): if num is None: num random.randint(0, 10 ** digit_len - 1) digit_part f{num:0{digit_len}d} sym random.choice(symbol_charset) return prefix digit_part sym # ---------- 纯随机 ---------- def _gen_random(self, add_fn, need, prefix, digit_len, symbol_charset, seen): generated 0 attempts 0 while generated need and self.generating: pwd self._make_password(prefix, digit_len, symbol_charset) if add_fn(pwd): generated 1 self._update_progress(len(seen)) attempts 1 if attempts need * 50: self._update_progress_msg( f⚠️ 去重后无法生成更多密码已生成 {len(seen)} 条) break # ---------- 顺序枚举 ---------- def _gen_sequential(self, add_fn, need, prefix, digit_len, symbol_charset, seen): 顺序枚举 - 数字从 0 开始递增按 digit_len 补零 - 标点符号按字符集顺序循环 symbols sorted(symbol_charset) total_numbers 10 ** digit_len generated 0 num 0 while generated need and self.generating: digit_part f{num:0{digit_len}d} sym symbols[num % len(symbols)] pwd prefix digit_part sym if add_fn(pwd): generated 1 self._update_progress(len(seen)) num 1 if num total_numbers: num 0 # 数字空间用尽后回绕 # ---------- 混合策略 ---------- def _gen_mixed(self, add_fn, need, prefix, digit_len, symbol_charset, seen): 混合策略 - 40% 纯随机 - 30% 顺序片段数字连续递增符号循环 - 20% 常见数字模式生日、年份、重复数字、连续数字 - 10% 弱模式变体全 0、全 1、12345678 等 part_random int(need * 0.40) part_seq int(need * 0.30) part_common int(need * 0.20) part_weak need - part_random - part_seq - part_common # 1) 纯随机 self._gen_random(add_fn, part_random, prefix, digit_len, symbol_charset, seen) # 2) 顺序片段 if self.generating and len(seen) need: symbols sorted(symbol_charset) total_numbers 10 ** digit_len generated 0 num random.randint(0, max(0, total_numbers - 1)) while generated part_seq and self.generating: digit_part f{num:0{digit_len}d} sym symbols[num % len(symbols)] pwd prefix digit_part sym if add_fn(pwd): generated 1 self._update_progress(len(seen)) num (num 1) % total_numbers # 3) 常见数字模式 if self.generating and len(seen) need: common_numbers self._get_common_numbers(digit_len) generated 0 attempts 0 while generated part_common and self.generating: num random.choice(common_numbers) sym random.choice(symbol_charset) pwd prefix f{num:0{digit_len}d} sym if add_fn(pwd): generated 1 self._update_progress(len(seen)) attempts 1 if attempts part_common * 50: break # 4) 弱模式变体 if self.generating and len(seen) need: generated 0 attempts 0 while generated part_weak and self.generating: num random.choice([0, 1, 11111111, 22222222, 12345678, 87654321, 88888888, 99999999, 66666666, 52013140]) num num % (10 ** digit_len) sym random.choice(symbol_charset) pwd prefix f{num:0{digit_len}d} sym if add_fn(pwd): generated 1 self._update_progress(len(seen)) attempts 1 if attempts part_weak * 50: break # 5) 不足补齐 remaining need - len(seen) if remaining 0 and self.generating: self._gen_random(add_fn, remaining, prefix, digit_len, symbol_charset, seen) def _get_common_numbers(self, digit_len): 生成常见数字模式 nums [] # 年份 for y in range(1950, 2026): nums.append(y) # 生日格式 for y in range(1950, 2026): for m in range(1, 13): for d in [1, 2, 3, 5, 8, 10, 12, 15, 18, 20, 22, 25, 28]: nums.append(int(f{y}{m:02d}{d:02d}) % (10 ** digit_len)) nums.append(int(f{m:02d}{d:02d}{y}) % (10 ** digit_len)) # 重复数字 for d in string.digits: nums.append(int(d * min(digit_len, 8)) % (10 ** digit_len)) # 连续数字 nums.append(int(12345678[:digit_len]) % (10 ** digit_len)) nums.append(int(87654321[:digit_len]) % (10 ** digit_len)) # 常见弱密码数字 nums.extend([0, 1, 12, 123, 1234, 12345, 123456, 1234567, 12345678, 520, 5201314, 1314520, 666, 888, 999]) # 去重 过滤 nums list({n % (10 ** digit_len) for n in nums}) return nums # 进度更新 def _update_progress(self, current): now time.time() if now - self._last_update 0.1: return self._last_update now try: count int(self.count_var.get()) except Exception: count current self.root.after(0, lambda ccurrent, tcount: self._refresh_progress(c, t)) def _refresh_progress(self, current, total): self.progress[value] min(current, total) self.count_var_ui.set(f已生成: {current:,} / {total:,}) pct (current / total * 100) if total else 0 self.progress_var.set(f进度: {pct:.1f}%) self.preview_text.delete(1.0, tk.END) for pwd in self.generated_passwords[-30:]: self.preview_text.insert(tk.END, pwd \n) self.preview_text.see(tk.END) def _update_progress_msg(self, msg): self.root.after(0, lambda mmsg: self.progress_var.set(m)) # 完成/错误 def _generate_done(self, total, output_path, elapsed): self.generating False self.generate_btn.config(statetk.NORMAL) self.stop_btn.config(statetk.DISABLED) self.progress[value] self.progress[maximum] self.progress_var.set(f✅ 生成完成耗时 {elapsed:.2f} 秒) self.count_var_ui.set(f已生成: {total:,} 条) self.preview_text.delete(1.0, tk.END) for pwd in self.generated_passwords[-30:]: self.preview_text.insert(tk.END, pwd \n) try: size os.path.getsize(output_path) size_str self._format_size(size) except Exception: size_str 未知 messagebox.showinfo( 生成完成, f✅ 密码字典生成成功\n\n f生成数量: {total:,} 条\n f保存路径: {output_path}\n f文件大小: {size_str}\n f耗时: {elapsed:.2f} 秒\n\n f结构Ye 8位数字 1位标点符号 ) def _generate_error(self, error_msg): self.generating False self.generate_btn.config(statetk.NORMAL) self.stop_btn.config(statetk.DISABLED) self.progress_var.set(❌ 生成失败) messagebox.showerror(错误, f生成失败:\n{error_msg}) staticmethod def _format_size(size): for unit in [B, KB, MB, GB]: if size 1024: return f{size:.2f} {unit} size / 1024 return f{size:.2f} TB # 预览 def preview(self): path self.output_path_var.get().strip() if not os.path.exists(path): messagebox.showwarning(提示, f文件不存在: {path}) return try: with open(path, r, encodingutf-8, errorsignore) as f: lines [f.readline().strip() for _ in range(50)] lines [l for l in lines if l] self.preview_text.delete(1.0, tk.END) self.preview_text.insert(tk.END, f 前 {len(lines)} 条密码 \n) for pwd in lines: self.preview_text.insert(tk.END, pwd \n) with open(path, r, encodingutf-8, errorsignore) as f: total sum(1 for _ in f) self.progress_var.set(f文件: {path} 共 {total:,} 条) except Exception as e: messagebox.showerror(错误, f读取失败: {e}) def stop_generate(self): if not self.generating: return self.generating False self.progress_var.set(⏹ 正在停止...) self.generate_btn.config(statetk.NORMAL) self.stop_btn.config(statetk.DISABLED) if __name__ __main__: root tk.Tk() app PasswordGeneratorApp(root) root.mainloop()