自定义刷题工具-python实现

背景:

最近想要刷题,虽然目前有很多成熟的软件,网站。但是能够支持自定义的导入题库的非常少,或者是要么让你开会员,而直接百度题库的话,正确答案就摆在你一眼能看见的地方,看的时候总觉得自己也会,然后考的时候又啥都不记得了,然后最重要的一点是自定义导入,否则现如今成熟的牛客力扣啥才是最优的选择

自定义题库要素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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/786654.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用Python绘制QQ图并分析数据

使用Python绘制QQ图并分析数据 在这篇博客中&#xff0c;我们将探讨如何使用Python中的pandas库和matplotlib库来绘制QQ图&#xff08;Quantile-Quantile Plot&#xff09;&#xff0c;并分析数据文件中的内容。QQ图是一种常用的统计图表&#xff0c;用于检查一组数据是否服从…

A股探底回升,2900点强势支撑,3000点还会远吗?

今天的A股探底回升&#xff0c;姿势绝了&#xff01;具体原因是这样的&#xff0c;盘面上出现2个重要变化&#xff0c;一起来看看&#xff1a; 1、今天两市低开高走&#xff0c;一度回踩2904点&#xff0c;然后筑底反弹&#xff0c;究竟是昙花一现还是迎来大反击&#xff1f; …

深度学习-数学基础(四)

深度学习数学基础 数学基础线性代数-标量和向量线性代数-向量运算向量加和向量内积向量夹角余弦值 线性代数-矩阵矩阵加法矩阵乘法矩阵点乘矩阵计算的其他内容 人工智能-矩阵的操作矩阵转置&#xff08;transpose&#xff09;矩阵与向量的转化 线性代数-张量&#xff08;tensor…

1001-04SF 同轴连接器

型号简介 1001-04SF是Southwest Microwave的2.92 mm连接器。该连接器采用多种材料制成&#xff0c;包括不锈钢、黄铜和硅橡胶等&#xff0c;以确保其性能和耐用性。它可以帮助工程师和制造商进行设计和制造&#xff0c;并确保连接器的性能和质量。 型号特点 电缆螺母&#xff…

【中项第三版】系统集成项目管理工程师 | 第 4 章 信息系统架构④ | 4.7

前言 第4章对应的内容选择题和案例分析都会进行考查&#xff0c;这一章节属于技术相关的内容&#xff0c;学习要以教材为准。本章分值预计在4-5分。 目录 4.7 安全架构 4.7.1 安全威胁 4.7.2 定义与范围 4.7.3 整体架构设计 4.7.4 网络安全架构设计 4.7.5 数据库系统安…

【Python基础】代码如何打包成exe可执行文件

本文收录于 《一起学Python趣味编程》专栏&#xff0c;从零基础开始&#xff0c;分享一些Python编程知识&#xff0c;欢迎关注&#xff0c;谢谢&#xff01; 文章目录 一、前言二、安装PyInstaller三、使用PyInstaller打包四、验证打包是否成功五、总结 一、前言 本文介绍如何…

西安电子科技大学811/821/833/834/953小班(8人)授课上线

你还在为自己努力&#xff0c;而成绩上不去而烦恼嘛&#xff1f;来看看下面这个小班介绍&#xff0c;或许你会感兴趣&#xff01; 01、西电研梦现在开设一个8人小班&#xff0c;什么是8人小班授课呢&#xff1f; 小班采用82授课机制&#xff1b;其中8人指&#xff1a;8位考研…

活动回顾|2024 MongoDB Developer Day圆满收官!

上周六&#xff0c;MongoDB专家与团队在深圳 与90位开发者度过了充实一日 至此&#xff0c;2024 MongoDB Developer Day 北上深三站之行全部圆满结束&#xff01; 一文回顾本次活动全程与精彩影像&#xff01; MongoDB Developer Day 专为开发者定制的技术盛宴 全天沉浸动手实…

【LabVIEW学习篇 - 5】:数据类型——数值、字符串

文章目录 数值枚举下拉列表控件 字符串字符串与十六进制截取字符串连接字符串 字符串与数值间的转换字符串转为数值数值转为字符串 数值 如下图所示&#xff0c;各种数值型数据的不同之处在于存储和表示数据时所使用的位置不同。 浮点型 整型 在LabVIEW中&#xff0c;想要改…

pointnet2_ops_lib/.安装报错解决方案

问题 3D点云相关的代码例如pointnn、pointmlp都需要安装pointnet2_ops&#xff0c;可是基本上在安装pointnet2_ops时总会报错&#xff0c;终归原因是虚拟环境的cuda版本和安装的torch&#xff0c;torchvision&#xff0c; torchaudio版本不一致导致。 方案 这里以pointmlp&am…

原来Kimi不是不作为,而是在准备大招!

月之暗面Kimi 作为一款我每天都在使用的AI智能助手&#xff0c;是真正的帮助我解决了不少工作及日常创作的一些事情。 它的表现能力也是毋庸置疑&#xff0c;不论是业内还是普通人&#xff0c;10个人当中可能就有9个人在使用Kimi。 而昨天&#xff08;7月8日&#xff09;Kimi…

探索AI大模型(LLM)减少幻觉的三种策略

大型语言模型&#xff08;LLM&#xff09;在生成文本方面具有令人瞩目的能力&#xff0c;但在面对陌生概念和查询时&#xff0c;它们有时会输出看似合理却实际错误的信息&#xff0c;这种现象被称为“幻觉”。近期的研究发现&#xff0c;通过策略性微调和情境学习、检索增强等方…

SpringBoot个人博客系统(开发中)

闲来无事&#xff0c;做个博客玩玩 代码仓库&#xff1a;gitee

【CPP】CPP的命名空间输入输出缺省参数函数重载

目录 1 命名空间 -- namespace2 CPP的输入与输出(io)2.1 输入输出流的一些规定2.2 实操一下2.3 关于endl2.4 关于精度控制2.5 效率提高 3 缺省参数(默认参数)3.1 样例3.2 全缺省与半缺省3.3 缺省参数的意义 4 函数重载4.1 函数重载的基本使用4.2 函数重载调用歧义 这里是oldkin…

v-model双向绑定的实现原理

在前端处理表单时&#xff0c;我们常常需要将表单输入框的内容同步给 JavaScript 中相应的变量。手动连接值绑定和更改事件监听器可能会很麻烦&#xff0c;这里我们先用input属性写入输入框&#xff0c;代码如下&#xff1a; <template><view class"out"&g…

java中 使用数组实现需求小案例(二)

Date: 2024.07.09 16:43:47 author: lijianzhan 需求实现&#xff1a; 设计一个java类&#xff0c;java方法&#xff0c;使用Random函数&#xff0c;根据实现用户输入随机数生成一个打乱的数组。 package com.lin.java.test;import java.util.Arrays; import java.util.Rando…

透明加密软件核心技术分享|十款好用的透明加密软件分享

透明加密软件的核心技术在于其能够自动、实时地对文件进行加密和解密&#xff0c;而这个过程对最终用户来说是无感的。这种技术在不改变用户操作习惯的前提下&#xff0c;增强了数据的安全性。下面是透明加密软件的一些核心技术要点。 实时加密与解密&#xff1a;软件在文件被打…

两名程序员与一名运维人员历经整整一天的时间,成功解决了 nacos 登录时出现“ERR CONNECTION RESET,用户名或密码错误”这一棘手问题

上图看页面报错 这是网页应用提示的信息&#xff0c;服务器日志没有明显的错误日志&#xff0c;检查了账号密码是正常的。 再上第二张图 把这个信息发到百度上查找 通过上图的信息大概知道网络层的原因&#xff0c;可能是在网络安全方面被重置了 下面我们就验证下 1、在服务…

《操作系统真象还原》学习笔记:第1章 部署工作环境

**提示&#xff1a;**这篇文章是根据学长提供的教程《操作系统真象还原》第一章 部署工作环境来完成的&#xff0c;我按照学长给的教程一步一步做下来&#xff0c;再结合《操作系统真象还原》这本书&#xff0c;对实验环境进行了配置。以下是我按照教程进行搭建的记录&#xff…

saas lims系统:适合中小检测机构的实验室管理系统

目前市面上的LIMS系统分为两种&#xff1a;一种是传统的LIMS系统&#xff0c;一次性买断&#xff0c;配置服务器&#xff0c;成本相对来说是比较高的。还有一种就是以白码LIMS云平台为代表的&#xff0c;基于SaaS模式的LIMS系统。白码LIMS系统提供saas模式购买&#xff0c;帮助…