Python实现单位活动抽奖实用工具

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()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学习&实践爱好者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值