公司年会是企业文化和团队精神的重要体现之一,在年会上,一般会安排一些创意的节目和抽奖环节,而抽奖环节则是最令人期待的环节之一。年会抽奖是年会活动中用于活跃气氛和奖励员工的重要环节,通常通过大屏幕展示抽奖过程,能给员工带来无穷的喜悦感。 传统的抽奖箱抽奖既费时又费力,我们完全可以使用Python代码来实现一个专属的抽奖程序。
1、公司年会抽奖系统介绍
在本教程中,我们将通过Python的Tkinter库构建一个简单的抽奖系统,该抽奖系统主要包括两个核心窗口页面,分别是抽奖的主窗口和奖项设置窗口,在Tkinter应用程序的主窗口里,可以通过“设置奖项”按钮打开奖项设置窗口,在弹出的输入框中自定义输入“奖项名称”和“获奖人数”,比如,特等奖设定1名,一等奖设定2名,以此类推。
为了使抽奖系统使用更方便,我们可以从Excel文件中导入员工名单。假设我们有一个名为“name.xlsx”的Excel文件,每行包含一个员工姓名。如下图所示:
导入Excel文件后,从Excel文件中读入第一列所有的员工姓名,再将员工姓名添加到列表中,用random模块的choice函数来随机选择列表中的一个姓名,最后中奖者姓名将从列表中删除,以免再次中奖。
导入员工姓名和根据要求设置奖项后,就可以点击“开始抽奖”按钮,系统会显示动画抽奖效果,并根据抽奖前后人数与奖项变化逻辑,实时更新显示内容。
2、公司年会抽奖系统界面演示
3、代码实现
(1)安装pandas库
首先,确保你已经安装了pandas库。如果还没有安装,可以通过pip安装:
pip install pandas
(2)导入必要的库
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import pandas as pd
import random, webbrowser
这里导入了Tkinter库用于创建GUI应用图形界面,Tkinter的filedialog模块用于处理文件选择、保存等操作,Tkinter的messagebox用于消息提示。Pandas库的pandas.read_excel()函数可以从Excel文件中读取数据。random库是用于生成和运用随机数的标准库。Python的webbrowser库能够调用系统的默认浏览器或指定浏览器,打开指定的URL或本地的HTML文件。
(3)公司年会抽奖系统的完整代码如下所示:
动手练一练:
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import pandas as pd
import random, webbrowser
# 定义PrizeDraw类,用于封装抽奖系统的功能
class PrizeDraw:
# 构造函数初始化对象
def __init__(self, root):
# 定义root参数,代表Tkinter的根窗口
self.root = root
self.root.title("公司年会抽奖系统")
# 设置窗口居中
window_width = 850
window_height = 660
screen_width = self.root .winfo_screenwidth()
screen_height = self.root .winfo_screenheight()
x = (screen_width - window_width) / 2
y = (screen_height - window_height) / 2
self.root .geometry('%dx%d+%d+%d' % (window_width, window_height, x, y))
self.root .resizable(width=False, height=False)
# 定义空列表,用于存放当前可抽奖员工列表
self.members = []
# 定义布尔值,用于启动或暂停抽奖动画
self.rolling_status = False
# 定义空列表,用于存放奖项列表
self.awards = []
# 初始化奖项索引,“-1”表示当前未设置奖项
self.award_set_state = -1
# 创建菜单栏,包括“文件”和“设置”菜单
self.set_menu()
# 创建抽奖系统主界面
self.set_widgets()
# 动态更新抽奖按钮状态
self.show_button_update()
# 定义菜单界面,包括“文件”和“设置”菜单
def set_menu(self):
menu_part = tk.Menu(self.root)
add_menu = tk.Menu(menu_part, tearoff=0)
add_menu.add_command(label="导入员工名单", command=self.import_members)
add_menu.add_command(label="导出中奖结果", command=self.export_winners)
add_menu.add_command(label="关于程序", command=self.program_about)
add_menu.add_command(label="程序讲解", command=self.help_window)
add_menu.add_separator()
add_menu.add_command(label="退出", command=self.root.quit)
menu_part.add_cascade(label="文件", menu=add_menu)
settings_menu = tk.Menu(menu_part, tearoff=0)
settings_menu.add_command(label="管理奖项", command=self.manage_awards)
settings_menu.add_command(label="重置系统", command=self.reset_data)
menu_part.add_cascade(label="设置", menu=settings_menu)
self.root.config(menu=menu_part)
# 程序讲解页面
def help_window(self):
webbrowser.open("https://www.pyhint.com/article/152.html")
# 定义“关于程序”页面
def program_about(self):
about = tk.Tk()
about.title('关于程序')
# 设置窗口居中
window_width = 350
window_height = 350
screen_width = about.winfo_screenwidth()
screen_height = about.winfo_screenheight()
x = (screen_width - window_width) / 2
y = (screen_height - window_height) / 2
about.geometry('%dx%d+%d+%d' % (window_width, window_height, x, y))
about.resizable(width=False, height=False)
about_frame = tk.Frame(about, width=320, height=320)
about_frame.pack()
tk.Label(about_frame, text='公司年会抽奖系统', font=("宋体", 12)).place(x=90, y=20)
tk.Label(about_frame, text='使用编程语言:Python', font=("宋体", 12)).place(x=50, y=90)
tk.Label(about_frame, text='导入名单方式:从Excel文件导入', font=("宋体", 12)).place(x=50, y=150)
tk.Label(about_frame, text='保存中奖名单方式:导出到Excel文件', font=("宋体", 12)).place(x=50, y=210)
tk.Label(about_frame, text='创作者:www.pyhint.com', font=("宋体", 12)).place(x=50, y=270)
about.mainloop()
# 定义抽奖系统主界面
def set_widgets(self):
base_frame = tk.Frame(self.root)
base_frame.place(relx=0.5, rely=0.5, anchor="center")
# 创建一个StringVar变量并设置默认值
title_text = tk.StringVar()
# 设置变量的初始值
title_text.set("XX公司年会抽奖活动(可编辑)")
# 定义标题输入框,打开程序时可输入自己的公司名称
self.title_label = tk.Entry(base_frame, text=title_text, font=('宋体', 36, 'bold'), bg="white", width=30, justify='center')
self.title_label.pack(pady=20)
# 创建一个框架,用于导入抽奖名单
load_frame = tk.Frame(base_frame)
load_frame.pack(pady=10)
# 左边创建一个标签显示“已加载人数”
self.member_count = tk.Label(load_frame, text="已加载员工:0人", font=("宋体", 12))
self.member_count.pack(padx=10, pady=10, side=tk.LEFT)
# 右边创建“导入员工名单”按钮并绑定到import_members函数
load_button = tk.Button(load_frame, text="导入员工名单", command=self.import_members, width=15)
load_button.pack(padx=10, pady=10, side=tk.RIGHT)
# 创建一个标签,用于显示抽奖动画
self.name_label = tk.Label(base_frame, text="抽奖准备就绪……", font=('宋体', 42, 'bold'), width=20)
self.name_label.pack(pady=20)
# 创建一个框架,用于显示中奖结果
show_frame = tk.Frame(base_frame)
show_frame.pack(pady=10)
self.winner_form = ttk.Treeview(show_frame, columns=("name", "award"), show="headings", height=10)
self.winner_form.heading("name", text="姓名")
self.winner_form.heading("award", text="奖项")
self.winner_form.grid(row=1, columnspan=1)
# 创建滚动条
scrollbar = ttk.Scrollbar(show_frame, orient="vertical", command=self.winner_form.yview)
scrollbar.grid(row=1, column=1, sticky="ns")
# 将滚动条控件与Treeview控件关联
self.winner_form.config(yscrollcommand=scrollbar.set)
# 创建一个框架,用于存放抽奖过程按钮
button_frame = tk.Frame(base_frame)
button_frame.pack(pady=10)
self.begin_button = tk.Button(button_frame, text="开始抽奖", command=self.start_lottery, width=15)
self.begin_button.pack(side="left", padx=10)
self.stop_button = tk.Button(button_frame, text="停止抽奖", command=self.stop_lottery, width=15, state="disabled")
self.stop_button.pack(side="left", padx=10)
self.settings_Button = tk.Button(button_frame, text="设置奖项", command=self.manage_awards, width=15)
self.settings_Button.pack(side="left", padx=10)
self.refresh_button = tk.Button(button_frame, text='复位', command=self.reset_data, width=10)
self.refresh_button.pack(side="left", padx=10)
# 创建一个框架,用于显示抽奖过程的实时奖项名称和剩余名额
self.award_frame = tk.Frame(base_frame)
self.award_frame.pack(pady=10)
self.show_now_award = tk.Label(self.award_frame, text="当前奖项:未设置", font=("宋体", 12))
self.show_now_award.pack()
self.show_remaining = tk.Label(self.award_frame, text="剩余名额:0", font=("宋体", 12))
self.show_remaining.pack()
# 定义复位函数,清空所有数据
def reset_data(self):
# 清空员工名单
self.members.clear()
self.root.update_idletasks()
self.awards = []
self.name_label.config(text="抽奖准备就绪……")
# 删除所有条目
self.winner_form.delete(*self.winner_form.get_children())
self.show_now_award_update()
self.member_count.config(text='已加载员工:0人')
self.show_button_update()
# 更新抽奖按钮状态,如果完成抽奖操作,“抽奖”按钮将立刻变成禁用状态
def show_button_update(self):
if self.award_set_state >= len(self.awards):
self.begin_button.config(state='disabled')
else:
self.begin_button.config(state='normal')
# 实时更新奖项显示
def show_now_award_update(self):
if not self.awards:
self.award_set_state = -1
self.show_now_award.config(text="当前奖项:未设置")
self.show_remaining.config(text="剩余名额:0")
return
if self.award_set_state == -1:
self.award_set_state = 0
if 0 <= self.award_set_state < len(self.awards):
now_award = self.awards[self.award_set_state]
remaining = now_award["quota"] - len(now_award["winners"])
self.show_now_award.config(text=f"当前奖项:{now_award['name']}")
self.show_remaining.config(
text=f"剩余名额:{remaining} ({len(now_award['winners'])}/{now_award['quota']})")
else:
self.show_now_award.config(text="所有奖项已抽取完毕")
self.show_remaining.config(text="剩余名额:0")
# 从Excel表格中导入抽奖名单
def import_members(self):
save_path = filedialog.askopenfilename(filetypes=[("Excel文件", "*.xlsx")])
if save_path:
try:
df = pd.read_excel(save_path, usecols='A', header=None)
column_list = df[df.columns[0]].tolist()
self.members = column_list.copy()
self.member_count.config(text=f"已加载员工:{len(self.members)}人")
messagebox.showinfo("成功", "员工名单导入成功!")
except Exception as e:
messagebox.showerror("错误", f"文件读取失败:{str(e)}")
# 定义奖项管理界面
def manage_awards(self):
set_award = tk.Toplevel(self.root)
set_award.title("奖项管理")
# 设置窗口居中
window_width = 400
window_height = 380
screen_width = set_award.winfo_screenwidth()
screen_height = set_award.winfo_screenheight()
x = (screen_width - window_width) / 2
y = (screen_height - window_height) / 2
set_award.geometry('%dx%d+%d+%d' % (window_width, window_height, x, y))
set_award.resizable(width=False, height=False)
# 创建“奖项名称”和“获奖人数”输入框,可以自定义输入
tk.Label(set_award, text="奖项名称:").grid(row=0, column=0, padx=10, pady=10, sticky="e")
award_name = tk.Entry(set_award)
award_name.grid(row=0, column=1, padx=10, pady=10, sticky="w")
tk.Label(set_award, text="获奖人数:").grid(row=1, column=0, padx=10, pady=10, sticky="e")
award_count = tk.Entry(set_award)
award_count.grid(row=1, column=1, padx=10, pady=10, sticky="w")
# 定义“添加奖项”函数
def award_add():
name = award_name.get().strip()
count_get = award_count.get().strip()
if not name:
messagebox.showerror("错误", "奖项名称不能为空")
return
try:
count = int(count_get)
if count <= 0:
raise ValueError
except ValueError:
messagebox.showerror("错误", "请输入有效的正整数")
return
if any(award["name"] == name for award in self.awards):
messagebox.showerror("错误", "该奖项名称已存在")
return
self.awards.append({
"name": name,
"quota": count,
"winners": []
})
listbox_update()
award_name.delete(0, tk.END)
award_count.delete(0, tk.END)
self.show_now_award_update()
# 删除已设置的奖项
def award_delete():
selected = listbox.curselection()
if not selected:
messagebox.showwarning("警告", "请先选择奖项再删除")
return
index = selected[0]
if len(self.awards[index]["winners"]) > 0:
messagebox.showwarning("警告", "该奖项已有中奖者,不能删除")
return
del self.awards[index]
listbox_update()
self.show_now_award_update()
# 定义退出函数
def user_confirm():
set_award.destroy()
add_button = tk.Button(set_award, text="添加奖项并保存", command=award_add)
add_button.grid(row=2, columnspan=2, padx=10, pady=10)
listbox = tk.Listbox(set_award, width=50)
listbox.grid(row=3, columnspan=2, padx=10, pady=5)
del_button = tk.Button(set_award, text="删除选中奖项", command=award_delete)
del_button.grid(row=4, column=0, padx=10, pady=5, sticky="e")
# 创建“确认设置”按钮
confirm_button = tk.Button(set_award,text='确认设置', command=user_confirm)
confirm_button.grid(row=4, column=1, padx=10, pady=5)
# 显示已设置的奖项
def listbox_update():
listbox.delete(0, tk.END)
for award in self.awards:
listbox.insert(tk.END, f"{award['name']} - {award['quota']}人")
listbox_update()
set_award.transient(self.root)
set_award.grab_set()
self.root.wait_window(set_award)
# 检查奖项状态
def check_awards_status(self):
if self.award_set_state >= len(self.awards):
return False
now_award = self.awards[self.award_set_state]
return len(now_award['winners']) < now_award['quota']
# 定义函数,用于绑定“开始抽奖”按钮
def start_lottery(self):
if not self.members:
messagebox.showwarning("警告", "请导入员工名单文件")
return
if not self.awards:
messagebox.showwarning("警告", "请先设置奖项")
return
if self.award_set_state >= len(self.awards):
messagebox.showinfo('提示', '所有奖项已抽取完毕,感谢参与!')
self.begin_button.config(state='disabled')
return
while self.award_set_state < len(self.awards):
now_award = self.awards[self.award_set_state]
if len(now_award['winners']) < now_award['quota']:
break
self.award_set_state += 1
if self.award_set_state >= len(self.awards):
messagebox.showinfo('提示', '所有奖项已抽取完毕,感谢参与!')
self.begin_button.config(state='disabled')
return
if self.award_set_state >= len(self.awards):
self.award_set_state = 0
now_award = self.awards[self.award_set_state]
if len(now_award["winners"]) >= now_award["quota"]:
self.award_set_state += 1
if self.award_set_state >= len(self.awards):
messagebox.showinfo("提示", "所有奖项已抽取完毕,感谢参与!")
return
self.rolling_status = True
self.begin_button.config(state="disabled")
self.stop_button.config(state="normal")
self.roll()
# 定义滚动效果函数
def roll(self):
if self.rolling_status and self.members:
current_name = random.choice(self.members)
self.name_label.config(text=current_name)
self.root.after(50, self.roll)
else:
self.stop_lottery()
# 定义函数,用于绑定“停止抽奖”按钮
def stop_lottery(self):
if not self.rolling_status:
return
self.rolling_status = False
self.begin_button.config(state="normal")
self.stop_button.config(state="disabled")
if not self.members:
messagebox.showwarning("警告", "员工名单已空")
return
now_winner = self.name_label.cget("text")
if now_winner not in self.members:
return
try:
now_award = self.awards[self.award_set_state]
now_award["winners"].append(now_winner)
self.winner_form.insert("", "end", values=(now_winner, now_award["name"]))
self.members.remove(now_winner)
self.member_count.config(text=f"已加载员工:{len(self.members)}人")
self.show_now_award_update()
if len(now_award["winners"]) >= now_award["quota"]:
self.award_set_state += 1
if self.award_set_state < len(self.awards):
messagebox.showinfo("提示", f"{now_award['name']}抽奖完成,即将开始下一个奖项")
self.show_now_award_update()
except IndexError:
messagebox.showerror("错误", "无效的奖项索引")
except ValueError:
messagebox.showerror("错误", "找不到中奖员工")
if self.award_set_state >= len(self.awards):
self.begin_button.config(state='disabled')
messagebox.showinfo('提示', '所有奖项已抽取完毕,感谢参与!')
self.root.update_idletasks()
return
if not self.check_awards_status():
self.begin_button.config(state='disabled')
self.root.update_idletasks()
# 导出抽奖结果到指定Excel表格
def export_winners(self):
if not any(len(award["winners"]) > 0 for award in self.awards):
messagebox.showwarning("警告", "没有中奖数据可导出")
return
try:
data = []
for award in self.awards:
for winner in award["winners"]:
data.append([winner, award["name"]])
df = pd.DataFrame(data, columns=["姓名", "奖项"])
save_path = filedialog.asksaveasfilename(
defaultextension=".xlsx",
filetypes=[("Excel文件", "*.xlsx")])
if save_path:
df.to_excel(save_path, index=False)
messagebox.showinfo("成功", "导出成功!")
except PermissionError:
messagebox.showerror("错误", "文件被占用,请关闭文件后重试")
except Exception as e:
messagebox.showerror("错误", f"导出失败:{str(e)}")
# 当前模块直接被执行
if __name__ == "__main__":
# 创建Tkinter窗口
root = tk.Tk()
# 实例化抽奖主页面
app = PrizeDraw(root)
# 开启主循环,让窗口处于显示状态
root.mainloop()