This commit is contained in:
2025-09-23 00:17:15 +08:00
parent 83da8e008a
commit 1ff3ab675b

809
main.py
View File

@@ -1,5 +1,5 @@
import tkinter as tk
from tkinter import ttk
from tkinter import ttk, messagebox, filedialog
import yaml
import colorama
import threading
@@ -7,36 +7,42 @@ import random
import time
import sys
import csv
import json
import functools
from PIL import Image
Image.CUBIC = Image.BICUBIC
#Image.CUBIC = Resampling.BICUBIC
import ttkbootstrap as tb
from datetime import datetime
import os
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}
class ClassAuxApp:
def __init__(self):
self.version = 'v2.7.0 Dev'
self.config = None
self.current_member = {"pause": None, "show": "", "genuine": ""}
self.member_sum = 0
self.stdtime = 0.1
self.random_stop = 0
self.LICENSE = None
self.member = None
self.is_paused = 1
self.is_tab_shown = 1
self.default_tab_layout = None
self.is_first = 1
self.db = None
self.marking_colormark = dict()
self.is_dark = None
self.mk_fg = []
self.mk_bg = []
self.btnbg = None
self.style = None
self.btnfg = None
self.ttkthemename = None
self.marking_buttons = []
self.omember = None
self.about_text = f"""CLASSAUX - 抽取软件 改进型
带轻量级数据库和随机功能的教学管理辅助程序 版本 {self.version}
版权所有 (C) 2025 Wang Zhiyu
本程序为共产版权的自由软件, 在自由软件联盟发布的GNU通用公共许可协议的约束下,
你可以对其进行再发布及修改。协议版本为第三版。
@@ -50,6 +56,7 @@ Colorama - Cross-platform colored terminal text
Tkinter - Python interface to Tcl/Tk
Pillow - Python Imaging Library
PyYaml - YAML parser and emitter for Python
TTkBootstrap - Bootstrap-themed widgets for Tkinter
Sarasa Gothic (更纱黑体)
--------------------------------------------------------------------------------
开源软件是一项立足于协作精神的事业,
@@ -58,330 +65,452 @@ Sarasa Gothic (更纱黑体)
开放源代码/报告程序缺陷: https://github.com/david-ajax/classaux
--------------------------------------------------------------------------------
"""
current_member["pause"] = None
current_member["show"] = ""
self.init_console()
self.load_config()
self.init_theme()
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__":
def init_console(self):
"""初始化控制台输出"""
print("确保此终端支持 UTF-8 字符输出!")
print(f"幸运选手α {version}")
print(f"抽取程序 {self.version}")
print("开放源代码: https://github.com/david-ajax/classaux")
def load_config(self):
"""加载配置文件"""
try:
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'])
with open("COPYING", encoding='utf-8') as license_file:
self.LICENSE = license_file.read()
with open("config.yaml", encoding='utf-8') as config_file:
self.config = yaml.safe_load(config_file)
self.omember = list(self.config['member'])
print(colorama.Fore.GREEN + "成功" + colorama.Style.RESET_ALL)
#print(config)
print(f"读取数据库 {config['info']['db']} ", end="")
# 加载数据库
db_file = self.config['info'].get('db', 'db.csv')
print(f"读取数据库 {db_file} ", end="")
self.load_database(db_file)
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(f"配置文件: {self.config['info']['name']}")
self.member_sum = len(self.config['member'])
print(f"成员人数: {self.member_sum}")
print(f"随机刻间隔时间: {self.stdtime}s")
except Exception as e:
print(colorama.Fore.RED + f"失败: {e}" + colorama.Style.RESET_ALL)
sys.exit(1)
def load_database(self, db_file):
"""加载数据库文件"""
try:
with open(db_file, encoding='utf-8-sig') as db_file:
self.db = list(csv.reader(db_file, delimiter=',', quotechar='"'))
# 过滤注释行
self.db = [row for row in self.db if row[0] != 'NOTE']
except FileNotFoundError:
print(colorama.Fore.YELLOW + "数据库文件不存在,创建空数据库" + colorama.Style.RESET_ALL)
self.db = []
def init_theme(self):
"""初始化主题设置"""
self.is_dark = self.config.get("darkmode", False)
if not self.is_dark:
self.mk_bg = ["#FFFFFF", "#009900", "#ff6666", "#0088ff", "#FFA500"]
self.mk_fg = ["#000000", "#FFFFFF", "#FFFFFF", "#FFFFFF", "#000000"]
self.ttkthemename = "cosmo"
self.btnbg = [('pressed', '#2097ff'), ('active', '#9bcbff')]
self.btnfg = [('pressed', 'white'), ('active', '#36393b')]
self.sbtnbg = 'white'
self.sbtnfg = '#36393b'
else:
self.mk_fg = ["#FFFFFF", "#00ff00", "#ff6666", "#9999ff", "#FFA500"]
self.mk_bg = ["#202020", "#005500", "#550000", "#000055", "#333300"]
self.ttkthemename = "darkly"
self.btnbg = [('pressed', '#242424'), ('active', '#242424')]
self.btnfg = [('pressed', '#FFFFFF'), ('active', '#dddddd')]
self.sbtnbg = "#202020"
self.sbtnfg = 'white'
def random_gen(self):
"""随机生成器线程函数"""
self.member = self.config['member']
while not self.random_stop:
random.shuffle(self.member)
for i in self.member:
if self.random_stop:
break
if i['weight'] > 0:
for k in range(i['weight']):
self.current_member["genuine"] = i['name']
time.sleep(self.stdtime)
def matrix_gen(self, format_type="linear"):
"""生成矩阵/列表视图"""
members = list(self.config["member"])
random.shuffle(members)
if format_type == "linear":
return "\n".join([f"{i+1}. {member['name']}" for i, member in enumerate(members)])
elif format_type == "ascii_table":
# 简单的ASCII表格
table = "序号 | 姓名\n" + "-" * 20 + "\n"
for i, member in enumerate(members):
table += f"{i+1:3} | {member['name']}\n"
return table
elif format_type == "csv":
return "序号,姓名\n" + "\n".join([f"{i+1},{member['name']}" for i, member in enumerate(members)])
elif format_type == "markdown":
return "| 序号 | 姓名 |\n|-----|------|\n" + "\n".join([f"| {i+1} | {member['name']} |" for i, member in enumerate(members)])
else:
return "\n".join([member['name'] for member in members])
def export_to_file(self, content, file_type="txt"):
"""导出内容到文件"""
try:
filename = filedialog.asksaveasfilename(
defaultextension=f".{file_type}",
filetypes=[(f"{file_type.upper()} files", f"*.{file_type}")]
)
if filename:
with open(filename, 'w', encoding='utf-8') as f:
f.write(content)
messagebox.showinfo("成功", f"文件已导出到: {filename}")
except Exception as e:
messagebox.showerror("错误", f"导出失败: {e}")
def copy_to_clipboard(self, content):
"""复制内容到剪贴板"""
try:
self.root.clipboard_clear()
self.root.clipboard_append(content)
messagebox.showinfo("成功", "内容已复制到剪贴板")
except Exception as e:
messagebox.showerror("错误", f"复制失败: {e}")
def marking_change_color(self, button):
"""标记视图中的颜色切换"""
if id(button) not in self.marking_colormark:
self.marking_colormark[id(button)] = 0
current_color = self.marking_colormark[id(button)]
next_color = (current_color + 1) % len(self.mk_bg)
self.marking_colormark[id(button)] = next_color
# 创建新的样式
style_name = f"MarkingX{id(button)}.TButton"
self.style.configure(style_name,
background=self.mk_bg[next_color],
foreground=self.mk_fg[next_color])
self.style.map(style_name,
background=[('pressed', self.mk_bg[next_color]),
('active', self.mk_bg[next_color])],
foreground=[('pressed', self.mk_fg[next_color]),
('active', self.mk_fg[next_color])])
button.config(style=style_name)
def marking_clear(self):
"""清空所有标记"""
self.marking_colormark.clear()
for button in self.marking_buttons:
self.marking_change_color(button)
def toggle(self):
"""切换抽取状态"""
if not self.is_paused:
self.current_member["show"] = self.current_member["genuine"]
self.is_paused = 1
self.electing_button.config(text="开始")
else:
self.electing_button.config(text="暂停")
self.is_paused = 0
def toggle_menu(self):
"""切换选项卡显示"""
if not self.is_tab_shown:
self.style.layout('TNotebook.Tab', self.default_tab_layout)
self.is_tab_shown = 1
else:
self.style.layout('TNotebook.Tab', [])
self.is_tab_shown = 0
def toggle_dark(self):
"""切换暗色模式"""
self.is_dark = not self.is_dark
self.config["darkmode"] = self.is_dark
self.init_theme()
self.style.theme_use(self.ttkthemename)
self.marking_clear()
self.is_tab_shown = not self.is_tab_shown
self.toggle_menu()
# 保存主题设置
self.save_config()
def save_config(self):
"""保存配置到文件"""
try:
with open("config.yaml", 'w', encoding='utf-8') as f:
yaml.dump(self.config, f, allow_unicode=True, default_flow_style=False)
except Exception as e:
print(f"保存配置失败: {e}")
def update_data(self):
"""更新显示数据"""
if not self.is_paused:
self.electing_label.config(text=random.choice(self.config['member'])["name"])
else:
self.electing_label.config(text=self.current_member["show"])
self.electing_label.after(int(1000 * self.stdtime * 0.75), self.update_data)
def create_interface(self):
"""创建主界面"""
self.root = tb.Window(themename=self.ttkthemename)
self.root.title(f"抽取程序 {self.version}")
self.root.geometry("900x700")
# 设置图标
try:
self.root.iconphoto(False, tk.PhotoImage(file='logo.png'))
except:
pass
# 创建样式
self.style = tb.Style()
self.setup_styles()
# 创建笔记本(选项卡)
self.create_notebook()
if self.default_tab_layout == None:
self.default_tab_layout = self.style.layout('TNotebook.Tab')
self.toggle_menu()
# 初始化各个选项卡
self.create_electing_tab()
self.create_marking_tab()
self.create_batch_tab()
self.create_about_tab()
self.create_edit_tab()
self.create_config_tab()
# 设置关闭事件
self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
# 启动数据更新
self.update_data()
self.root.mainloop()
def setup_styles(self):
"""设置样式"""
self.style.configure("Marking.TButton", background=self.sbtnbg, foreground=self.sbtnfg)
self.style.map("Marking.TButton", background=self.btnbg, foreground=self.btnfg)
self.style.configure("TabToggle.TButton", background=self.sbtnbg, foreground=self.sbtnfg)
self.style.map("TabToggle.TButton", background=self.btnbg, foreground=self.btnfg)
def create_notebook(self):
"""创建选项卡容器"""
self.notebook = ttk.Notebook(self.root)
self.notebook.pack(fill='both', expand=True, padx=10, pady=10)
# 创建各个选项卡框架
self.tab_electing = tk.Frame(self.notebook)
self.tab_marking = tk.Frame(self.notebook)
self.tab_batch = tk.Frame(self.notebook)
self.tab_edit = tk.Frame(self.notebook)
self.tab_about = tk.Frame(self.notebook)
self.tab_config = tk.Frame(self.notebook)
# 添加选项卡
self.notebook.add(self.tab_electing, text='单次抽签')
self.notebook.add(self.tab_marking, text='标记视图')
self.notebook.add(self.tab_batch, text='批量视图')
self.notebook.add(self.tab_edit, text='编辑数据库')
self.notebook.add(self.tab_config, text='设置')
self.notebook.add(self.tab_about, text='关于')
def create_electing_tab(self):
"""创建单次抽签选项卡"""
# 主显示标签
self.electing_label = ttk.Label(self.tab_electing, text="待抽取",
font=("Sarasa UI SC", 70, 'bold'))
self.electing_label.place(relx=0.5, rely=0.35, anchor='center')
# 控制按钮
self.electing_button = ttk.Button(self.tab_electing, text="开始",
command=self.toggle)
self.electing_button.place(relx=0.5, rely=0.8, relwidth=0.25,
relheight=0.15, anchor='center')
# 辅助按钮
ttk.Button(self.tab_electing, text="选项卡", command=self.toggle_menu,
style="TabToggle.TButton").place(relx=0.01, rely=0.99,
relwidth=0.12, relheight=0.08, anchor='sw')
ttk.Button(self.tab_electing, text="暗色调", command=self.toggle_dark,
style="TabToggle.TButton").place(relx=0.99, rely=0.99,
relwidth=0.12, relheight=0.08, anchor='se')
def create_marking_tab(self):
"""创建标记视图选项卡"""
# 控制按钮
ttk.Button(self.tab_marking, text="清空", command=self.marking_clear
).place(relx=0.5, rely=0.99, relwidth=0.13,
relheight=0.08, anchor='s')
# 标记按钮网格
self.create_marking_grid()
def create_marking_grid(self):
"""创建标记网格"""
members = list(self.omember)
# 补全到8的倍数
for i in range((8 - len(members) % 8) % 8):
members.append({"name": "占位符"})
for i, member in enumerate(members):
# 创建标记按钮
btn_text = f" {member['name']} " if len(member["name"]) == 2 else member["name"]
button = ttk.Button(self.tab_marking, text=btn_text,
style="Marking.TButton")
# 绑定点击事件
button.config(command=functools.partial(self.marking_change_color, button))
button.grid(column=i % 8, row=i // 8, padx=5, pady=5, sticky='nsew')
# 配置网格权重
self.tab_marking.grid_columnconfigure(i % 8, weight=1)
self.tab_marking.grid_rowconfigure(i // 8, weight=1)
self.marking_buttons.append(button)
self.marking_change_color(button) # 初始化颜色
def create_batch_tab(self):
"""创建批量视图选项卡"""
# 结果显示文本框
self.batch_text = tk.Text(self.tab_batch, wrap=tk.WORD, font=("Sarasa UI SC", 10))
self.batch_text.place(relx=0.03, rely=0.03, relwidth=0.94, relheight=0.79)
# 格式选择
self.format_var = tk.StringVar(value="linear")
format_combo = ttk.Combobox(self.tab_batch, textvariable=self.format_var,
values=("linear", "ascii_table", "csv", "markdown"))
format_combo.place(relx=0.01, rely=0.91, relwidth=0.3, relheight=0.08, anchor='sw')
format_combo.set("linear")
# 生成按钮
ttk.Button(self.tab_batch, text="生成新随机",
command=self.generate_batch).place(relx=0.5, rely=0.845,
relwidth=0.25, relheight=0.13, anchor='n')
# 操作按钮
ttk.Button(self.tab_batch, text="复制到剪贴板",
command=lambda: self.copy_to_clipboard(self.batch_text.get(1.0, tk.END)),
style="TabToggle.TButton").place(relx=0.99, rely=0.99,
relwidth=0.3, relheight=0.08, anchor='se')
ttk.Button(self.tab_batch, text="导出至文件",
command=lambda: self.export_to_file(self.batch_text.get(1.0, tk.END), "txt"),
style="TabToggle.TButton").place(relx=0.99, rely=0.91,
relwidth=0.3, relheight=0.08, anchor='se')
# 初始生成内容
self.generate_batch()
def generate_batch(self):
"""生成批量视图内容"""
content = self.matrix_gen(self.format_var.get())
self.batch_text.delete(1.0, tk.END)
self.batch_text.insert(1.0, content)
def create_edit_tab(self):
"""创建编辑数据库选项卡"""
# 简单的编辑界面
label = ttk.Label(self.tab_edit, text="数据库编辑功能开发中...",
font=("Sarasa UI SC", 16))
label.pack(expand=True)
# 显示当前数据库信息
info_text = f"当前数据库记录数: {len(self.db)}"
ttk.Label(self.tab_edit, text=info_text).pack(pady=10)
def create_config_tab(self):
"""创建设置选项卡"""
# 主题设置
ttk.Label(self.tab_config, text="外观设置",
font=("Sarasa UI SC", 14, 'bold')).grid(row=0, column=0,
sticky='w', padx=5, pady=5)
self.theme_var = tk.StringVar(value=self.ttkthemename)
theme_combo = ttk.Combobox(self.tab_config, textvariable=self.theme_var,
values=["cosmo", "flatly", "darkly", "litera", "minty"])
theme_combo.grid(row=1, column=0, sticky='w', padx=5, pady=5)
theme_combo.bind('<<ComboboxSelected>>', self.change_theme)
# 其他设置
ttk.Label(self.tab_config, text="其他设置",
font=("Sarasa UI SC", 14, 'bold')).grid(row=2, column=0,
sticky='w', padx=5, pady=5)
self.speed_var = tk.DoubleVar(value=self.stdtime)
speed_scale = ttk.Scale(self.tab_config, from_=0.05, to=0.5,
variable=self.speed_var, orient='horizontal')
speed_scale.grid(row=3, column=0, sticky='we', padx=5, pady=5)
speed_scale.bind('<ButtonRelease-1>', self.change_speed)
ttk.Label(self.tab_config, text=f"速度: {self.stdtime}s").grid(row=4, column=0, sticky='w', padx=5)
def change_theme(self, event):
"""更改主题"""
self.ttkthemename = self.theme_var.get()
self.style.theme_use(self.ttkthemename)
def change_speed(self, event):
"""更改速度"""
self.stdtime = self.speed_var.get()
# 更新显示
for widget in self.tab_config.grid_slaves():
if isinstance(widget, ttk.Label) and widget.cget('text').startswith("速度:"):
widget.config(text=f"速度: {self.stdtime}s")
def create_about_tab(self):
"""创建关于选项卡"""
about_text = tk.Text(self.tab_about, wrap=tk.WORD,
font=("Sarasa UI SC", 10))
about_text.pack(fill='both', expand=True, padx=10, pady=10)
about_text.insert(tk.END, self.about_text)
about_text.insert(tk.END, "\n\n" + "="*50 + "\n")
about_text.insert(tk.END, "GNU GENERAL PUBLIC LICENSE\n")
about_text.insert(tk.END, "="*50 + "\n")
about_text.insert(tk.END, self.LICENSE[:2000] + "...") # 只显示部分许可证内容
about_text.configure(state="disabled")
def on_closing(self):
"""程序关闭处理"""
self.random_stop = 1
self.root.destroy()
def run(self):
"""运行程序"""
print("启动随机生成器守护线程 ", end="")
random_thr = threading.Thread(target=random_gen)
random_thr = threading.Thread(target=self.random_gen)
random_thr.daemon = True
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()
self.create_interface()
print(colorama.Fore.GREEN + "结束" + colorama.Style.RESET_ALL)
if __name__ == "__main__":
app = ClassAuxApp()
app.run()