2.1
This commit is contained in:
387
main.py
Normal file
387
main.py
Normal 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)
|
Reference in New Issue
Block a user