基于Python与Tkinter开发测验程序:如何用txt读取题目、答案与解析,是否改用数组?
最简实现方案
核心认知澄清
不存在「txt存内容」和「数组存内容」二选一的问题。txt是用来离线维护题库的存储格式,程序启动时把txt内容读出来转换成内存里的列表(也就是你说的数组),既不用硬编码改题库,又能拿到数组遍历方便的好处,完全适配你的需求。
你原来想的三个txt的方案完全可行,只要解决「行号对齐」的问题就能跑,不用推翻重来。
第一步:给三个txt定固定格式,避免内容错位
如果三个txt随便写内容,很容易出现“第3题的题目匹配到第2题的解析”这种错位问题,只要按下面的规则写,就不会出问题:
题目.txt:一行对应一道题,不要把一道题拆成两行写答案集合.txt:一行对应同序号题目的所有选项,用固定分隔符(比如|)把单个选项隔开,行号和题目.txt严格一一对应解析.txt:一行对应同序号题目的解析内容,行号和题目.txt严格一一对应
格式示例参考:
# 题目.txt 内容 1. Python中以下哪个不是合法的变量名? 2. Tkinter中用于创建主窗口的类是? 3. 以下哪个布局方法支持自适应窗口缩放?
# 答案集合.txt 内容(和题目行严格对齐) A. score|B. 2_score|C. score2|D. _score A. Tk()|B. Frame()|C. Button()|D. Label() A. pack|B. grid|C. place|D. 以上都是
# 解析.txt 内容(和题目行严格对齐) Python变量名不能以数字开头,因此2_score是非法命名。 Tk()是生成程序主窗口的核心类,其余均为组件或容器。 pack、grid、place三种布局均支持窗口自适应调整。
第二步:写通用的题库读取逻辑
读文件时自动过滤空行,把三个文件的内容按行匹配成结构化的题目对象,加个简单的行数校验,避免错位:
def load_quiz(): # 读取题目 with open("题目.txt", "r", encoding="utf-8") as f: q_list = [line.strip() for line in f if line.strip()] # 读取选项,按|拆分单个选项 with open("答案集合.txt", "r", encoding="utf-8") as f: opt_list = [line.strip().split("|") for line in f if line.strip()] # 读取解析 with open("解析.txt", "r", encoding="utf-8") as f: exp_list = [line.strip() for line in f if line.strip()] # 校验三个文件行数一致 if not (len(q_list) == len(opt_list) == len(exp_list)): raise RuntimeError("三个题库文件行数不匹配,请检查内容对齐") # 组合成可遍历的题目列表(本质就是你说的数组结构) return [ {"content": q, "options": o, "explain": e} for q, o, e in zip(q_list, opt_list, exp_list) ]
第三步:Tkinter端的渲染逻辑
只要维护一个当前题目的索引变量,每次切题时先清空窗口上的旧组件,再渲染新的题目Label、选项Button,点击选项后再切到解析展示界面就行:
import tkinter as tk from tkinter import messagebox root = tk.Tk() root.geometry("700x500") # 加载题库 quiz = load_quiz() current_idx = 0 def render_question(): # 清空所有旧组件 for w in root.winfo_children(): w.destroy() q = quiz[current_idx] # 渲染题目为Label tk.Label( root, text=q["content"], wraplength=650, font=("微软雅黑", 14), pady=30 ).pack() # 遍历选项生成Button for opt in q["options"]: tk.Button( root, text=opt, width=45, font=("微软雅黑", 12), pady=8, command=lambda o=opt: show_explain(o, q) ).pack(pady=6) def show_explain(selected_opt, q): # 清空旧组件,展示解析 for w in root.winfo_children(): w.destroy() tk.Label( root, text=f"答案解析:{q['explain']}", wraplength=650, font=("微软雅黑", 12), pady=40 ).pack() # 下一题按钮 tk.Button(root, text="下一题", width=20, command=next_q).pack(pady=20) def next_q(): global current_idx current_idx +=1 if current_idx >= len(quiz): messagebox.showinfo("完成", "所有题目已作答完毕") root.destroy() return render_question() # 启动第一题 render_question() root.mainloop()
避坑提示
- 给题目、解析的Label一定要设置
wraplength参数,不然长文本会把窗口撑得超出屏幕 - 每次切换界面时必须先调用
winfo_children()清空旧组件,不然旧的按钮、标签会和新内容叠在一起 - 如果题库量大怕行错位,可以给每道题加固定ID标记,不用完全依赖行号匹配,容错率更高
没必要为了“用数组存储”就把所有题目硬编码写在代码里,txt离线存题库的方式改起来比翻代码方便太多。
内容的提问来源于stack exchange,提问作者Unko
相关产品推荐
相关产品推荐

