This commit is contained in:
2025-04-02 23:24:14 +08:00
commit 83da8e008a
3 changed files with 1061 additions and 0 deletions

387
main.py Normal file
View File

@@ -0,0 +1,387 @@
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)