Python实现单位活动抽奖实用工具
这个使用Python实现的的单位活动抽奖工具,包含以下功能/特点:
支持CSV文件/txt文件参与者名单导入
多级奖项设置(如金奖、银奖一等奖、二等奖等)
状态栏实时提示结果
实时显示抽奖情况
防重复抽选
结果导出功能(见文件菜单)
支持删除误添加的奖项(抽奖开始后禁用删除功能)
运行界面
这个程序全部用到Python中标准库/模块,其中:
- import tkinter as tk:导入Tkinter库,这是一个用于创建图形用户界面(GUI)的标准Python库。通常用来开发桌面应用程序。别名为tk只是为了简化代码中的引用。
- from tkinter import ttk, filedialog, messagebox:从Tkinter库中进一步导入特定的模块。ttk提供了一些更现代的控件;filedialog用于打开文件或目录选择对话框;messagebox用于显示消息框。
- import csv:导入csv模块,允许程序读写CSV(逗号分隔值)文件格式的数据。
- import random:导入random模块,用于生成随机数,可以进行随机选取、打乱顺序等操作。
- import threading:导入threading模块,用于创建和管理线程,实现多线程编程,使程序能够同时执行多个任务。
- import time:导入time模块,提供了与时间相关的各种函数,如暂停程序(sleep)、获取当前时间等。
源码如下:
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import csv
import random
import threading
import time
import os
class LotteryApp:
def __init__(self, root):
self.root = root
self.root.title("单位活动抽奖系统 v1.0.2")
self.root.geometry("1200x700")
# 初始化变量
self.participants = []
self.awards = []
self.results = {}
self.current_award = None
self.is_rolling = False
# 创建界面
self.create_widgets()
self.create_menu()
def create_menu(self):
menubar = tk.Menu(self.root)
self.root.config(menu=menubar)
file_menu = tk.Menu(menubar, tearoff=0)
file_menu.add_command(label="导入名单", command=self.import_participants)
file_menu.add_command(label="导出抽奖结果", command=self.export_results)
menubar.add_cascade(label="文件", menu=file_menu)
def create_widgets(self):
# 设置背景颜色
self.root.configure(bg="#f0f0f0")
# 左侧面板
left_frame = ttk.Frame(self.root)
left_frame.pack(side=tk.LEFT, padx=10, pady=10, fill=tk.BOTH)
# 参与者列表
ttk.Label(left_frame, text="参与者名单").pack()
self.participant_list = tk.Listbox(left_frame, width=30, height=15)
self.participant_list.pack(fill=tk.BOTH, expand=True)
# 导入按钮
ttk.Button(left_frame, text="导入名单", command=self.import_participants).pack(fill=tk.X)
# 中间面板
center_frame = ttk.Frame(self.root)
center_frame.pack(side=tk.LEFT, padx=10, pady=10, fill=tk.BOTH)
# 奖项设置
ttk.Label(center_frame, text="奖项设置").grid(row=0, column=0, sticky=tk.W)
self.award_name = ttk.Entry(center_frame, width=15)
self.award_name.grid(row=1, column=0, padx=5)
self.award_count = ttk.Spinbox(center_frame, from_=1, to=100, width=5)
self.award_count.grid(row=1, column=1, padx=5)
ttk.Button(center_frame, text="添加奖项", command=self.add_award).grid(row=1, column=2)
# 删除奖项按钮
self.delete_award_button = ttk.Button(center_frame, text="删除奖项", command=self.delete_award, state=tk.DISABLED)
self.delete_award_button.grid(row=1, column=3, padx=5)
# 奖项列表
self.award_tree = ttk.Treeview(center_frame, columns=("name", "count"), show="headings", height=10)
self.award_tree.heading("name", text="奖项名称")
self.award_tree.heading("count", text="名额")
self.award_tree.grid(row=2, column=0, columnspan=4, pady=5)
# 抽奖控制
self.roll_label = tk.Label(center_frame, text="", font=("Arial", 24), fg="red", bg="#f0f0f0")
self.roll_label.grid(row=3, column=0, columnspan=4)
ttk.Button(center_frame, text="开始抽奖", command=self.start_lottery).grid(row=4, column=0, pady=10)
ttk.Button(center_frame, text="停止抽奖", command=self.stop_lottery).grid(row=4, column=1, pady=10)
# 状态栏(新增在抽奖按钮下方)
self.status_bar = ttk.Label(center_frame, text="就绪", relief=tk.SUNKEN, anchor=tk.W)
self.status_bar.grid(row=5, column=0, columnspan=4, sticky="ew", pady=(10,0))
# 配置列权重
center_frame.grid_columnconfigure(0, weight=1)
center_frame.grid_columnconfigure(1, weight=1)
center_frame.grid_columnconfigure(2, weight=1)
center_frame.grid_columnconfigure(3, weight=1)
# 右侧面板
right_frame = ttk.Frame(self.root)
right_frame.pack(side=tk.LEFT, padx=10, pady=10, fill=tk.BOTH, expand=True)
# 结果显示
ttk.Label(right_frame, text="抽奖结果").pack()
self.result_tree = ttk.Treeview(right_frame, columns=("award", "winner"), show="headings", height=20)
self.result_tree.heading("award", text="奖项")
self.result_tree.heading("winner", text="中奖者")
self.result_tree.pack(fill=tk.BOTH, expand=True)
def import_participants(self):
file_path = filedialog.askopenfilename(
filetypes=[("CSV文件", "*.csv"), ("文本文件", "*.txt")]
)
if file_path:
try:
if file_path.endswith('.txt'):
with open(file_path, 'r', encoding='utf-8') as f:
self.participants = [line.strip() for line in f if line.strip()]
elif file_path.endswith('.csv'):
with open(file_path, 'r', encoding='utf-8') as f:
reader = csv.reader(f)
self.participants = [row[0] for row in reader if row and row[0].strip()]
else: # Excel文件
messagebox.showinfo("提示", "请将Excel文件另存为CSV格式后再导入")
return
self.participant_list.delete(0, tk.END)
for p in self.participants:
self.participant_list.insert(tk.END, p)
self.update_status(f"成功导入 {len(self.participants)} 位参与者")
except Exception as e:
messagebox.showerror("错误", f"文件读取失败:{str(e)}")
def add_award(self):
name = self.award_name.get()
count = self.award_count.get()
if name and count:
try:
count = int(count)
self.awards.append({"name": name, "count": count})
self.award_tree.insert("", tk.END, values=(name, count))
self.award_name.delete(0, tk.END)
self.award_count.delete(0, tk.END)
self.delete_award_button.config(state=tk.NORMAL) # 启用删除按钮
except ValueError:
messagebox.showerror("错误", "名额必须是数字")
def delete_award(self):
selected = self.award_tree.selection()
if selected:
award_name = self.award_tree.item(selected[0])['values'][0]
self.award_tree.delete(selected[0])
self.awards = [award for award in self.awards if award["name"] != award_name]
self.update_status(f"已删除奖项:{award_name}")
if not self.award_tree.get_children(): # 如果没有奖项,禁用删除按钮
self.delete_award_button.config(state=tk.DISABLED)
def start_lottery(self):
if not self.awards:
messagebox.showwarning("警告", "请先添加奖项!")
return
if not self.participants:
messagebox.showwarning("警告", "请先导入参与者名单!")
return
selected = self.award_tree.selection()
if selected:
self.current_award = self.award_tree.item(selected[0])['values']
self.is_rolling = True
self.delete_award_button.config(state=tk.DISABLED) # 禁用删除按钮
threading.Thread(target=self.roll_animation).start()
self.play_sound() # 播放简单的系统声音
else:
messagebox.showwarning("警告", "请先选择要抽取的奖项!")
def stop_lottery(self):
self.is_rolling = False
def roll_animation(self):
temp_pool = list(set(self.participants)) # 使用集合去重
winner = None
# 设置随机滚动的时间范围(1-3秒)
duration = random.uniform(1, 3)
end_time = time.time() + duration
while self.is_rolling and temp_pool and time.time() < end_time:
winner = random.choice(temp_pool)
self.roll_label.config(text=winner)
temp_pool.remove(winner)
if not temp_pool: # 如果候选人已用完,重新填充
temp_pool = list(set(self.participants))
time.sleep(0.05)
self.root.update()
# 最终选择一个获奖者
if self.is_rolling: # 如果是自然结束
self.is_rolling = False
winner = random.choice(self.participants)
self.roll_label.config(text=winner)
self.root.update()
if winner and not self.is_rolling: # 确保是停止状态
self.save_result(winner)
self.participants.remove(winner)
self.update_status(f"{self.current_award[0]} 抽奖完成!")
self.play_sound(win=True) # 播放获奖声音
def play_sound(self, win=False):
"""
使用系统命令播放简单的提示音
"""
try:
if os.name == 'nt': # Windows
if win:
os.system("echo \a") # 简单的系统提示音
else:
os.system("echo \a")
except:
pass # 忽略声音播放失败
def save_result(self, winner):
award_name = self.current_award[0]
if award_name not in self.results:
self.results[award_name] = []
self.results[award_name].append(winner)
self.result_tree.insert("", tk.END, values=(award_name, winner))
# 更新奖项剩余名额
for item in self.award_tree.get_children():
values = self.award_tree.item(item)['values']
if values[0] == award_name:
remaining = int(values[1]) - 1
if remaining <= 0:
self.award_tree.delete(item)
else:
self.award_tree.item(item, values=(values[0], remaining))
break
def export_results(self):
if not self.results:
messagebox.showwarning("警告", "没有可导出的抽奖结果!")
return
file_path = filedialog.asksaveasfilename(
defaultextension=".csv",
filetypes=[("CSV文件", "*.csv"), ("文本文件", "*.txt")]
)
if file_path:
try:
if file_path.endswith('.csv'):
with open(file_path, 'w', encoding='utf-8', newline='') as f:
writer = csv.writer(f)
writer.writerow(["奖项", "中奖者"])
for award in self.results:
for winner in self.results[award]:
writer.writerow([award, winner])
elif file_path.endswith('.txt'):
with open(file_path, 'w', encoding='utf-8') as f:
for award in self.results:
for winner in self.results[award]:
f.write(f"{award}\t{winner}\n")
messagebox.showinfo("成功", "结果导出成功!")
except Exception as e:
messagebox.showerror("错误", f"导出失败:{str(e)}")
def update_status(self, message):
self.status_bar.config(text=message)
if __name__ == "__main__":
root = tk.Tk()
app = LotteryApp(root)
root.mainloop()