镜像自地址
https://github.com/a13zhy/a13rollcall.git
已同步 2026-10-20 19:37:47 +08:00
351 行
15 KiB
Python
351 行
15 KiB
Python
import os
|
||
import sys
|
||
import subprocess
|
||
import configparser
|
||
import tkinter as tk
|
||
from tkinter import ttk, messagebox
|
||
from tkinter.font import Font
|
||
|
||
APP_EXE = "A13课堂点名系统.exe"
|
||
WIZARD_INI = "wizard_record.ini"
|
||
STUDENT_FILE = "students.txt"
|
||
TEXT_FILE = "texts.txt"
|
||
MAIN_PY = "main_qt.py"
|
||
|
||
if getattr(sys, 'frozen', False):
|
||
BASE_DIR = os.path.dirname(sys.executable)
|
||
else:
|
||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||
|
||
INI_PATH = os.path.join(BASE_DIR, WIZARD_INI)
|
||
STUDENT_PATH = os.path.join(BASE_DIR, STUDENT_FILE)
|
||
TEXT_PATH = os.path.join(BASE_DIR, TEXT_FILE)
|
||
|
||
|
||
class WizardApp:
|
||
def __init__(self, root):
|
||
self.root = root
|
||
self.root.title("A13 课堂点名系统 - 配置向导")
|
||
self.root.geometry("640x820")
|
||
self.root.resizable(False, False)
|
||
self.root.configure(bg="#f3f3f3")
|
||
try:
|
||
self.root.iconbitmap(os.path.join(BASE_DIR, "app.ico"))
|
||
except Exception:
|
||
pass
|
||
|
||
self.current_step = 0
|
||
self.class_name = tk.StringVar(value="A13")
|
||
self.steps = [
|
||
self._page_welcome,
|
||
self._page_class,
|
||
self._page_students,
|
||
self._page_texts,
|
||
self._page_finish,
|
||
]
|
||
|
||
self._setup_style()
|
||
self._build_ui()
|
||
self._show_step()
|
||
|
||
def _setup_style(self):
|
||
style = ttk.Style()
|
||
style.theme_use("clam")
|
||
style.configure("Wiz.TFrame", background="#f3f3f3")
|
||
style.configure("Title.TLabel", font=("微软雅黑", 20, "bold"), foreground="#0067c0", background="#f3f3f3")
|
||
style.configure("Subtitle.TLabel", font=("微软雅黑", 11), foreground="#666666", background="#f3f3f3")
|
||
style.configure("Content.TLabel", font=("微软雅黑", 11), foreground="#333333", background="#f3f3f3")
|
||
style.configure("Card.TFrame", background="#ffffff", relief="solid", borderwidth=1)
|
||
style.configure("Primary.TButton", font=("微软雅黑", 11, "bold"), padding=10)
|
||
style.configure("Nav.TButton", font=("微软雅黑", 10), padding=8)
|
||
|
||
def _build_ui(self):
|
||
main = ttk.Frame(self.root, style="Wiz.TFrame")
|
||
main.pack(fill="both", expand=True, padx=0, pady=0)
|
||
|
||
header = tk.Frame(main, bg="#0067c0", height=60)
|
||
header.pack(fill="x")
|
||
header.pack_propagate(False)
|
||
header_title = tk.Label(header, text="A13 课堂点名系统", font=("微软雅黑", 16, "bold"), fg="white", bg="#0067c0")
|
||
header_title.pack(side="left", padx=24, pady=14)
|
||
self.step_label = tk.Label(header, text="", font=("微软雅黑", 11), fg="#cce8ff", bg="#0067c0")
|
||
self.step_label.pack(side="right", padx=24, pady=18)
|
||
|
||
self.content = tk.Frame(main, bg="#f3f3f3")
|
||
self.content.pack(fill="both", expand=True, padx=32, pady=24)
|
||
|
||
nav = tk.Frame(main, bg="#f3f3f3", height=60)
|
||
nav.pack(fill="x", side="bottom")
|
||
nav.pack_propagate(False)
|
||
self.prev_btn = tk.Button(nav, text="上一步", font=("微软雅黑", 10), bg="#ffffff", fg="#333333", relief="solid", bd=1, padx=20, pady=6, command=self._prev)
|
||
self.prev_btn.pack(side="left", padx=24, pady=12)
|
||
self.next_btn = tk.Button(nav, text="下一步", font=("微软雅黑", 10, "bold"), bg="#0067c0", fg="white", relief="flat", padx=24, pady=6, command=self._next)
|
||
self.next_btn.pack(side="right", padx=24, pady=12)
|
||
|
||
def _clear_content(self):
|
||
for w in self.content.winfo_children():
|
||
w.destroy()
|
||
|
||
def _show_step(self):
|
||
self._clear_content()
|
||
self.step_label.config(text=f"第 {self.current_step + 1} / {len(self.steps)} 步")
|
||
self.steps[self.current_step]()
|
||
self.prev_btn.config(state="normal" if self.current_step > 0 else "disabled")
|
||
if self.current_step == len(self.steps) - 1:
|
||
self.next_btn.config(text="完成")
|
||
else:
|
||
self.next_btn.config(text="下一步")
|
||
|
||
def _page_welcome(self):
|
||
title = tk.Label(self.content, text="欢迎使用!", font=("微软雅黑", 22, "bold"), fg="#0067c0", bg="#f3f3f3")
|
||
title.pack(pady=(20, 8))
|
||
subtitle = tk.Label(self.content, text="A13 智能抽取引擎 V6.8", font=("微软雅黑", 12), fg="#666666", bg="#f3f3f3")
|
||
subtitle.pack(pady=(0, 24))
|
||
|
||
card = tk.Frame(self.content, bg="white", relief="solid", bd=1)
|
||
card.pack(fill="x", padx=20, pady=10)
|
||
card_inner = tk.Frame(card, bg="white")
|
||
card_inner.pack(fill="x", padx=20, pady=16)
|
||
|
||
features = [
|
||
("🎲", "智能随机抽取", "支持自动/手动模式,不重复抽取"),
|
||
("📖", "课文联动", "抽取学生同时展示对应课文"),
|
||
("📊", "积分统计", "自动记录背诵状态,生成排行榜"),
|
||
("💬", "回声洞", "匿名留言功能,倾听每一个心声"),
|
||
]
|
||
for icon, title, desc in features:
|
||
row = tk.Frame(card_inner, bg="white")
|
||
row.pack(fill="x", pady=6)
|
||
icon_label = tk.Label(row, text=icon, font=("微软雅黑", 18), bg="white")
|
||
icon_label.pack(side="left", padx=(0, 12))
|
||
text_col = tk.Frame(row, bg="white")
|
||
text_col.pack(side="left", fill="x", expand=True)
|
||
t_label = tk.Label(text_col, text=title, font=("微软雅黑", 11, "bold"), fg="#333333", bg="white", anchor="w")
|
||
t_label.pack(fill="x")
|
||
d_label = tk.Label(text_col, text=desc, font=("微软雅黑", 10), fg="#888888", bg="white", anchor="w")
|
||
d_label.pack(fill="x")
|
||
|
||
tip = tk.Label(self.content, text="点击「下一步」开始配置你的班级点名系统", font=("微软雅黑", 10), fg="#999999", bg="#f3f3f3")
|
||
tip.pack(pady=20)
|
||
|
||
def _page_class(self):
|
||
title = tk.Label(self.content, text="设置班级名称", font=("微软雅黑", 20, "bold"), fg="#0067c0", bg="#f3f3f3")
|
||
title.pack(pady=(20, 8))
|
||
subtitle = tk.Label(self.content, text="窗口标题将显示为「某某班点名程序」", font=("微软雅黑", 11), fg="#666666", bg="#f3f3f3")
|
||
subtitle.pack(pady=(0, 24))
|
||
|
||
card = tk.Frame(self.content, bg="white", relief="solid", bd=1)
|
||
card.pack(fill="x", padx=40, pady=10)
|
||
card_inner = tk.Frame(card, bg="white")
|
||
card_inner.pack(fill="x", padx=24, pady=24)
|
||
|
||
label = tk.Label(card_inner, text="班级名称:", font=("微软雅黑", 12), fg="#333333", bg="white")
|
||
label.pack(anchor="w", pady=(0, 8))
|
||
entry = tk.Entry(card_inner, textvariable=self.class_name, font=("微软雅黑", 14), relief="solid", bd=1)
|
||
entry.pack(fill="x", ipady=6)
|
||
entry.focus_set()
|
||
|
||
preview = tk.Label(card_inner, text="", font=("微软雅黑", 11), fg="#0067c0", bg="white")
|
||
preview.pack(anchor="w", pady=(12, 0))
|
||
|
||
def update_preview(*args):
|
||
name = self.class_name.get().strip() or "A13"
|
||
preview.config(text=f"预览:{name}班点名程序")
|
||
|
||
self.class_name.trace_add("write", update_preview)
|
||
update_preview()
|
||
|
||
tip = tk.Label(self.content, text="提示:可以在设置中随时修改班级名称", font=("微软雅黑", 10), fg="#999999", bg="#f3f3f3")
|
||
tip.pack(pady=20)
|
||
|
||
def _page_students(self):
|
||
title = tk.Label(self.content, text="学生名单", font=("微软雅黑", 20, "bold"), fg="#0067c0", bg="#f3f3f3")
|
||
title.pack(pady=(20, 8))
|
||
subtitle = tk.Label(self.content, text="配置学生名单文件格式", font=("微软雅黑", 11), fg="#666666", bg="#f3f3f3")
|
||
subtitle.pack(pady=(0, 20))
|
||
|
||
card = tk.Frame(self.content, bg="white", relief="solid", bd=1)
|
||
card.pack(fill="both", expand=True, padx=20, pady=10)
|
||
card_inner = tk.Frame(card, bg="white")
|
||
card_inner.pack(fill="both", expand=True, padx=20, pady=16)
|
||
|
||
format_label = tk.Label(card_inner, text="文件格式:students.txt", font=("微软雅黑", 12, "bold"), fg="#333333", bg="white")
|
||
format_label.pack(anchor="w", pady=(0, 8))
|
||
|
||
example = tk.Text(card_inner, height=6, font=("Consolas", 11), relief="solid", bd=1, bg="#fafafa")
|
||
example.pack(fill="x", pady=(0, 12))
|
||
example.insert("1.0", "张三\n李四\n王五\n赵六\n...")
|
||
example.config(state="disabled")
|
||
|
||
tips = [
|
||
"• 每行一个学生姓名",
|
||
"• 支持带序号格式(如「1 张三」)",
|
||
"• 文件编码请使用 UTF-8",
|
||
"• 点击下方按钮可用记事本编辑",
|
||
]
|
||
for tip in tips:
|
||
t = tk.Label(card_inner, text=tip, font=("微软雅黑", 10), fg="#666666", bg="white", anchor="w")
|
||
t.pack(fill="x", pady=2)
|
||
|
||
btn_row = tk.Frame(card_inner, bg="white")
|
||
btn_row.pack(fill="x", pady=(12, 0))
|
||
edit_btn = tk.Button(btn_row, text="📝 编辑学生名单", font=("微软雅黑", 10), bg="#0067c0", fg="white", relief="flat", padx=16, pady=6, command=lambda: self._edit_file(STUDENT_PATH))
|
||
edit_btn.pack(side="left")
|
||
|
||
def _page_texts(self):
|
||
title = tk.Label(self.content, text="课文库", font=("微软雅黑", 20, "bold"), fg="#0067c0", bg="#f3f3f3")
|
||
title.pack(pady=(20, 8))
|
||
subtitle = tk.Label(self.content, text="配置课文库文件格式", font=("微软雅黑", 11), fg="#666666", bg="#f3f3f3")
|
||
subtitle.pack(pady=(0, 20))
|
||
|
||
card = tk.Frame(self.content, bg="white", relief="solid", bd=1)
|
||
card.pack(fill="both", expand=True, padx=20, pady=10)
|
||
card_inner = tk.Frame(card, bg="white")
|
||
card_inner.pack(fill="both", expand=True, padx=20, pady=16)
|
||
|
||
format_label = tk.Label(card_inner, text="文件格式:texts.txt", font=("微软雅黑", 12, "bold"), fg="#333333", bg="white")
|
||
format_label.pack(anchor="w", pady=(0, 8))
|
||
|
||
example = tk.Text(card_inner, height=6, font=("Consolas", 11), relief="solid", bd=1, bg="#fafafa")
|
||
example.pack(fill="x", pady=(0, 12))
|
||
example.insert("1.0", "【第一课 春】\n春天来了,万物复苏...\n\n【第二课 夏】\n夏天到了,烈日炎炎...")
|
||
example.config(state="disabled")
|
||
|
||
tips = [
|
||
"• 用【】包裹课文标题",
|
||
"• 标题下方为课文内容,空行分隔段落",
|
||
"• 支持多篇课文,抽取时自动联动",
|
||
"• 点击下方按钮可用记事本编辑",
|
||
]
|
||
for tip in tips:
|
||
t = tk.Label(card_inner, text=tip, font=("微软雅黑", 10), fg="#666666", bg="white", anchor="w")
|
||
t.pack(fill="x", pady=2)
|
||
|
||
btn_row = tk.Frame(card_inner, bg="white")
|
||
btn_row.pack(fill="x", pady=(12, 0))
|
||
edit_btn = tk.Button(btn_row, text="📝 编辑课文库", font=("微软雅黑", 10), bg="#0067c0", fg="white", relief="flat", padx=16, pady=6, command=lambda: self._edit_file(TEXT_PATH))
|
||
edit_btn.pack(side="left")
|
||
|
||
def _page_finish(self):
|
||
title = tk.Label(self.content, text="配置完成!", font=("微软雅黑", 22, "bold"), fg="#107c10", bg="#f3f3f3")
|
||
title.pack(pady=(30, 8))
|
||
|
||
class_name = self.class_name.get().strip() or "A13"
|
||
subtitle = tk.Label(self.content, text=f"{class_name}班点名程序已准备就绪", font=("微软雅黑", 12), fg="#666666", bg="#f3f3f3")
|
||
subtitle.pack(pady=(0, 24))
|
||
|
||
card = tk.Frame(self.content, bg="white", relief="solid", bd=1)
|
||
card.pack(fill="x", padx=40, pady=10)
|
||
card_inner = tk.Frame(card, bg="white")
|
||
card_inner.pack(fill="x", padx=24, pady=20)
|
||
|
||
summary = [
|
||
("班级名称", class_name),
|
||
("学生名单", STUDENT_FILE),
|
||
("课文库", TEXT_FILE),
|
||
("引擎版本", "A13 V6.8"),
|
||
]
|
||
for label, value in summary:
|
||
row = tk.Frame(card_inner, bg="white")
|
||
row.pack(fill="x", pady=4)
|
||
l = tk.Label(row, text=label + ":", font=("微软雅黑", 11), fg="#888888", bg="white", width=10, anchor="e")
|
||
l.pack(side="left")
|
||
v = tk.Label(row, text=value, font=("微软雅黑", 11, "bold"), fg="#333333", bg="white")
|
||
v.pack(side="left", padx=(8, 0))
|
||
|
||
tip = tk.Label(self.content, text="点击「完成」启动点名程序,首次启动将显示操作引导", font=("微软雅黑", 10), fg="#999999", bg="#f3f3f3")
|
||
tip.pack(pady=20)
|
||
|
||
def _edit_file(self, filepath):
|
||
if not os.path.exists(filepath):
|
||
try:
|
||
with open(filepath, "w", encoding="utf-8") as f:
|
||
f.write("")
|
||
except Exception:
|
||
pass
|
||
try:
|
||
subprocess.Popen(["notepad.exe", filepath])
|
||
except Exception as e:
|
||
messagebox.showerror("错误", f"无法打开记事本:{e}")
|
||
|
||
def _prev(self):
|
||
if self.current_step > 0:
|
||
self.current_step -= 1
|
||
self._show_step()
|
||
|
||
def _next(self):
|
||
if self.current_step < len(self.steps) - 1:
|
||
self.current_step += 1
|
||
self._show_step()
|
||
else:
|
||
self._finish()
|
||
|
||
def _finish(self):
|
||
class_name = self.class_name.get().strip() or "A13"
|
||
|
||
try:
|
||
cfg = configparser.ConfigParser()
|
||
cfg["status"] = {"finished": "True"}
|
||
cfg["app"] = {"class_name": class_name}
|
||
with open(INI_PATH, "w", encoding="utf-8") as f:
|
||
cfg.write(f)
|
||
except Exception as e:
|
||
messagebox.showerror("错误", f"写入配置失败:{e}")
|
||
return
|
||
|
||
try:
|
||
config_path = os.path.join(BASE_DIR, "config.json")
|
||
import json
|
||
config = {}
|
||
if os.path.exists(config_path):
|
||
try:
|
||
with open(config_path, "r", encoding="utf-8") as f:
|
||
config = json.load(f)
|
||
except Exception:
|
||
pass
|
||
if "app" not in config:
|
||
config["app"] = {}
|
||
config["app"]["class_name"] = class_name
|
||
with open(config_path, "w", encoding="utf-8") as f:
|
||
json.dump(config, f, ensure_ascii=False, indent=2)
|
||
except Exception:
|
||
pass
|
||
|
||
self.root.destroy()
|
||
|
||
exe_path = os.path.join(BASE_DIR, APP_EXE)
|
||
py_path = os.path.join(BASE_DIR, MAIN_PY)
|
||
try:
|
||
if os.path.exists(exe_path):
|
||
subprocess.Popen([exe_path, "--show-tip"], cwd=BASE_DIR)
|
||
elif os.path.exists(py_path):
|
||
subprocess.Popen([sys.executable, py_path, "--show-tip"], cwd=BASE_DIR)
|
||
except Exception as e:
|
||
print(f"启动主程序失败: {e}")
|
||
|
||
|
||
def generate_sample_files():
|
||
if not os.path.exists(STUDENT_PATH):
|
||
try:
|
||
with open(STUDENT_PATH, "w", encoding="utf-8") as f:
|
||
f.write("张三\n李四\n王五\n赵六\n钱七\n孙八\n周九\n吴十\n")
|
||
except Exception:
|
||
pass
|
||
if not os.path.exists(TEXT_PATH):
|
||
try:
|
||
with open(TEXT_PATH, "w", encoding="utf-8") as f:
|
||
f.write("【第一课 春】\n春天来了,万物复苏。\n小草从土里探出头来,\n花儿也露出了笑脸。\n\n【第二课 夏】\n夏天到了,烈日炎炎。\n知了在树上不停地叫着,\n小朋友们在河边玩耍。\n\n【第三课 秋】\n秋天来了,树叶变黄。\n一片片叶子从树上落下,\n像一只只蝴蝶在飞舞。\n")
|
||
except Exception:
|
||
pass
|
||
|
||
|
||
def main():
|
||
generate_sample_files()
|
||
root = tk.Tk()
|
||
app = WizardApp(root)
|
||
root.mainloop()
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|