Files
Random-Picker/点名器.pyw

260 lines
9.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'''
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 <https://www.gnu.org/licenses/>.
'''
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()