背景:
最近想要刷题,虽然目前有很多成熟的软件,网站。但是能够支持自定义的导入题库的非常少,或者是要么让你开会员,而直接百度题库的话,正确答案就摆在你一眼能看见的地方,看的时候总觉得自己也会,然后考的时候又啥都不记得了,然后最重要的一点是自定义导入,否则现如今成熟的牛客力扣啥才是最优的选择
自定义题库要素A
这个是百度的时候发现的
1 题号
2 问题题干
3 类型(单选,多选,文本)
4 选项(单选多选的选项,实际上判断题也可以归为2个选项的单选题)5 参考答案
6 答案详解,开发完成后加入的,感觉光是答案不太好
7 上次答案记录。后续加入的,你连上次做的对的错的都不知道的话,刷题的意义性何在?
8 标签:也是后续加上的,本来是打算数据多了做一个条件查询的,但是发现python的查询太丑了,可能是我打开方式不对,因此我想了一个办法就是把问题标签化,你可以通过标签来做筛选,因此你可以只看某部分标签内的题
代码:
代码结构如下:注意python的名称好像不太重要可以随意改
ExerciseDemo1.py questions.json QuestionsJsonGenerator.py 题库文件夹 里面可以放你自己的各种文档,当然你可以放在一个xlsx文件里面也行,但是数据多了好不好用我就不负责了
ExerciseDemo1.py
import json
import tkinter as tk
from tkinter import ttk
# 从JSON文件中加载题目
with open('questions.json', 'r', encoding='utf-8') as file:
data = json.load(file)
questions = data['questions']
# 获取所有标签并生成唯一标签列表
tags_set = set()
for question in questions:
tags_set.update(question['tags'])
tags_list = list(tags_set)
# 创建主窗口
root = tk.Tk()
root.title("刷题软件")
root.geometry('1200x800')
current_question_index = 0
# 切换题目
def load_question(index):
global current_question_index
current_question_index = index
question_data = questions[current_question_index]
question_label.config(text=f"{question_data['id']}. {question_data['question']}")
# 清空当前答案框架
for widget in answer_frame.winfo_children():
widget.destroy()
global answer_var
answer_var = None
if question_data["type"] == "single":
answer_var = tk.StringVar()
for key, value in question_data["options"].items():
rb = tk.Radiobutton(answer_frame, text=value, variable=answer_var, value=key)
rb.pack(anchor='w')
elif question_data["type"] == "multiple":
answer_var = {}
for key, value in question_data["options"].items():
var = tk.BooleanVar()
answer_var[key] = var
cb = tk.Checkbutton(answer_frame, text=value, variable=var)
cb.pack(anchor='w')
elif question_data["type"] == "text":
answer_var = tk.Text(answer_frame, height=5, wrap="word")
answer_var.pack(fill='both', expand=True)
# 清空答案显示区域并滚动至顶部
answer_display.delete("1.0", tk.END)
answer_display.yview_moveto(0)
# 显示答案
def show_answer():
question = questions[current_question_index]
answer = question['answer']
explanation = question['explanation']
last_answer = question.get('lastAnswer', None)
answer_display.delete('1.0', tk.END)
if last_answer:
answer_display.insert(tk.END, f"答案:\n{answer}\n\n你的上次答案:\n{last_answer}\n\n解释:\n{explanation}")
else:
answer_display.insert(tk.END, f"答案:\n{answer}\n\n解释:\n{explanation}")
answer_display.yview_moveto(0)
# 隐藏答案
def hide_answer():
answer_display.delete('1.0', tk.END)
# 保存答案
def save_answer():
question = questions[current_question_index]
if question["type"] == "single":
question['lastAnswer'] = answer_var.get()
elif question["type"] == "multiple":
question['lastAnswer'] = [key for key, var in answer_var.items() if var.get()]
elif question["type"] == "text":
question['lastAnswer'] = answer_var.get("1.0", tk.END).strip()
with open('questions.json', 'w', encoding='utf-8') as file:
json.dump({'questions': questions}, file, indent=2, ensure_ascii=False)
# 更新标题下拉列表
def update_question_selector(tag=None):
if tag:
filtered_titles = [f"{q['id']}. {q['question']}" for q in questions if tag in q['tags']]
else:
filtered_titles = [f"{q['id']}. {q['question']}" for q in questions]
question_selector['values'] = filtered_titles
if filtered_titles:
question_selector.current(0)
load_question(0)
# 清除标签选择
def clear_tag_selection():
tag_selector.set('')
update_question_selector()
# 标签下拉列表选项
tag_frame = tk.Frame(root)
tag_frame.pack(pady=10, anchor='w')
tag_selector = ttk.Combobox(tag_frame, values=tags_list, width=50, state='readonly')
tag_selector.grid(row=0, column=0, padx=5)
tag_selector.bind("<<ComboboxSelected>>", lambda event: update_question_selector(tag_selector.get()))
clear_tag_button = tk.Button(tag_frame, text="清除标签选择", command=clear_tag_selection)
clear_tag_button.grid(row=0, column=1, padx=5)
# 标题下拉列表选项
question_titles = [f"{q['id']}. {q['question']}" for q in questions]
question_selector = ttk.Combobox(root, values=question_titles, width=100, state='readonly')
question_selector.current(0)
question_selector.pack(pady=10, anchor='w', fill=tk.X)
question_selector.bind("<<ComboboxSelected>>", lambda event: load_question(question_selector.current()))
# 功能按钮
button_frame = tk.Frame(root)
button_frame.pack(pady=10, anchor='w')
previous_button = tk.Button(button_frame, text="上一题", command=lambda: load_question((current_question_index - 1) % len(questions)))
previous_button.grid(row=0, column=0, padx=5)
next_button = tk.Button(button_frame, text="下一题", command=lambda: load_question((current_question_index + 1) % len(questions)))
next_button.grid(row=0, column=1, padx=5)
# 题干
question_label = tk.Label(root, text="", wraplength=600, justify="left")
question_label.pack(pady=10, anchor='w')
# 答案框架
answer_frame = tk.Frame(root)
answer_frame.pack(pady=10, fill='both', expand=True, anchor='w')
# 显示答案和隐藏答案按钮框架
show_hide_button_frame = tk.Frame(root)
show_hide_button_frame.pack(pady=5, anchor='w')
save_answer_button = tk.Button(show_hide_button_frame, text="保存答案", command=save_answer)
save_answer_button.grid(row=0, column=0, padx=5)
show_answer_button = tk.Button(show_hide_button_frame, text="显示答案", command=show_answer)
show_answer_button.grid(row=0, column=1, padx=5)
hide_answer_button = tk.Button(show_hide_button_frame, text="隐藏答案", command=hide_answer)
hide_answer_button.grid(row=0, column=2, padx=5)
# 显示答案区域框架
answer_display_frame = tk.Frame(root)
answer_display_frame.pack(pady=10, fill='both', expand=True)
answer_display_scroll = tk.Scrollbar(answer_display_frame)
answer_display_scroll.pack(side=tk.RIGHT, fill=tk.Y)
answer_display = tk.Text(answer_display_frame, height=4, yscrollcommand=answer_display_scroll.set)
answer_display.pack(fill='both', expand=True)
answer_display_scroll.config(command=answer_display.yview)
# 初始化加载第一题
load_question(0)
# 运行主循环
root.mainloop()
questions.json
这个就不举例了,实际上是通过xlsx文件执行QuestionsJsonGenerator自动生成的。
QuestionsJsonGenerator.py
import json
import openpyxl
import os
import re
class QuestionsJsonGenerator:
def __init__(self, excel_file, json_file):
self.excel_file = excel_file
self.json_file = json_file
self.questions = []
def read_excel(self):
workbook = openpyxl.load_workbook(self.excel_file)
sheet = workbook.active
rows = list(sheet.iter_rows(values_only=True))
headers = rows[0]
for row in rows[1:]:
question_data = {}
for header, value in zip(headers, row):
if header == "标签":
question_data['tags'] = [tag for tag in re.split(r'[,,]', value) if tag.strip()]
elif header == "类型":
question_data['type'] = value
elif header == "问题":
question_data['question'] = value
elif header == "答案":
if question_data['type'] == "multiple":
question_data['answer'] = re.split(r'[,,]', value)
else:
question_data['answer'] = value
elif header == "答案解释":
question_data['explanation'] = value if value else ""
elif header == "选项":
# 使用不同的分隔符对选项进行分割
raw_options = re.split(r'[;\n:;]+', value)
# 去除空白项
options = [opt.strip() for opt in raw_options if opt.strip()]
question_data['options'] = {}
for opt in options:
# 允许使用 : 或 . 作为键值分隔符
key_value = re.split(r'[:.]', opt)
if len(key_value) == 2:
key = key_value[0].strip()
val = key_value[1].strip()
question_data['options'][key] = val
# 只有在处理完一行所有数据后才添加到 questions 列表中
self.questions.append(question_data)
# Add IDs to each question
for i, question in enumerate(self.questions):
question['id'] = i + 1
def write_json(self):
if os.path.exists(self.json_file):
with open(self.json_file, 'r', encoding='utf-8') as file:
data = json.load(file)
else:
data = {'questions': []}
data['questions'] = self.questions
with open(self.json_file, 'w', encoding='utf-8') as file:
json.dump(data, file, indent=2, ensure_ascii=False)
def generate_json(self):
self.read_excel()
self.write_json()
# 使用示例
excel_file = '题库/牛客-java.xlsx' # 替换为你的Excel文件路径
json_file = 'questions.json' # 替换为你的JSON文件路径
generator = QuestionsJsonGenerator(excel_file, json_file)
generator.generate_json()
导入模板,数据准备演示
图例:
说明:
看图,自己最好设置一个模板,还有觉得我的这种切分不够你使用习惯的自己去改代码就行,下面说明在哪改吧
标签切分
if header == "标签":
question_data['tags'] = [tag for tag in re.split(r'[,,]', value) if tag.strip()]
答案切分
elif header == "答案":
if question_data['type'] == "multiple":
question_data['answer'] = re.split(r'[,,]', value)
else:
question_data['answer'] = value
选项切分
elif header == "选项":
# 使用不同的分隔符对选项进行分割
raw_options = re.split(r'[;\n:;]+', value)
# 去除空白项
options = [opt.strip() for opt in raw_options if opt.strip()]
question_data['options'] = {}
for opt in options:
# 允许使用 : 或 . 作为键值分隔符
key_value = re.split(r'[:.]', opt)
if len(key_value) == 2:
key = key_value[0].strip()
val = key_value[1].strip()
question_data['options'][key] = val
执行QuestionsJsonGenerator.py
执行成功后检查一个json有没有问题,这里用我这里生成的截图做参考
主窗口程序演示
运行ExerciseDemo1.py