ClassAux/main.py
2025-04-02 23:24:14 +08:00

388 lines
16 KiB
Python

import tkinter as tk
from tkinter import ttk
import yaml
import colorama
import threading
import random
import time
import sys
import csv
import functools
from PIL import Image
Image.CUBIC = Image.BICUBIC
#Image.CUBIC = Resampling.BICUBIC
import ttkbootstrap as tb
marking_buttons = list()
version='v2.1'
config = None
current_member = dict()
member_sum = 0
stdtime = 0.1
random_stop = 0
LICENSE = None
member = None
is_paused = 1
is_tab_shown = 0
default_tab_layout = None
is_first = 1
db = None
marking_colormark = dict() # 标记颜色序号
is_dark = None
mk_fg = []
mk_bg = []
btnbg = None
style = None
btnfg = None
ttkthemename = None
about_text = f"""CLASSAUX - 幸运选手α
带轻量级数据库和随机功能的教学管理辅助程序 版本 {version}
版权所有 (C) 2025 Wang Zhiyu
本程序为共产版权的自由软件, 在自由软件联盟发布的GNU通用公共许可协议的约束下,
你可以对其进行再发布及修改。协议版本为第三版。
我们希望发布的这款程序有用, 但不保证, 甚至不保证它有经济价值和适合特定用途。
详情参见 GNU 通用公共许可协议
你理当已收到一份GNU通用公共许可协议的副本,
如果没有, 请查阅 <http://www.gnu.org/licenses/>
--------------------------------------------------------------------------------
使用的第三方资源与开放源代码库:
Colorama - Cross-platform colored terminal text
Tkinter - Python interface to Tcl/Tk
Pillow - Python Imaging Library
PyYaml - YAML parser and emitter for Python
Sarasa Gothic (更纱黑体)
--------------------------------------------------------------------------------
开源软件是一项立足于协作精神的事业,
它的运作和产出不受任何单一个人或者机构的控制。
不管您来自何方,我们都欢迎您加入并做出贡献。
开放源代码/报告程序缺陷: https://github.com/david-ajax/classaux
--------------------------------------------------------------------------------
"""
current_member["pause"] = None
current_member["show"] = ""
def init_theme():
global ttkthemename
global mk_bg
global mk_fg
global style
global btnfg
global btnbg
global sbtnfg
global sbtnbg
if not is_dark:
mk_bg = ["#FFFFFF","#009900","#ff6666","#0088ff"]
mk_fg = ["#000000","#FFFFFF","#FFFFFF","#FFFFFF"]
ttkthemename = "cosmo"
btnbg = [('pressed', '#2097ff'), ('active', '#9bcbff')]
btnfg = [('pressed', 'white'), ('active', '#36393b')]
sbtnbg = 'white'
sbtnfg = '#36393b'
else:
mk_fg = ["#FFFFFF","#00ff00","#ff6666","#9999ff"]
mk_bg = ["#202020","#005500","#550000","#000055"]
ttkthemename = "darkly"
btnbg = [('pressed', '#242424'), ('active', '#242424')]
btnfg = [('pressed', '#FFFFFF'), ('active', '#dddddd')]
sbtnbg = "#202020"
sbtnfg = 'white'
def matrix_gen(format):
ttmem = list()
tmem = list()
mem = list(config["member"])
random.shuffle(mem)
print(list(enumerate(mem)))
for i, ele in enumerate(mem):
tmem.append(ele["name"])
if i % 8 == 0:
ttmem.append(tmem)
tmem = list()
ttmem.reverse()
omember = None
def random_gen():
global config
global current_member
global random_stop
global member
member = config['member']
while not random_stop:
#print("重置列表")
random.shuffle(member)
#print(member)
for i in member:
if random_stop:
break
#print(i)
if i['weight'] == 0:
pass
else:
for k in range(0, i['weight']):
current_member["genuine"] = i['name']
time.sleep(stdtime)
def interface():
global btnfg
global btnbg
global style
global ttkthemename
global sbtnbg
global sbtnfg
root = tb.Window(themename=ttkthemename)
#root = tk.Tk()
root.iconphoto(False, tk.PhotoImage(file='logo.png'))
style = tb.Style()
style.configure("Marking.TButton", background=sbtnbg, foreground=sbtnfg)
style.map("Marking.TButton",
background=btnbg,
foreground=btnfg)
style.configure("TabToggle.TButton", background=sbtnbg, foreground=sbtnfg)
style.map("TabToggle.TButton",
background=btnbg,
foreground=btnfg)
root.title(f"幸运选手α {version}")
root.geometry("800x600")
notebook = ttk.Notebook(root)
notebook.pack(padx=10, pady=10)
tab_electing = tk.Frame(notebook, width=800, height=600)
tab_marking = tk.Frame(notebook, width=800, height=600)
tab_about = tk.Frame(notebook, width=800, height=600)
tab_bat = tk.Frame(notebook, width=800, height=600)
tab_edit = tk.Frame(notebook, width=800, height=600)
tab_filter = tk.Frame(notebook, width=800, height=600)
tab_migration = tk.Frame(notebook, width=800, height=600)
tab_cfg = tk.Frame(notebook, width=800, height=600)
notebook.add(tab_electing, text='单次抽签')
notebook.add(tab_marking, text='标记视图')
notebook.add(tab_bat, text='批量视图')
notebook.add(tab_edit, text='编辑数据库')
notebook.add(tab_filter, text='配置筛选器')
notebook.add(tab_cfg, text='设置')
notebook.add(tab_migration, text='转换兼容配置')
notebook.add(tab_about, text='关于')
marking_colormode = tk.StringVar()
bat_formationmode = tk.StringVar()
bat_attrmode = tk.StringVar()
chosen_attr = tk.StringVar()
def update_data():
global is_paused
if not is_paused:
wdgt[tab_electing][0].config(text=config['member'][random.randint(0, member_sum - 1)]["name"])
else:
wdgt[tab_electing][0].config(text=current_member["show"])
wdgt[tab_electing][0].after(int(1000 * stdtime * 0.75), update_data)
def on_closing():
root.destroy()
global random_stop
random_stop = 1
def toggle(): # 仅用于单次抽取模式
global is_paused
if not is_paused:
current_member["show"] = current_member["genuine"]
is_paused = 1
wdgt[tab_electing][1].config(text="开始")
else:
wdgt[tab_electing][1].config(text="暂停")
is_paused = 0
def toggle_menu():
global is_tab_shown
global default_tab_layout
if not is_tab_shown:
style.layout('TNotebook.Tab', default_tab_layout)
is_tab_shown = 1
else:
style.layout('TNotebook.Tab', [])
is_tab_shown = 0
def toggle_dark():
global style
global is_dark
global ttkthemename
global btnbg
global btnfg
global sbtnbg
global sbtnfg
global is_tab_shown
is_dark = not is_dark
init_theme()
style.theme_use(ttkthemename)
style.configure("Marking.TButton", background=sbtnbg, foreground=sbtnfg)
style.map("Marking.TButton",
background=btnbg,
foreground=btnfg)
style.configure("TabToggle.TButton", background=sbtnbg, foreground=sbtnfg)
style.map("TabToggle.TButton",
background=btnbg,
foreground=btnfg)
marking_clear()
is_tab_shown = not is_tab_shown
toggle_menu()
def marking_clear():
global style
global marking_buttons
marking_colormark.clear()
for i in marking_buttons:
marking_change_color(i)
wdgt = {
tab_electing: [
ttk.Label(tab_electing, text="待抽取", font=("Sarasa UI SC", 70, 'bold')),
ttk.Button(tab_electing, text="开始", command=toggle),
ttk.Button(tab_electing, text="选项卡", command=toggle_menu,style="TabToggle.TButton"),
ttk.Button(tab_electing, text="暗色调", command=toggle_dark,style="TabToggle.TButton"),
],
tab_about: [
tk.Text(tab_about, wrap=tk.WORD,font=("Sarasa UI SC", 10, '')),
#ttk.Label(tab_about, text=about_text)
],
tab_edit: [tk.Text(tab_about, wrap=tk.WORD,font=("Sarasa UI Mono SC", 10, '')),
],
tab_marking: [
ttk.Button(tab_marking, text="清空",command=marking_clear),
ttk.Checkbutton(tab_marking, text="只读模式", onvalue=1, offvalue=0,cursor="arrow",state="normal"),
ttk.Combobox(tab_marking, textvariable=marking_colormode, values=("双值模式", "三值模式", "五值模式"))
],
tab_bat: [
tk.Text(tab_bat),
ttk.Button(tab_bat, text="生成新随机", command=matrix_gen),
ttk.Button(tab_bat, text="复制到剪贴板",style="TabToggle.TButton"),
ttk.Button(tab_bat, text="导出至文件 (CSV/TXT)",style="TabToggle.TButton"),
ttk.Combobox(tab_bat, textvariable=bat_formationmode, values=("选择格式...", "线性列表", "ASCII 表格", "CSV (逗号分隔)", "Markdown 表格")),
ttk.Combobox(tab_bat, textvariable=bat_attrmode, values=("选择随机项...", "姓名", "性别", "居住"))
],
tab_filter: [
ttk.Combobox(tab_filter,textvariable=chosen_attr, values=["选择筛选项...","示例1","示例2"]),
ttk.Label(tab_filter, text="属性: 无"),
ttk.Label(tab_filter, text="类型: 无"),
ttk.Label(tab_filter, text="注意: 更改是实时的")
],
tab_migration: [
ttk.Combobox(tab_filter,textvariable=chosen_attr, values=["选择兼容的配置文件格式...","示例1","示例2"]),
ttk.Label(tab_filter, text="支持的文件格式: TXT 姓名列表(回车分隔), 标准 CSV 文件"),
],
tab_cfg: [
ttk.Label(tab_cfg, text="外观",font=("Sarasa UI SC", 14, 'bold')),
ttk.Label(tab_cfg, text="功能",font=("Sarasa UI SC", 14, 'bold')),
ttk.Label(tab_cfg, text="调试",font=("Sarasa UI SC", 14, 'bold')),
ttk.Combobox(tab_cfg, values=["选择主题", "cosmo (默认)", "flatly", "darkly"]),
ttk.Combobox(tab_cfg, values=["选择字体", "更纱黑体 (默认)", "系统默认字体"]),
]
}
wdgt[tab_electing][0].place(relx=0.5, rely=0.35, anchor='center')
wdgt[tab_electing][1].place(relx=0.5, rely=0.8, relwidth=0.25, relheight=0.15, anchor='center')
wdgt[tab_electing][2].place(relx=0.01, rely=0.99, relwidth=0.12, relheight=0.08, anchor='sw')
wdgt[tab_electing][3].place(relx=0.99, rely=0.99, relwidth=0.12, relheight=0.08, anchor='se')
wdgt[tab_about][0].insert(tk.END, about_text,"center")
wdgt[tab_about][0].insert(tk.END, LICENSE)
wdgt[tab_about][0].grid(row=1, column=0, sticky=tk.N)
wdgt[tab_about][0].configure(state="disabled")
wdgt[tab_bat][4].current(0)
wdgt[tab_bat][1].place(relx=0.5, rely=0.845, relwidth=0.25, relheight=0.13, anchor='n')
wdgt[tab_bat][2].place(relx=0.99, rely=0.99, relwidth=0.3, relheight=0.08, anchor='se')
wdgt[tab_bat][3].place(relx=0.99, rely=0.91, relwidth=0.3, relheight=0.08, anchor='se')
wdgt[tab_bat][4].place(relx=0.01, rely=0.91, relwidth=0.3, relheight=0.08, anchor='sw')
wdgt[tab_bat][5].place(relx=0.01, rely=0.99, relwidth=0.3, relheight=0.08, anchor='sw')
wdgt[tab_bat][0].place(relx=0.03, rely=0.03, relwidth=0.94, relheight=0.79, anchor='nw')
wdgt[tab_marking][0].place(relx=0.5, rely=1, relwidth=0.13, relheight=0.08, anchor='s')
wdgt[tab_marking][1].place(relx=1, rely=1, relwidth=0.13, relheight=0.08, anchor='se')
wdgt[tab_marking][2].place(relx=0, rely=1, relwidth=0.15, relheight=0.08, anchor='sw')
wdgt[tab_marking][2].current(0)
def marking_change_color(l):
global style
global marking_colormark
global mk_bg
global mk_fg
if id(l) not in marking_colormark.keys():
marking_colormark[id(l)] = 0
style = tb.Style()
style.configure(f"MarkingX{id(l)}.TButton", background=mk_bg[marking_colormark[id(l)]], foreground=mk_fg[marking_colormark[id(l)]])
style.map(f"MarkingX{id(l)}.TButton",
background=[('pressed', mk_bg[marking_colormark[id(l)]]), ('active', mk_bg[marking_colormark[id(l)]])],
foreground=[('pressed', mk_fg[marking_colormark[id(l)]]), ('active', mk_fg[marking_colormark[id(l)]])])
marking_colormark[id(l)] += 1
marking_colormark[id(l)]%=len(mk_fg)
# 应用样式
l.config(style=f"MarkingX{id(l)}.TButton")
global omember
tmember = list(omember)
for i in range((8 - len(member) % 8 )% 8):
tmember.append({"name":"占位符"})
for i, j in enumerate(tmember):
if len(j["name"]) == 2:
j["name"] = " " + j["name"] + " "
# 创建按钮并赋值给 l
l = ttk.Button(tab_marking, text=j["name"], style="Marking.TButton")
marking_buttons.append(l)
# 使用 partial 将 l 作为参数传递给回调函数
marking_change_color(l)
l.config(command=functools.partial(marking_change_color, l))
l.grid(column=i % 8, row=i // 8, padx=5, pady=5)
wdgt[tab_filter][0].current(0)
wdgt[tab_filter][0].grid(column=0,row=0)
wdgt[tab_filter][1].grid(column=0,row=1,sticky=tk.W,padx=5,pady=5)
wdgt[tab_filter][2].grid(column=0,row=2,sticky=tk.W,padx=5,pady=5)
wdgt[tab_filter][3].place(relx=0.01, rely=1, relwidth=1, relheight=0.08, anchor='sw')
wdgt[tab_filter][0].grid(column=0,row=0,sticky=tk.W,padx=5,pady=5)
wdgt[tab_filter][1].grid(column=0,row=1,sticky=tk.W,padx=5,pady=5)
wdgt[tab_cfg][0].grid(column=0, row=0, padx=5,pady=5,sticky=tk.W)
wdgt[tab_cfg][1].grid(column=0, row=3, padx=5,pady=5,sticky=tk.W)
wdgt[tab_cfg][2].grid(column=0, row=4, padx=5,pady=5,sticky=tk.W)
wdgt[tab_cfg][3].grid(column=0, row=1, padx=5,pady=5,sticky=tk.W)
wdgt[tab_cfg][4].grid(column=0, row=2, padx=5,pady=5,sticky=tk.W)
global is_first
if is_first:
global default_tab_layout
default_tab_layout = style.layout('TNotebook.Tab')
style.layout('TNotebook.Tab', [])
is_first = 0
root.protocol("WM_DELETE_WINDOW", on_closing)
update_data()
root.mainloop()
if __name__ == "__main__":
print("确保此终端支持 UTF-8 字符输出!")
print(f"幸运选手α {version}")
print("开放源代码: https://github.com/david-ajax/classaux")
print("读取 YAML 配置文件与 GPL 协议 ", end="")
with open("COPYING", encoding='utf_8') as license_file:
LICENSE = license_file.read()
with open("config.yaml", encoding='utf_8') as config_file:
config = yaml.safe_load(config_file.read())
print(config['member'])
omember = list(config['member'])
print(colorama.Fore.GREEN + "成功" + colorama.Style.RESET_ALL)
#print(config)
print(f"读取数据库 {config['info']['db']} ", end="")
print(colorama.Fore.GREEN + "成功" + colorama.Style.RESET_ALL)
with open("db.csv", encoding='utf_8-sig') as db_file:
db = list(csv.reader(db_file, delimiter=',', quotechar='"'))
for i in db:
if i[0] == 'NOTE':
db.remove(i)
# print(db)
print(f"配置文件: {config['info']['name']}")
member_sum = len(config['member'])
print(f"成员人数: {len(config['member'])}")
print(f"随机刻间隔时间: {stdtime}s")
print("启动随机生成器守护线程 ", end="")
random_thr = threading.Thread(target=random_gen)
random_thr.start()
print(colorama.Fore.GREEN + "成功" + colorama.Style.RESET_ALL)
print("初始化主题资源 ", end="")
is_dark = config["darkmode"]
init_theme()
print(colorama.Fore.GREEN + "成功" + colorama.Style.RESET_ALL)
print("初始化 GUI ", end="")
print(colorama.Fore.YELLOW + "正运行" + colorama.Style.RESET_ALL)
interface()
print(colorama.Fore.GREEN + "结束" + colorama.Style.RESET_ALL)