基于Python 3 Tkinter开发的一款多名单加密抽人软件——自动抽人v3.0版 base加密v1.0版
下载链接:蓝奏云 免登录 不限速 密码:e4vc
使用提示:
1.请解压使用 双击运行exe
2.目录中nameinfo文件夹里面存放的是名单加密文件,删除后运行软件自动生成。
3.目录中_internal文件夹里面存放的是加密运行库,删除后程序无法正常运行。
4.加密名单文件一旦被篡改,自动重置。
##公示源代码:
import tkinter as tk
from tkinter import ttk, messagebox
import random
import os
import time
import base64
import shutil
##Ky1n编写 Written By Ky1n
##BASE64加密部分由Deepseek-R1辅助编写 The BASE64 Auxiliary Preparation By DeepSeek-R1 Large Model
##注释由AI生成 AI-Generated Code Comments
##变量名由AI规范 AI-Standardized Variable Names
##Windows可执行程序由py-to-exe打包 Windows EXE Via py-to-exe
class RandomPickerApp:
def __init__(self, root):
self.root = root
self.root.title("抽人v3 拖此处可移动")
# 设置主界面大小为300x200
self.root.geometry("300x200")
self.root.minsize(300, 200)
# 初始化名单
self.lists = {}
self.current_list = None
self.topmost = False
self.hidden = False
# 确保nameinfo目录存在
if not os.path.exists("nameinfo"):
os.makedirs("nameinfo")
# 创建主界面
self.create_main_interface()
# 加载名单
if not self.load_lists():
# 如果加载失败,显示错误窗口
self.show_decryption_error_dialog()
# 置顶按钮状态
self.update_top_button()
# 使窗口可拖动
self.root.bind("<ButtonPress-1>", self.start_move)
self.root.bind("<B1-Motion>", self.do_move)
def create_main_interface(self):
# 主框架
main_frame = ttk.Frame(self.root)
main_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
# 结果显示 - 使用更大的空间
result_frame = ttk.Frame(main_frame)
result_frame.pack(fill=tk.BOTH, expand=True)
self.result_label = ttk.Label(
result_frame,
text="请选择名单",
font=("Arial", 32, "bold"),
foreground="blue",
anchor=tk.CENTER,
justify=tk.CENTER
)
self.result_label.pack(fill=tk.BOTH, expand=True)
# 底部控制区域
control_frame = ttk.Frame(main_frame)
control_frame.pack(fill=tk.X, pady=(5, 0))
# 名单选择放在底部
list_frame = ttk.Frame(control_frame)
list_frame.pack(fill=tk.X, pady=2)
self.list_var = tk.StringVar()
self.list_combobox = ttk.Combobox(list_frame, textvariable=self.list_var, state="readonly", width=15)
self.list_combobox.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=2)
self.list_combobox.bind("<<ComboboxSelected>>", self.on_list_selected)
# 按钮区域
button_frame = ttk.Frame(control_frame)
button_frame.pack(fill=tk.X, pady=2)
# 使用网格布局按钮
self.pick_button = ttk.Button(
button_frame,
text="抽取",
command=self.pick_random,
width=6
)
self.pick_button.grid(row=0, column=0, padx=2)
self.top_button = ttk.Button(
button_frame,
text="置顶",
command=self.toggle_topmost,
width=6
)
self.top_button.grid(row=0, column=1, padx=2)
edit_button = ttk.Button(
button_frame,
text="编辑",
command=self.open_edit_window,
width=6
)
edit_button.grid(row=0, column=2, padx=2)
# 隐藏按钮
hide_button = ttk.Button(
button_frame,
text="隐藏",
command=self.hide_window,
width=6
)
hide_button.grid(row=0, column=3, padx=2)
# 配置按钮框架的列权重
button_frame.columnconfigure(0, weight=1)
button_frame.columnconfigure(1, weight=1)
button_frame.columnconfigure(2, weight=1)
button_frame.columnconfigure(3, weight=1)
def start_move(self, event):
# 允许在窗口任意位置拖动
self.x = event.x
self.y = event.y
def do_move(self, event):
deltax = event.x - self.x
deltay = event.y - self.y
x = self.root.winfo_x() + deltax
y = self.root.winfo_y() + deltay
self.root.geometry(f"+{x}+{y}")
@staticmethod
def encrypt_data(data):
"""加密数据"""
# 使用简单的异或加密
key = "Ky1nSecretKey" # 加密密钥
encrypted = bytearray()
for i, char in enumerate(data.encode('utf-8')):
encrypted.append(char ^ ord(key[i % len(key)]))
return base64.b64encode(encrypted).decode('utf-8')
@staticmethod
def decrypt_data(data):
"""解密数据"""
try:
encrypted = base64.b64decode(data.encode('utf-8'))
key = "Ky1nSecretKey" # 加密密钥
decrypted = bytearray()
for i, char in enumerate(encrypted):
decrypted.append(char ^ ord(key[i % len(key)]))
return decrypted.decode('utf-8')
except:
return None
def load_lists(self):
"""加载名单文件,返回是否成功"""
self.lists = {}
list_names = []
decryption_failed = False
for i in range(1, 9):
filename = f"nameinfo/name.info{i}"
if os.path.exists(filename):
try:
with open(filename, "r", encoding="utf-8") as f:
encrypted_content = f.read()
# 解密内容
decrypted_content = RandomPickerApp.decrypt_data(encrypted_content)
if decrypted_content is None:
print(f"解密名单 {filename} 失败")
decryption_failed = True
continue
lines = decrypted_content.splitlines()
if len(lines) >= 2:
list_name = lines[0].strip()
try:
count = int(lines[1].strip())
except ValueError:
count = 0
names = [line.strip() for line in lines[2:2+count] if line.strip()]
self.lists[filename] = {
"name": list_name,
"count": count,
"names": names
}
list_names.append(list_name)
except Exception as e:
print(f"加载名单 {filename} 出错: {e}")
decryption_failed = True
# 更新下拉菜单
self.list_combobox["values"] = list_names
if list_names:
self.list_var.set(list_names[0])
self.on_list_selected()
# 如果有解密失败的文件,返回False
return not decryption_failed
def show_decryption_error_dialog(self):
"""显示解密错误对话框"""
# 创建错误对话框
error_dialog = tk.Toplevel(self.root)
error_dialog.title("数据错误")
error_dialog.geometry("300x150")
error_dialog.resizable(False, False)
error_dialog.transient(self.root)
error_dialog.grab_set()
# 居中显示
error_dialog.update_idletasks()
x = (error_dialog.winfo_screenwidth() - error_dialog.winfo_width()) // 2
y = (error_dialog.winfo_screenheight() - error_dialog.winfo_height()) // 2
error_dialog.geometry(f"+{x}+{y}")
# 错误信息
error_label = ttk.Label(
error_dialog,
text="数据文件已损坏或已被篡改!\n请选择操作:",
font=("Arial", 10),
justify=tk.CENTER
)
error_label.pack(pady=20)
# 按钮框架
button_frame = ttk.Frame(error_dialog)
button_frame.pack(pady=10)
# 退出按钮
exit_button = ttk.Button(
button_frame,
text="退出程序",
command=lambda: self.exit_program(error_dialog),
width=12
)
exit_button.pack(side=tk.LEFT, padx=10)
# 重置按钮
reset_button = ttk.Button(
button_frame,
text="重置名单",
command=lambda: self.reset_lists(error_dialog),
width=12
)
reset_button.pack(side=tk.LEFT, padx=10)
def exit_program(self, dialog):
"""退出程序"""
if dialog:
dialog.destroy()
self.root.quit()
self.root.destroy()
def reset_lists(self, dialog):
"""重置名单文件"""
try:
# 备份原文件(如果存在)
if os.path.exists("nameinfo"):
backup_dir = "nameinfo_backup_" + time.strftime("%Y%m%d_%H%M%S")
shutil.copytree("nameinfo", backup_dir)
# 删除原目录
if os.path.exists("nameinfo"):
shutil.rmtree("nameinfo")
# 重新创建目录和示例文件
os.makedirs("nameinfo")
self.create_sample_files()
# 重新加载名单
if self.load_lists():
if dialog:
dialog.destroy()
messagebox.showinfo("成功", "名单已重置为默认值。")
else:
messagebox.showerror("错误", "重置名单失败,请重试。")
except Exception as e:
messagebox.showerror("错误", f"重置名单时出错: {str(e)}")
def create_sample_files(self):
"""创建示例名单文件"""
for i in range(1, 9):
filename = f"nameinfo/name.info{i}"
# 构建文件内容
content = f"名单{i}\n5\n"
for j in range(1, 6):
content += f"人员{i}-{j}\n"
# 使用静态方法加密
encrypted_content = RandomPickerApp.encrypt_data(content)
with open(filename, "w", encoding="utf-8") as f:
f.write(encrypted_content)
def on_list_selected(self, event=None):
selected_name = self.list_var.get()
for filename, data in self.lists.items():
if data["name"] == selected_name:
self.current_list = filename
break
else:
self.current_list = None
def pick_random(self):
if not self.current_list:
return
list_data = self.lists.get(self.current_list)
if not list_data:
return
if list_data["count"] <= 0:
self.result_label.config(text="Error", foreground="red")
return
if not list_data["names"]:
self.result_label.config(text="名单为空", foreground="orange")
return
# 禁用抽取按钮
self.pick_button.config(state=tk.DISABLED)
# 动画效果:快速抽取20次
original_names = list_data["names"]
for i in range(20):
# 随机选择一个人
selected = random.choice(original_names)
self.result_label.config(text=selected, foreground="purple")
self.root.update()
time.sleep(0.04) # 间隔0.04秒
# 最终选择一个人
selected = random.choice(original_names)
self.result_label.config(text=selected, foreground="green")
# 启用抽取按钮
self.pick_button.config(state=tk.NORMAL)
def toggle_topmost(self):
self.topmost = not self.topmost
self.root.attributes("-topmost", self.topmost)
self.update_top_button()
def update_top_button(self):
text = "取消置顶" if self.topmost else "置顶"
self.top_button.config(text=text)
def open_edit_window(self):
EditWindow(self)
def hide_window(self):
# 获取窗口位置
x = self.root.winfo_x()
y = self.root.winfo_y()
screen_width = self.root.winfo_screenwidth()
# 判断窗口在屏幕左侧还是右侧
if x < screen_width / 2:
# 左侧
position = (0, y)
else:
# 右侧
position = (screen_width - 50, y) # 增加宽度确保显示完整
# 隐藏主窗口
self.root.withdraw()
self.hidden = True
# 创建美观的抽人按钮
self.create_beautiful_button(position)
def create_beautiful_button(self, position):
self.compact_button_window = tk.Toplevel()
self.compact_button_window.overrideredirect(True) # 无边框
self.compact_button_window.attributes("-topmost", True)
# 设置位置和大小(增加高度确保文字完整显示)
self.compact_button_window.geometry(f"50x60+{position[0]}+{position[1]}")
# 设置背景色
bg_color = "#f0f0f0" # 浅灰色背景
# 创建主框架 - 用于实现圆角效果
main_frame = tk.Frame(
self.compact_button_window,
bg=bg_color,
highlightbackground="#c0c0c0", # 边框颜色
highlightthickness=1 # 边框厚度
)
main_frame.pack(fill=tk.BOTH, expand=True, padx=1, pady=1)
# 创建Canvas用于绘制圆角
canvas = tk.Canvas(
main_frame,
width=48, # 比框架小2像素
height=58, # 比框架小2像素
bg=bg_color,
highlightthickness=0 # 无边框
)
canvas.pack(fill=tk.BOTH, expand=True)
# 绘制圆角矩形
canvas.create_rounded_rectangle(
0, 0, 48, 58,
radius=10, # 圆角半径
fill=bg_color,
outline="#c0c0c0"
)
# 创建紧凑布局:"抽"在上,"人"在下
top_label = tk.Label(
canvas,
text="抽",
font=("Arial", 14, "bold"),
anchor=tk.CENTER,
bg=bg_color,
fg="#333333" # 深灰色文字
)
# 放置标签 - 确保位置居中
top_label.place(relx=0.5, rely=0.3, anchor=tk.CENTER) # 上移位置
bottom_label = tk.Label(
canvas,
text="人",
font=("Arial", 14, "bold"),
anchor=tk.CENTER,
bg=bg_color,
fg="#333333" # 深灰色文字
)
# 放置标签 - 确保位置居中
bottom_label.place(relx=0.5, rely=0.7, anchor=tk.CENTER) # 下移位置
# 为整个窗口绑定点击事件
self.compact_button_window.bind("<Button-1>", lambda e: self.show_main_window())
# 添加悬停效果
def on_enter(e):
canvas.config(bg="#e0e0e0")
top_label.config(bg="#e0e0e0")
bottom_label.config(bg="#e0e0e0")
# 重绘圆角矩形
canvas.delete("all")
canvas.create_rounded_rectangle(
0, 0, 48, 58,
radius=10,
fill="#e0e0e0",
outline="#c0c0c0"
)
# 重新放置标签
top_label.place(relx=0.5, rely=0.3, anchor=tk.CENTER)
bottom_label.place(relx=0.5, rely=0.7, anchor=tk.CENTER)
def on_leave(e):
canvas.config(bg=bg_color)
top_label.config(bg=bg_color)
bottom_label.config(bg=bg_color)
# 重绘圆角矩形
canvas.delete("all")
canvas.create_rounded_rectangle(
0, 0, 48, 58,
radius=10,
fill=bg_color,
outline="#c0c0c0"
)
# 重新放置标签
top_label.place(relx=0.5, rely=0.3, anchor=tk.CENTER)
bottom_label.place(relx=0.5, rely=0.7, anchor=tk.CENTER)
self.compact_button_window.bind("<Enter>", on_enter)
self.compact_button_window.bind("<Leave>", on_leave)
def show_main_window(self):
# 销毁紧凑按钮窗口
self.compact_button_window.destroy()
# 显示主窗口
self.root.deiconify()
self.hidden = False
def save_list(self, filename, list_name, names):
count = len(names)
# 构建文件内容
content = f"{list_name}\n{count}\n"
content += "\n".join(names)
# 加密内容
encrypted_content = RandomPickerApp.encrypt_data(content)
with open(filename, "w", encoding="utf-8") as f:
f.write(encrypted_content)
# 重新加载名单
self.load_lists()
# 更新当前选择
if self.current_list == filename:
self.list_var.set(list_name)
# 为Canvas添加绘制圆角矩形的方法
def create_rounded_rectangle(self, x1, y1, x2, y2, radius=25, **kwargs):
points = [
x1 + radius, y1,
x1 + radius, y1,
x2 - radius, y1,
x2 - radius, y1,
x2, y1,
x2, y1 + radius,
x2, y1 + radius,
x2, y2 - radius,
x2, y2 - radius,
x2, y2,
x2 - radius, y2,
x2 - radius, y2,
x1 + radius, y2,
x1 + radius, y2,
x1, y2,
x1, y2 - radius,
x1, y2 - radius,
x1, y1 + radius,
x1, y1 + radius,
x1, y1,
x1 + radius, y1
]
return self.create_polygon(points, **kwargs, smooth=True)
# 将方法添加到Canvas类
tk.Canvas.create_rounded_rectangle = create_rounded_rectangle
class EditWindow:
def __init__(self, app):
self.app = app
self.window = tk.Toplevel(app.root)
self.window.title("编辑名单")
# 设置编辑界面大小为100x300
self.window.geometry("100x300")
self.window.minsize(100, 300)
self.window.transient(app.root)
self.window.grab_set()
# 创建笔记本式标签页
self.notebook = ttk.Notebook(self.window)
self.notebook.pack(fill=tk.BOTH, expand=True, padx=2, pady=2)
# 为每个名单创建标签页
self.tabs = {}
self.list_vars = {}
self.text_widgets = {}
for i in range(1, 9):
filename = f"nameinfo/name.info{i}"
tab = ttk.Frame(self.notebook)
self.notebook.add(tab, text=str(i)) # 只显示数字标签节省空间
self.tabs[filename] = tab
# 名单名称编辑
name_frame = ttk.Frame(tab)
name_frame.pack(fill=tk.X, padx=2, pady=2)
list_var = tk.StringVar()
self.list_vars[filename] = list_var
name_entry = ttk.Entry(name_frame, textvariable=list_var, width=8)
name_entry.pack(fill=tk.X, padx=2)
# 添加提示标签
tip_label = ttk.Label(
tab,
text="一行一个名字",
font=("Arial", 6), # 使用非常小的字体
foreground="gray",
anchor=tk.CENTER
)
tip_label.pack(fill=tk.X, padx=2, pady=(0, 2))
# 名单内容编辑
text_frame = ttk.Frame(tab)
text_frame.pack(fill=tk.BOTH, expand=True, padx=2, pady=2)
scrollbar = ttk.Scrollbar(text_frame)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
text_widget = tk.Text(
text_frame,
wrap=tk.WORD,
yscrollcommand=scrollbar.set,
font=("Arial", 8), # 使用更小的字体
height=8
)
text_widget.pack(fill=tk.BOTH, expand=True)
scrollbar.config(command=text_widget.yview)
self.text_widgets[filename] = text_widget
# 按钮区域
button_frame = ttk.Frame(tab)
button_frame.pack(fill=tk.X, padx=2, pady=2)
save_button = ttk.Button(
button_frame,
text="保存",
command=lambda f=filename: self.save_list(f),
width=5
)
save_button.pack(side=tk.LEFT, padx=1)
clear_button = ttk.Button(
button_frame,
text="清空",
command=lambda f=filename: self.clear_list(f),
width=5
)
clear_button.pack(side=tk.LEFT, padx=1)
# 加载数据到界面
self.load_data()
# 设置关闭窗口事件
self.window.protocol("WM_DELETE_WINDOW", self.on_close)
def load_data(self):
for filename, tab in self.tabs.items():
if filename in self.app.lists:
data = self.app.lists[filename]
self.list_vars[filename].set(data["name"])
text_widget = self.text_widgets[filename]
text_widget.delete(1.0, tk.END)
text_widget.insert(tk.END, "\n".join(data["names"]))
else:
self.list_vars[filename].set(f"名单{filename[-1]}")
self.text_widgets[filename].delete(1.0, tk.END)
def save_list(self, filename):
list_name = self.list_vars[filename].get()
if not list_name:
return
text_content = self.text_widgets[filename].get(1.0, tk.END)
names = [name.strip() for name in text_content.splitlines() if name.strip()]
self.app.save_list(filename, list_name, names)
def clear_list(self, filename):
if messagebox.askyesno("确认", "确定要清空此名单吗?"):
self.text_widgets[filename].delete(1.0, tk.END)
def on_close(self):
self.window.grab_release()
self.window.destroy()
if __name__ == "__main__":
# 确保nameinfo目录存在
if not os.path.exists("nameinfo"):
os.makedirs("nameinfo")
# 创建示例名单文件(如果不存在)
for i in range(1, 9):
filename = f"nameinfo/name.info{i}"
if not os.path.exists(filename):
# 构建文件内容
content = f"名单{i}\n5\n"
for j in range(1, 6):
content += f"人员{i}-{j}\n"
# 使用静态方法加密
encrypted_content = RandomPickerApp.encrypt_data(content)
with open(filename, "w", encoding="utf-8") as f:
f.write(encrypted_content)
root = tk.Tk()
app = RandomPickerApp(root)
root.mainloop()

