''' Random_Picker 用Python实现的随机点名器 Copyright (C) 2022 海Cha This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ''' import xlrd as excel from tkinter import * import tkinter.messagebox as msgbox import random import sys import winsound import webbrowser version = "v1.2.2" filename = "./data/names.xls" configname = "./data/config.data" selname = "./assets/sel.wav" procname = "./assets/proc.wav" iconname = "./assets/点名器.ico" gui_size = "650x200" protection_override = False up_percent = 30 down_persent = 40 elim_rows = 1 percent_override = True elim_id = [] up_id = [] down_id = [] sound = True students = [] classes = [] subjects = [] filter = [] last_choice = "" gui = Tk() gui.withdraw() gui.title("点名器") gui.geometry(gui_size) gui.resizable(0,0) gui.configure(bg = "white") try: if len(sys.argv) > 1: for arg in sys.argv[1:]: if arg == "--licence": if msgbox.askokcancel('许可证信息', '点名器 ' + version + ' Copyright (C) 2022 海Cha\n本程序是开源程序,受开源协议 GNU General Public Licence v3.0 (GPL v3) 保护\n基于GPL v3协议,本程序没有任何质量保证。\n这是一个自由软件,欢迎再次分发。\n点击「确认」将会由系统默认浏览器打开LICENCE文件\n出现此对话框的原因是由于您在运行本软件时传入了 --licence 参数,欲启动主程序,请移除该参数。'): webbrowser.open_new_tab('https://www.gnu.org/licenses/gpl-3.0.html') assert False if arg == "--version": msgbox.showinfo('版本信息', '点名器 ' + version + ' Copyright (C) 2022 海Cha\n出现此对话框的原因是由于您在运行本软件时传入了 --version 参数,欲启动主程序,请移除该参数。') assert False if arg == "--help": msgbox.showinfo('帮助信息','----- 点名器 ' + version + ' -----\n--licence 显示许可证信息\n--version 显示版本信息\n--path 显示程序运行路径\n--help 显示此信息\n欲打开主程序GUI,则无需传参\n出现此对话框的原因是由于您在运行本软件时传入了 --help 参数,欲启动主程序,请移除该参数。') assert False if arg == "--path": msgbox.showinfo('路径信息','点名器 ' + version + ' Copyright (C) 2022 海Cha\n当前程序运行在 ' + sys.argv[0] + ' 路径上\n出现此对话框的原因是由于您在运行本软件时传入了 --path 参数,欲启动主程序,请移除该参数。') assert False if arg.find("--") != -1: msgbox.showerror('传参错误','无法识别参数 ' + arg + ' 请调整您的启动参数\n传入 --help以查看帮助信息\n出现此对话框的原因是由于您在运行本软件时传入了 ' + arg + ' 参数,欲启动主程序,请移除该参数。') assert False except AssertionError: sys.exit() try: file = open(iconname) file.close() gui.iconbitmap(iconname) except IOError: if not msgbox.askokcancel('资源文件错误', '在 ./assets目录下未能找到 点名器.ico 文件,点击「确定」将加载默认图标,点击「取消」以关闭程序'): sys.exit() try: file = open(selname) file.close() file = open(procname) file.close() except IOError: if msgbox.askokcancel('资源文件错误', '在 ./assets目录下未能找到音频文件,点击「确定」将关闭声音,点击「取消」以关闭程序'): sound = False else: sys.exit() class Student(object): name = "" subject = "" id = "" class_name = "" def __init__(self,id,class_name,subject,name): self.id = id self.class_name = class_name self.subject = subject self.name = name try: with excel.open_workbook(filename) as excel_data: table = excel_data.sheets()[0] # win32api.SetFileAttributes(filename,win32con.FILE_ATTRIBUTE_HIDDEN) except FileNotFoundError: msgbox.showerror('找不到数据库文件', '在相同目录下未能找到 names.xls 文件,请确认您的文件存在') sys.exit() try: cols = table.ncols rows = table.nrows assert cols == 4 assert rows > 1 except AssertionError: msgbox.showerror('数据库文件格式不正确', '数据库文件内行/列数不合法,请确认数据库文件拥有超过1的行数和等于4的列数,当前行数:' + str(rows) + ' 当前列数:' + str(cols)) sys.exit() try: with open(configname,'r') as file: # win32api.SetFileAttributes(configname,win32con.FILE_ATTRIBUTE_HIDDEN) config = file.read().splitlines() for unit in config: temp = unit.split(",") if temp[1] == '0': elim_id.append(int(temp[0])) elif temp[1] == '+': up_id.append(int(temp[0])) elif temp[1] == "-": down_id.append(int(temp[0])) else: pass except: percent_override = False up_percent = len(up_id) * 5 if len(up_id) <= 6 else 30 down_percent = len(down_id) * 8 if len(down_id) <= 5 else 40 gui.deiconify() for i in range(elim_rows,rows): tmp_list = [str(table.cell_value(i,j)) for j in range(0,cols)] if (int(float(tmp_list[0])) not in elim_id): student = Student(tmp_list[0],tmp_list[1],tmp_list[2],tmp_list[3]) students.append(student) cur_stu = students[:] for student in students: if student.class_name not in classes: classes.append(student.class_name) if student.subject not in subjects: subjects.append(student.subject) cur_name = StringVar() def upd_name(name): cur_name.set(name) upd_name("") listbox = Listbox(gui, selectmode = MULTIPLE, height = 5) for tmp_class in classes: listbox.insert("end",tmp_class) def sel_class(flag = 1): global filter, listbox, classes, cur_stu, students, percent_override filter.clear() for selection in listbox.curselection(): # print(selection) filter.append(classes[selection]) # print(filter) if filter: if flag: msgbox.showinfo('班级选择已应用', '已应用当前班级选择', parent=gui) percent_override = False cur_stu.clear() for student in students: if student.class_name in filter: # print(student.name) cur_stu.append(student) else: if flag: msgbox.showwarning('班级选择已应用', '当前设置会选择所有班级的名单,这样对吗?', parent=gui) percent_override = False filter = classes[:] cur_stu = students[:] def choose(): global last_choice, cur_stu sel_class(0) # 触发30%概率,从up中挑选 if (random.randint(1,100) < up_percent and percent_override): protection_override = True; temp = [] for student in cur_stu: if int(float(student.id)) in up_id: temp.append(student) cur_stu = temp[:] elif (random.randint(1,100) < down_persent and percent_override): protection_override = True; temp = []; for student in cur_stu: if int(float(student.id)) not in down_id: temp.append(student) cur_stu = temp[:] else: protection_override = False if not cur_stu: protection_override = False sel_class(0) choice = random.choice(cur_stu) # print(choice.name) counter = 0; while (last_choice == choice.name and (not protection_override) and (len(cur_stu) > 1)) or choice.id in elim_id: choice = random.choice(cur_stu) counter = counter + 1 if counter >= 100: break # print(choice.name) upd_name(choice.name) class_label.config(text = choice.class_name) subject_label.config(text = choice.subject) last_choice = choice.name if sound: winsound.PlaySound(selname, winsound.SND_ASYNC | winsound.SND_FILENAME) startrandom = Button(gui, text = "立刻摇人!", font = ("宋体", 17, "bold"), height = 2, fg = "red", command = choose, bg = "white") setclass = Button(gui, text = "应用班级选用", font = ("宋体", 17), height = 2, command = sel_class, bg = "white") scrool = Scrollbar(gui, command = listbox.yview) listbox.config(yscrollcommand=scrool.set) name_label = Label(gui, textvariable = cur_name, font = ("宋体", 30, "bold"), width = 20, bg = "white", fg = "blue") class_label = Label(gui, text = "", font = ("宋体", 15), width = 20, bg = "white") subject_label = Label(gui, text = "", font = ("宋体", 15), width = 20, bg = "white") class_label.grid(row = 2, column = 1, sticky = N+S+E+W) subject_label.grid(row = 2, column = 2, sticky = N+S+E+W) name_label.grid(row = 1, column = 1, sticky = N+S) listbox.grid(row = 1, column = 2, sticky = N+S) startrandom.grid(row = 3, column = 1, sticky = N+S+E+W) setclass.grid(row = 3, column = 2, sticky = N+S+E+W) scrool.grid(row = 1, column = 3, sticky = N+S) gui.mainloop()