引言
在各类比赛和活动中,抽签、中奖、分组、配对等操作常常是必不可少的步骤。为了提高操作的效率和减少人为错误,开发一个基于Python和Tkinter的抽签系统可以大大简化这些流程。这篇博文将介绍如何使用Tkinter构建一个用于“2024年包头市‘工匠杯’CAD机械设计比赛”的抽签系统,支持多种模式,包括抽签模式、中奖模式、分组模式和配对模式。
项目介绍
该抽签系统支持以下几种模式:
1. **抽签模式**:随机从导入的名单中选择一位参与者,并为其分配一个随机号码。
2. **中奖模式**:从导入名单中选择若干名中奖者,逐个显示中奖结果。
3. **分组模式**:根据导入名单,将所有参与者随机分配到指定的组别中。
4. **配对模式**:从两个导入的名单中,随机配对,形成对应的左右名单配对。
主要功能
- **导入Excel名单**:支持导入Excel格式的名单,并根据不同模式执行相应的操作。
- **随机抽取**:在抽签模式和配对模式下,支持随机抽取名单或配对。
- **滚动显示效果**:在抽签和配对模式下,系统会模拟滚动效果,直到用户手动停止,显示最终结果。
- **历史记录和导出**:所有结果都实时展示在界面中,并且可以导出到Excel文件中保存。
代码实现
import tkinter as tk
from tkinter import filedialog, messagebox
import pandas as pd
import random
class RaffleApp:
def __init__(self, root):
self.root = root
self.root.title("2024 年包头市“工匠杯”CAD 机械设计职工职业技能比赛 抽签系统")
self.mode = "抽签模式" # 初始为抽签模式
self.names = []
self.left_names = [] # 配对模式中的左名单
self.right_names = [] # 配对模式中的右名单
self.results = [] # 用于存储抽签/中奖/分组/配对结果
self.groups = {} # 分组模式下的组
self.rolling_number = None # 滚动显示的数字
self.is_rolling = False # 控制数字滚动的状态
self.current_name = "" # 当前抽签人
self.selected_numbers = set() # 已经抽取的数字
self.winner_queue = [] # 中奖人列表,用于中奖模式的逐个展示
self.current_left = "" # 当前配对模式中的左名单
self.current_right = "" # 当前配对模式中的右名单
self.create_widgets()
def create_widgets(self):
# 主框架
self.main_frame = tk.Frame(self.root, padx=20, pady=20)
self.main_frame.pack(fill=tk.BOTH, expand=True)
# 标题
self.title_label = tk.Label(self.main_frame, text="2024 年包头市“工匠杯”CAD 机械设计比赛 抽签系统",
font=("Arial", 38), padx=10, pady=10)
self.title_label.pack(pady=(0, 20))
# 模式切换按钮
self.mode_frame = tk.Frame(self.main_frame)
self.mode_frame.pack(pady=(0, 20))
self.raffle_mode_button = tk.Button(self.mode_frame, text="抽签模式", command=self.set_raffle_mode)
self.raffle_mode_button.pack(side=tk.LEFT, padx=10)
self.prize_mode_button = tk.Button(self.mode_frame, text="中奖模式", command=self.set_prize_mode)
self.prize_mode_button.pack(side=tk.LEFT, padx=10)
self.group_mode_button = tk.Button(self.mode_frame, text="分组模式", command=self.set_group_mode)
self.group_mode_button.pack(side=tk.LEFT, padx=10)
self.pair_mode_button = tk.Button(self.mode_frame, text="配对模式", command=self.set_pair_mode)
self.pair_mode_button.pack(side=tk.LEFT, padx=10)
# 主操作区域
self.operation_frame = tk.Frame(self.main_frame)
self.operation_frame.pack()
# 导入按钮
self.load_button = tk.Button(self.operation_frame, text="导入名单", command=self.load_names)
self.load_button.pack(side=tk.LEFT, padx=5)
# 开始按钮
self.start_button = tk.Button(self.operation_frame, text="开始", command=self.start_action, state=tk.DISABLED)
self.start_button.pack(side=tk.LEFT, padx=5)
# 停止按钮 (用于抽签和配对模式)
self.stop_button = tk.Button(self.operation_frame, text="停止", command=self.stop_action, state=tk.DISABLED)
self.stop_button.pack(side=tk.LEFT, padx=5)
# 导出按钮,启用状态
self.export_button = tk.Button(self.operation_frame, text="导出结果", command=self.export_results, state=tk.NORMAL)
self.export_button.pack(side=tk.LEFT, padx=5)
# 显示当前结果的标签
self.result_label = tk.Label(self.main_frame, text="", font=("Arial", 50))
self.result_label.pack(pady=(20, 10))
# 历史记录框
self.history_label = tk.Label(self.main_frame, text="历史记录:", font=("Arial", 18))
self.history_label.pack(pady=(10, 5))
self.history_listbox = tk.Listbox(self.main_frame, width=200, height=100)
self.history_listbox.pack(pady=(0, 20))
self.set_raffle_mode() # 默认初始化为抽签模式
def set_raffle_mode(self):
self.mode = "抽签模式"
self.stop_button.pack(side=tk.LEFT, padx=5) # 显示停止按钮
self.result_label.config(text="") # 清空结果显示
self.history_listbox.delete(0, tk.END)
self.update_buttons()
def set_prize_mode(self):
self.mode = "中奖模式"
self.stop_button.pack_forget() # 隐藏停止按钮
self.result_label.config(text="") # 清空结果显示
self.history_listbox.delete(0, tk.END)
self.update_buttons()
def set_group_mode(self):
self.mode = "分组模式"
self.stop_button.pack_forget() # 隐藏停止按钮
self.result_label.config(text="") # 清空结果显示
self.history_listbox.delete(0, tk.END)
self.update_buttons()
def set_pair_mode(self):
self.mode = "配对模式"
self.stop_button.pack(side=tk.LEFT, padx=5)
self.result_label.config(text="") # 清空结果显示
self.history_listbox.delete(0, tk.END)
self.update_buttons()
def update_buttons(self):
if (self.names or (self.left_names and self.right_names)):
self.start_button.config(state=tk.NORMAL)
else:
self.start_button.config(state=tk.DISABLED)
def load_names(self):
if self.mode == "配对模式":
# Load left and right lists for pairing mode
left_file = filedialog.askopenfilename(title="导入左名单", filetypes=[("Excel Files", "*.xlsx")])
right_file = filedialog.askopenfilename(title="导入右名单", filetypes=[("Excel Files", "*.xlsx")])
if left_file and right_file:
left_df = pd.read_excel(left_file)
right_df = pd.read_excel(right_file)
self.left_names = left_df.iloc[:, 0].dropna().tolist()
self.right_names = right_df.iloc[:, 0].dropna().tolist()
self.result_label.config(text=f"已导入左名单 {len(self.left_names)} 人, 右名单 {len(self.right_names)} 人")
self.update_buttons()
else:
# Load names for other modes
file_path = filedialog.askopenfilename(filetypes=[("Excel Files", "*.xlsx")])
if file_path:
df = pd.read_excel(file_path)
self.names = df.iloc[:, 0].dropna().tolist()
self.result_label.config(text=f"已导入 {len(self.names)} 人员")
self.update_buttons()
def start_action(self):
if self.mode == "抽签模式":
self.start_raffle()
elif self.mode == "中奖模式":
self.start_prize_draw()
elif self.mode == "分组模式":
self.start_grouping()
elif self.mode == "配对模式":
self.start_pairing()
def stop_action(self):
if self.mode == "抽签模式":
self.stop_raffle()
elif self.mode == "配对模式":
self.stop_pairing()
def start_raffle(self):
if self.names:
self.current_name = random.choice(self.names)
self.result_label.config(text=f"当前抽签人: {self.current_name} 正在抽取数字...")
self.names.remove(self.current_name)
self.is_rolling = True # 开始滚动
self.roll_number()
self.start_button.config(state=tk.DISABLED)
self.stop_button.config(state=tk.NORMAL)
def roll_number(self):
if self.is_rolling:
available_numbers = list(set(range(1, len(self.results) + len(self.names) + 1)) - self.selected_numbers)
if available_numbers:
self.rolling_number = random.choice(available_numbers)
self.result_label.config(text=f"{self.current_name} 正在抽取: {self.rolling_number}")
self.root.after(100, self.roll_number)
def stop_raffle(self):
self.is_rolling = False
self.selected_numbers.add(self.rolling_number)
self.result_label.config(text=f"{self.current_name} 的抽签结果: {self.rolling_number}")
self.history_listbox.insert(tk.END, f"{self.current_name}: {self.rolling_number}")
self.results.append((self.current_name, self.rolling_number))
self.stop_button.config(state=tk.DISABLED)
self.update_buttons()
def start_pairing(self):
if self.left_names and self.right_names:
self.is_rolling = True
self.roll_pair()
self.start_button.config(state=tk.DISABLED)
self.stop_button.config(state=tk.NORMAL)
def roll_pair(self):
if self.is_rolling and self.left_names and self.right_names:
self.current_left = random.choice(self.left_names)
self.current_right = random.choice(self.right_names)
self.result_label.config(text=f"配对中: {self.current_left} 和 {self.current_right}")
self.root.after(100, self.roll_pair)
def stop_pairing(self):
self.is_rolling = False
self.result_label.config(text=f"配对结果: {self.current_left} 和 {self.current_right}")
self.history_listbox.insert(tk.END, f"{self.current_left} 和 {self.current_right}")
self.results.append((self.current_left, self.current_right))
self.left_names.remove(self.current_left)
self.right_names.remove(self.current_right)
self.stop_button.config(state=tk.DISABLED)
if not self.left_names or not self.right_names:
self.start_button.config(state=tk.DISABLED)
self.update_buttons()
def export_results(self):
file_path = filedialog.asksaveasfilename(defaultextension=".xlsx", filetypes=[("Excel Files", "*.xlsx")])
if file_path:
if self.mode == "配对模式":
df = pd.DataFrame(self.results, columns=["左名单", "右名单"])
else:
df = pd.DataFrame(self.results, columns=["姓名", "抽签结果"])
df.to_excel(file_path, index=False)
messagebox.showinfo("导出成功", "结果已成功导出到 Excel 文件。")
if __name__ == "__main__":
root = tk.Tk()
app = RaffleApp(root)
root.mainloop()
总结
通过这个抽签系统,我们可以轻松实现多个比赛流程的自动化管理,包括抽签、中奖、分组以及配对操作。代码的可扩展性也非常好,未来可以进一步添加更多功能,例如自定义分组策略、自动存档等功能。这是一个简单而实用的比赛管理工具,使用Python的Tkinter框架开发,可以为比赛组织者带来很大的便利。
后续改进
未来可以考虑的改进:
1. **用户界面的优化**:提升交互体验。
2. **更多的模式支持**:例如多人配对、复杂分组策略等。
3. **抽签动画效果**:加入更炫酷的视觉效果。