From 1ff3ab675bc7f0705a3fc6aa40182b8320d36742 Mon Sep 17 00:00:00 2001 From: Wang Zhiyu Date: Tue, 23 Sep 2025 00:17:15 +0800 Subject: [PATCH] 2.2.0 --- main.py | 815 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 472 insertions(+), 343 deletions(-) diff --git a/main.py b/main.py index 978a437..7962cf3 100644 --- a/main.py +++ b/main.py @@ -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"] = "" - - -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 + + self.init_console() + self.load_config() + self.init_theme() + + def init_console(self): + """初始化控制台输出""" + print("确保此终端支持 UTF-8 字符输出!") + 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: + 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) + + # 加载数据库 + 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) + + 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 toggle(): # 仅用于单次抽取模式 - global is_paused - if not is_paused: - current_member["show"] = current_member["genuine"] - is_paused = 1 - wdgt[tab_electing][1].config(text="开始") + 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: - 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 + 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: - style.layout('TNotebook.Tab', []) - is_tab_shown = 0 + 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() - 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=["选择字体", "更纱黑体 (默认)", "系统默认字体"]), - ] - } + # 创建笔记本(选项卡) + self.create_notebook() - 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') + if self.default_tab_layout == None: + self.default_tab_layout = self.style.layout('TNotebook.Tab') - 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") + self.toggle_menu() - 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') + # 初始化各个选项卡 + 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='关于') - 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() + 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('<>', 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('', 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=self.random_gen) + random_thr.daemon = True + random_thr.start() + print(colorama.Fore.GREEN + "成功" + colorama.Style.RESET_ALL) + + print("初始化 GUI ", end="") + print(colorama.Fore.YELLOW + "正运行" + colorama.Style.RESET_ALL) + + self.create_interface() + print(colorama.Fore.GREEN + "结束" + colorama.Style.RESET_ALL) 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) + app = ClassAuxApp() + app.run()