基于Python实现一个简单的题库与在线考试系统

2025-06-11 04:50

本文主要是介绍基于Python实现一个简单的题库与在线考试系统,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《基于Python实现一个简单的题库与在线考试系统》在当今信息化教育时代,在线学习与考试系统已成为教育技术领域的重要组成部分,本文就来介绍一下如何使用Python和PyQt5框架开发一个名为白泽题库系...

概述

在当今信息化教育时代,在线学习与考试系统已成为教育技术领域的重要组成部分。本文将详细介绍一款名为"白泽题库系统"的智能刷题软件的设计与实现,该系统基于python的PyQt5框架开发,集题库管理、随机组卷、智能刷题、错题集和学习统计等功能于一体。

白泽是中国古代神话中的神兽,以博学多识著称,本系统取其"智慧"之意,旨在为用户提供一个高效、智能的刷题学习平台。系统采用模块化设计,具有良好的扩展性和用户体验,特别适合各类考试备考使用。

功能特点

白泽题库系统具备以下核心功能模块:

功能模块描述技术实现
题库管理支持创建、编辑、导入/导出题库jsON/Excel文件处理
随机组卷按题型比例随机生成试卷随机算法、计时功能
题海刷题顺序/逆序/随机刷题模式用户答案记录
错题集自动收集错题,支持复习数据结构存储
学习统计可视化错题分布与趋势Matplotlib图表

系统采用MVC架构设计,主要技术栈包括:

  • 前端:PyQt5 + Qt Designer
  • 数据处理:JSON + openpyxl
  • 图表展示:Matplotlib
  • 核心逻辑:Python 3.x

界面展示

主界面

基于Python实现一个简单的题库与在线考试系统

题库管理

基于Python实现一个简单的题库与在线考试系统

基于Python实现一个简单的题库与在线考试系统

考试模式

基于Python实现一个简单的题库与在线考试系统

基于Python实现一个简单的题库与在线考试系统

刷题模式

基于Python实现一个简单的题库与在线考试系统

错题集

基于Python实现一个简单的题库与在线考试系统

学习统计

基于Python实现一个简单的题库与在线考试系统

系统架构设计

类结构图

基于Python实现一个简单的题库与在线考试系统

Excel题库填写格式模板

基于Python实现一个简单的题库与在线考试系统

题库题目填写格式表

字段名类型必填说明示例
type字符串题目类型,可选值:“单选题”、“多选题”、“判断题”“单选题”
question字符串题目内容“Python是什么类型的语言?”
options字符串列表单选/多选必填题目选项(判断题固定为[“正确”, “错误”])[“编译型”, “解释型”, “混合型”, “汇编型”]
answer字符串正确答案(多选题用逗号+空格分隔)“B”(单选) “A, B, C”(多选) “正确”(判断)
score整数题目分值(默认1分)2
explanation字符串题目解析(可选)“Python是解释型语言,代码在运行时逐行解释执行。”

核心数据结构

题库存储结构

{
    "题库名称": [
        {
            "type": "单选题",
            "question": "问题文本",
            "options": ["A", "B", "C", "D"],
            "answer": "A",
            "score": 2
        },
        # 更多题目...
    ]
}

错题集结构

{
    "题库名称": [
        {
            "type": "单选题",
            "question": "问题文本",
            "options": ["A", "B", "C", "D"],
            "answer": "A",
            "user_answer": "B",
            "score": 2
        },
        # 更多错题...
    ]
}

实现步骤详解

1. 环境配置

首先需要安装必要的Python库:

pip install pyqt5 openpyxl matplotlib

2. 主窗口初始化

class BrainyQuiz(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("白泽题库系统")
        self.setGeometry(100, 100, 1000, 700)
        self.setStyleSheet(self.get_stylesheet())
        
        # 初始化数据
        self.current_question_bank = None
        self.question_banks = {}
        self.current_questions = []
        self.current_index = 0
        self.user_answers = {}
        self.wrong_questions = {}
        
        # 创建界面
        self.init_ui()
        self.load_question_bank_list()

3. 界面布局设计

系统采用侧边栏导航+堆叠窗口的设计模式:

def init_ui(self):
    self.central_widget = QWidget()
    self.setCentralWidget(self.central_widget)
    
    # 主布局:水平布局(侧边栏+内容区)
    self.main_layout = QHBoxLayout(self.central_widget)
    
    # 创建侧边栏
    self.create_sidebar()
    
    # 创建内容区域(堆叠窗口)
    self.content_area = QStackedwidget()
    self.create_all_pages()
    
    # 将组件添加到主布局
    self.main_layout.addWidget(self.sidebar)
    self.main_layout.addWidget(self.content_area)

4. 题库管理实现

题库支持JSON和Excel两种格式的导入导出:

def import_question_bank(self):
    filename, _ = QFileDialog.getOpenFileName(
        self, "导入题库", "", "题库文件 (*.json *.xlsx)"
    )
    
    if filename.endswith('.json'):
        with open(filename, 'r', encoding='utf-8') as f:
            bank_name = filename.split('/')[-1].replace('.json', '')
            self.question_banks[bank_name] = json.load(f)
    elif filename.endswith('.xlsx'):
        bank_name = filename.split('/')[-1].replace('.xlsx', '')
        self.question_banks[bank_name] = self.load_excel_bank(filename)

5. 随机组卷算法

按题型比例随机抽取题目:

def start_exam(self):
    # 计算各题型题目数量
    single_count = int(num_questions * single_ratio / 100)
    multi_count = int(num_questions * multi_ratio / 100)
    bool_count = num_questions - single_count - multi_count
    
    # 随机选择题目
    selected_questions = []
    if single_count > 0:
        selected_questions.extend(random.sample(single_questions, single_count))
    if multi_count > 0:
        selected_questions.extend(random.sample(multi_questions, multi_count))
    if bool_count > 0:
        selected_questions.extend(random.sample(bool_questions, bool_count))
    
    # 随机排序
    random.shuffle(selected_questions)

6. 刷题模式实现

支持三种刷题顺序:

def start_practice(self):
    self.practice_order = self.order_combo.currentText()
    self.current_questions = questions.copy()
    
    if self.practice_order == "逆序":
        self.current_questions.reverse()
    elif self.practice_order == "随机":
        random.shuffle(self.current_questions)

7. 错题集管理

自动记录错题并提供复习功能:

def submit_exam(self):
    if not is_correct:
        # 添加到错题集
        if self.current_question_bank not in self.wrong_questions:
            self.wrong_questions[self.current_question_bank] = []
        
        wrong_question = question.copy()
        wrong_question['user_answer'] = user_answer
        self.wrong_questions[self.current_question_bank].append(wrong_question)

8. 学习统计可视化

使用Matplotlib生成错题分析图表:

def show_stats(self):
    # 创建图表
    fig = self.stats_canvas.figure
    fig.clear()
    
    # 错题题型分布饼图
    ax1 = fig.add_subplot(121)
    ax1.pie(sizes, labels=labels, autopct='%1.1f%%', startangle=90)
    
    # 各题库错题数量柱状图
    ax2 = fig.add_subplot(122)
    ax2.bar(bank_names, wrong_counts)
    
    self.stats_canvas.draw()

关键代码解析

1. 题目显示逻辑

def show_question(self):
    # 获取当前题目
    current_question = self.current_questions[self.current_index]
    
    # 显示题目信息
    self.question_text.setText(current_question['question'])
    
    # 根据题型创建相应选项
    if current_question['type'] == "单选题":
        for i, option in enumerate(current_question['options']):
            rb = QRadioButton(f"{chr(65+i)}. {option}")
            self.options_layout.addWidget(rb)

2. 答案检查逻辑 

def check_answer(self, user_answer, correct_answer, q_type):
    if q_type == "多选题":
        # 对多选题答案进行排序比较
        user_sorted = "".join(sorted(user_answer.upper()))
        correct_sorted = "".join(sorted(correct_answer.replace(", ", "").upper()))
        return user_sorted == correct_sorted
    else:
        return str(user_answer).upper() == str(correct_answer).upper()

3. 样式表设计

def get_stylesheet(self):
    return """
        QMainWindow { background-color: #f5f7fa; }
        QLabel { color: #333; }
        QPushButton {
            background-color: #4e73df;
            color: white;
            border: none;
            padding: 8px 16px;
            border-radius: 4px;
        }
        QPushButton:hover { background-color: #3a5fcd; }
    """

源码下载

import sys
import json
import openpyxl
import random
from PyQt5 import QtGui
from PyQt5.QtGui import QIntValidator
from datetime import datetime
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QvboxLayout, QHBoxLayout, 
                             QLabel, QPushButton, QListWidget, QStackedWidget, QMessageBox,
                             QLineEdit, QTextEdit, QRadioButton, QCheckBox, QComboBox,
                             QGroupBox, QDialog, QFormLayout, QInputDialog, QFileDialog,
                             QTreeWidget, QTreeWidgetItem, QAbstractItemView, QTabWidget)
from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtGui import QFont, QIcon, QPixmap
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas

class BrainyQuiz(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("白泽题库系统")
        self.setGeometry(100, 100, 1000, 700)
        self.setStyleSheet(self.get_stylesheet())
        
        # 初始化数据
        self.current_question_bank = None
        self.question_banks = {}
        self.current_questions = []
        self.current_index = 0
        self.user_answers = {}
        self.wrong_questions = {}
        self.exam_mode = False
        self.practice_mode = False
        self.score = 0
        self.total_score = 0
        self.practice_order = "顺序"
        
        # 创建主界面
        self.init_ui()
        
        # 加载题库列表
        self.load_question_bank_list()
    
    def get_stylesheet(self):
        """返回应用程序样式表"""
        return """
            QMainWindow {
                background-color: #f5f7fa;
            }
            QLabel {
                color: #333;
            }
            QPushButton {
                background-color: #4e73df;
                color: white;
                border: none;
                padding: 8px 16px;
                border-radius: 4px;
                min-width: 100px;
            }
            QPushButton:hover {
                background-color: #3a5fcd;
            }
            QPushButton:disabled {
                background-color: #cccccc;
            }
            QListWidget, QTreeWidget, QTextEdit, QLineEdit, QComboBox {
                border: 1px solid #ddd;
                border-radius: 4px;
                padding: 5px;
                background-color: white;
            }
            QGroupBox {
                border: 1px solid #ddd;
                border-radius: 4px;
                margin-top: 10px;
                padding-top: 15px;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
            }
            QRadioButton, QCheckBox {
                spacing: 5px;
            }
        """
    
    def init_ui(self):
        """初始化UI界面"""
        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)
        
        self.main_layout = QHBoxLayout(self.central_widget)
        
        # 左侧导航栏
        self.create_sidebar()
        
        # 右侧内容区域
        self.content_area = QStackedWidget()
        
        # 添加各个页面
        self.create_all_pages()
        
        # 将侧边栏和内容区域添加到主布局
        self.main_layout.addWidget(self.sidebar)
        self.main_layout.addWidget(self.content_area)
    
    def create_sidebar(self):
        """创建左侧导航栏"""
        self.sidebar = QWidget()
        self.sidebar.setFixedWidth(200)
        self.sidebar.setStyleSheet("background-color: #2c3e50;")
        self.sidebar_layout = QVBoxLayout(self.sidebar)
        
        # 应用标题
        title = QLabel(" 题库考试系统")
        title.setStyleSheet("""
            QLabel {
                color: white;
                font-size: 18px;
                font-weight: bold;
                padding: 20px 10px;
            }
        """)
        title.setAlignment(Qt.AlignCenter)
        self.sidebar_layout.addWidget(title)
        
        # 导航按钮
      php  self.create_navigation_buttons()
        
        # 添加按钮到侧边栏
        self.sidebar_layout.addStretch()
        self.sidebar_layout.addWidget(self.btn_exit)
    
    def create_navigation_buttons(self):
        """创建导航按钮"""
        self.btn_bank = QPushButton(" 题库管理")
        self.btn_bank.setStyleSheet("text-align: left; padding-left: 20px;")
        self.btn_bank.clicked.connect(self.show_bank_manager)
        
        self.btn_exam = QPushButton(" 随机组卷")
        self.btn_exam.setStyleSheet("text-align: left; padding-left: 20px;")
        self.btn_exam.clicked.connect(self.show_exam_setup)
        
        self.btn_practice = QPushButton(" 题海刷题")
        self.btn_practice.setStyleSheet("text-align: left; padding-left: 20px;")
        self.btn_practice.clicked.connect(self.show_practice_setup)
        
        self.btn_wrong = QPushButton("❌ 错题集")
        self.btn_wrong.setStyleSheet("text-align: left; padding-left: 20px;")
        self.btn_wrong.clicked.connect(self.show_wrong_questions)
        
        self.btn_stats = QPushButton(" 学习统计")
        self.btn_stats.setStyleSheet("text-align: left; padding-left: 20px;")
        self.btn_stats.clicked.connect(self.show_stats)
        
        self.btn_exit = QPushButton(" 退出")
        self.btn_exit.setStyleSheet("text-align: left; padding-left: 20px;")
        self.btn_exit.clicked.connect(self.close)
        
        # 添加按钮到侧边栏
        buttons = [self.btn_bank, self.btn_exam, self.btn_practice,
                  self.btn_wrong, self.btn_stats]
        for btn in buttons:
            self.sidebar_layout.addWidget(btn)
    
    def create_all_pages(self):
        """创建所有内容页面"""
        self.main_page = self.create_main_page()
        self.bank_manager_page = self.create_bank_manager_page()
        self.bank_editor_page = self.create_bank_editor_page()
        self.exam_setup_page = self.create_exam_setup_page()
        self.exam_page = self.create_exam_page()
        self.result_page = self.create_result_page()
        self.wrong_questions_page = self.create_wrong_questions_page()
        self.stats_page = self.create_stats_page()
        self.practice_setup_page = self.create_practice_setup_page()
        self.practice_page = self.create_practice_page()
        
        # 添加到堆栈窗口
        pages = [
            self.main_page, self.bank_manager_page, self.bank_editor_page,
            self.exam_setup_page, self.exam_page, self.result_page,
            self.wrong_questions_page, self.stats_page,
            self.practice_setup_page, self.practice_page
        ]
        for page in pages:
            self.content_area.addWidget(page)
    
    def create_main_page(self):
        """创建主页面"""
        page = QWidget()
        layout = QVBoxLayout(page)
        
        # 标题
        title = QLabel("白泽为引,点亮你的题海修行之路")
        title.setStyleSheet("""
            QLabel {
                font-size: 24px;
                font-weight: bold;
                color: #2c3e50;
                margin-bottom: 30px;
            }
        """)
        title.setAlignment(Qt.AlignCenter)
        layout.addWidget(title)
        

        
        
        # 应用描述
        desc = QLabel("一款功能强大的刷题软件,支持多种题型和智能学习统计")
        desc.setStyleSheet("font-size: 16px; color: #7f8c8d;")
        desc.setAlignment(Qt.AlignCenter)
        layout.addWidget(desc)
        
        # 插入空行
        spacer = QLabel("")
        spacer.setFixedHeight(15)  # 设置高度,10~20px 之间都可以
        layout.addWidget(spacer)
        
        # 图标(如果没有资源文件则不显示)
        icon = QLabel()
        try:
            pixmap = QPixmap("icon.ico")
            if not pixmap.isNull():
                pixmap = pixmap.scaled(100, 100, Qt.KeepASPectRatio, Qt.SmoothTransformation)
                icon.setPixmap(pixmap)
                icon.setAlignment(Qt.AlignCenter)
                layout.addWidget(icon)
        except:
            pass
        
        layout.addStretch()
        return page
    
    def create_bank_manager_page(self):
        """创建题库管理页面"""
        page = QWidget()
        layout = QVBoxLayout(page)
        
        # 标题
        title = QLabel(" 题库管理")
        title.setStyleSheet("""
            QLabel {
                font-size: 20px;
                font-weight: bold;
                margin-bottom: 20px;
            }
        """)
        title.setAlignment(Qt.AlignCenter)
        layout.addWidget(title)
        
        # 题库列表
        self.bank_list = QListWidget()
        self.bank_list.setStyleSheet("font-size: 14px;")
        layout.addWidget(self.bank_list)
        
        # 按钮区域
        btn_layout = QHBoxLayout()
        
        self.btn_new_bank = QPushButton("➕ 新建题库")
        self.btn_new_bank.clicked.connect(self.create_new_bank)
        
        self.btn_import_bank = QPushButton(" 导入题库")
        self.btn_import_bank.clicked.connect(self.import_question_bank)
        
        self.btn_edit_bank = QPushButton("✏️ 编辑题库")
        self.btn_edit_bank.clicked.connect(self.edit_selected_bank)
        
        self.btn_export_bank = QPushButton(" 导出题库")
        self.btn_export_bank.clicked.connect(self.export_selected_bank)
        
        self.btn_delete_bank = QPushButton("️ 删除题库")
        self.btn_delete_bank.clicked.connect(self.delete_selected_bank)
        
        buttons = [self.btn_new_bank, self.btn_import_bank, self.btn_edit_bank,
                  self.btn_export_bank, self.btn_delete_bank]
        for btn in buttons:
            btn_layout.addWidget(btn)
        
        layout.addLayout(btn_layout)
        
        # 返回按钮
        self.btn_back = QPushButton(" 返回")
        self.btn_back.clicked.connect(self.show_main_page)
        layout.addWidget(self.btn_back, alignment=Qt.AlignRight)
        
        return page
    
    def create_bank_editor_page(self):
        """创建题库编辑器页面"""
        page = QWidget()
        layout = QVBoxLayout(page)
        
        # 标题
        self.bank_editor_title = QLabel()
        self.bank_editor_title.setStyleSheet("""
            QLabel {
                font-size: 20px;
                font-weight: bold;
                margin-bottom: 20px;
            }
        """)
        self.bank_editor_title.setAlignment(Qt.AlignCenter)
        layout.addWidget(self.bank_editor_title)
        
        # www.chinasem.cn题目列表
        self.question_tree = QTreeWidget()
        self.question_tree.setHeaderLabels(["题型", "题目", "选项", "答案", "分值"])
        self.question_tree.setColumnWidth(0, 80)
        self.question_tree.setColumnWidth(1, 300)
        self.question_tree.setColumnWidth(2, 200)
        self.question_tree.setColumnWidth(3, 100)
        self.question_tree.setColumnWidth(4, 60)
        self.question_tree.setSelectionMode(QAbstractItemView.SingleSelection)
        layout.addWidget(self.question_tree)
        
        # 按钮区域
        btn_layout = QHBoxLayout()
        
        self.btn_add_question = QPushButton("➕ 添加题目")
        self.btn_add_question.clicked.connect(self.add_question_dialog)
        
        self.btn_edit_question = QPushButton("✏️ 编辑题目")
        self.btn_edit_question.clicked.connect(self.edit_question_dialog)
        
        self.btn_delete_question = QPushButton("️ 删除题目")
        self.btn_delete_question.clicked.connect(self.delete_question)
        
        buttons = [self.btn_add_question, self.btn_edit_question, self.btn_delete_question]
        for btn in buttons:
            btn_layout.addWidget(btn)
        
        layout.addLayout(btn_layout)
        
        # 保存和返回按钮
        bottom_btn_layout = QHBoxLayout()
        
        self.btn_save_bank = QPushButton(" 保存题库")
        self.btn_save_bank.clicked.connect(self.save_question_bank)
        
        self.btn_back_editor = QPushButton(" 返回")
        self.btn_back_editor.clicked.connect(self.show_bank_manager)
        
        bottom_btn_layout.addWidget(self.btn_save_bank)
        bottom_btn_layout.addStretch()
        bottom_btn_layout.addWidget(self.btn_back_editor)
        
        layout.addLayout(bottom_btn_layout)
        
        return page
    
    def create_exam_setup_page(self):
        """创建组卷设置页面"""
        page = QWidget()
        layout = QVBoxLayout(page)
        
        # 标题
        self.exam_setup_title = QLabel(" 随机组卷设置")
        self.exam_setup_title.setStyleSheet("""
            QLabel {
                font-size: 20px;
                font-weight: bold;
                margin-bottom: 20px;
            }
        """)
        self.exam_setup_title.setAlignment(Qt.AlignCenter)
        layout.addWidget(self.exam_setup_title)
        
        # 设置表单
        form_layout = QFormLayout()
        
        # 题库选择
        self.bank_combo = QComboBox()
        form_layout.addRow("选择题库:", self.bank_combo)
        
        # 组卷设置
        self.exam_settings_group = QGroupBox("组卷设置")
        exam_layout = QVBoxLayout(self.exam_settings_group)
        
        # 题目数量
        self.num_questions = QLineEdit("10")
        self.num_questions.setValidator(QtGui.QIntValidator(1, 100))
        exam_layout.addWidget(QLabel("题目数量:"))
        exam_layout.addWidget(self.num_questions)
        
        # 题型分布
        type_layout = QHBoxLayout()
        
        self.single_ratio = QLineEdit("40")
        self.single_ratio.setValidator(QtGui.QIntValidator(0, 100))
        type_layout.addWidget(QLabel("单选:"))
        type_layout.addWidget(self.single_ratio)
        type_layout.addWidget(QLabel("%"))
        
        self.multi_ratio = QLineEdit("30")
        self.multi_ratio.setValidator(QtGui.QIntValidator(0, 100))
        type_layout.addWidget(QLabel("多选:"))
        type_layout.addWidget(self.multi_ratio)
        type_layout.addWidget(QLabel("%"))
        
        self.bool_ratio = QLineEdit("30")
        self.bool_ratio.setValidator(QtGui.QIntValidator(0, 100))
        type_layout.addWidget(QLabel("判断:"))
        type_layout.addWidget(self.bool_ratio)
        type_layout.addWidget(QLabel("%"))
        
        exam_layout.addWidget(QLabel("题型分布:"))
        exam_layout.addLayout(type_layout)
        
        # 时间限制
        self.time_limit_label = QLabel("时间限制(分钟):")
        self.time_limit = QLineEdit("30")
        self.time_limit.setValidator(QtGui.QIntValidator(1, 300))
        exam_layout.addWidget(self.time_limit_label)
        exam_layout.addWidget(self.time_limit)
        
        form_layout.addRow(self.exam_settings_group)
        
        layout.addLayout(form_layout)
        
        # 按钮区域
        btn_layout = QHBoxLayout()
        
        self.btn_start_exam = QPushButton(" 开始考试")
        self.btn_start_exam.clicked.connect(self.start_exam)
        
        self.btn_back_setup = QPushButton(" 返回")
        self.btn_back_setup.clicked.connect(self.show_main_page)
        
        btn_layout.addWidget(self.btn_start_exam)
        btn_layout.addWidget(self.btn_back_setup)
        
        layout.addLayout(btn_layout)
        
        return page
    
    def create_exam_page(self):
        """创建考试页面"""
        page = QWidget()
        layout = QVBoxLayout(page)
        
        # 计时器
        self.timer_layout = QHBoxLayout()
        self.timer_label = QLabel()
        self.timer_label.setStyleSheet("font-size: 16px; font-weight: bold; color: #e74c3c;")
        self.timer_layout.addStretch()
        self.timer_layout.addWidget(self.timer_label)
        layout.addLayout(self.timer_layout)
        
        # 进度显示
        self.progress_label = QLabel()
        self.progress_label.setStyleSheet("font-size: 16px; font-weight: bold;")
        layout.addWidget(self.progress_label, alignment=Qt.AlignLeft)
        
        # 题目内容
        self.question_group = QGroupBox()
        self.question_group.setStyleSheet("QGroupBox { font-size: 14px; }")
        question_layout = QVBoxLayout(self.question_group)
        
        self.question_type_label = QLabel()
        self.question_type_label.setStyleSheet("font-weight: bold; color: #3498db;")
        
        self.question_text = QLabel()
        self.question_text.setWordWrap(True)
        self.question_text.setStyleSheet("font-size: 15px;")
        
        self.options_layout = QVBoxLayout()
        
        question_layout.addWidget(self.question_type_label)
        question_layout.addWidget(self.question_text)
        question_layout.addLayout(self.options_layout)
        question_layout.addStretch()
        
        layout.addWidget(self.question_group)
        
        # 按钮区域
        btn_layout = QHBoxLayout()
        
        self.btn_prev = QPushButton("⬅️ 上一题")
        self.btn_prev.clicked.connect(self.prev_question)
        
        self.btn_next = QPushButton("➡️ 下一题")
        self.btn_next.clicked.connect(self.next_question)
        
        self.btn_submit = QPushButton(" 交卷")
        self.btn_submit.clicked.connect(self.submit_exam)
        
        self.btn_back_exam = QPushButton(" 返回")
        self.btn_back_exam.clicked.connect(self.confirm_exit_exam)
        
        buttons = [self.btn_prev, self.btn_next, self.btn_submit, self.btn_back_exam]
        for btn in buttons:
            btn_layout.addWidget(btn)
        
        layout.addLayout(btn_layout)
        
        return page
    
    def create_result_page(self):
        """创建测验结果页面"""
        page = QWidget()
        layout = QVBoxLayout(page)
        
        # 标题
        title = QLabel(" 测验结果")
        title.setStyleSheet("""
            QLabel {
                font-size: 20px;
                font-weight: bold;
                margin-bottom: 20px;
            }
        """)
        title.setAlignment(Qt.AlignCenter)
        layout.addWidget(title)
        
        # 结果统计
        self.stats_group = QGroupBox("测验统计")
        stats_layout = QFormLayout(self.stats_group)
        
        self.total_score_label = QLabel()
        self.accuracy_label = QLabel()
        
        stats_layout.addRow("总分:", self.total_score_label)
        stats_layout.addRow("正确率:", self.accuracy_label)
        
        layout.addWidget(self.stats_group)
        
        # 错题按钮
        self.btn_wrong_answers = QPushButton("查看错题")
        self.btn_wrong_answers.setStyleSheet("background-color: #e74c3c;")
        self.btn_wrong_answers.clicked.connect(self.show_wrong_answers)
        layout.addWidget(self.btn_wrong_answers, alignment=Qt.AlignCenter)
        
        # 按钮区域
        btn_layout = QHBoxLayout()
        
        self.btn_retry = QPushButton(" 重新测验")
        self.btn_retry.clicked.connect(self.retry_exam)
        
        self.btn_back_reslpeYuult = QPushButton(" 返回主页")
        self.btn_back_result.clicked.connect(self.show_main_page)
        
        btn_layout.addWidget(self.btn_retry)
        btn_layout.addWidget(self.btn_back_result)
        
        layout.addLayout(btn_layout)
        
        return page
    
    def create_wrong_questions_page(self):
        """创建错题集页面"""
        page = QWidget()
        layout = QVBoxLayout(page)
        
        # 标题
        title = QLabel("❌ 错题集")
        title.setStyleSheet("""
            QLabel {
                font-size: 20px;
                font-weight: bold;
                margin-bottom: 20px;
            }
        """)
        title.setAlignment(Qt.AlignCenter)
        layout.addWidget(title)
        
        # 题库选择
        bank_layout = QHBoxLayout()
        
        bank_layout.addWidget(QLabel("选择题库:"))
        
        self.wrong_bank_combo = QComboBox()
        self.wrong_bank_combo.currentIndexChanged.connect(self.update_wrong_list)
        bank_layout.addWidget(self.wrong_bank_combo)
        
        layout.addLayout(bank_layout)
        
        # 错题列表
        self.wrong_tree = QTreeWidget()
        self.wrong_tree.setHeaderLabels(["题型", "题目", "正确答案", "你的答案"])
        self.wrong_tree.setColumnWidth(0, 80)
        self.wrong_tree.setColumnWidth(1, 400)
        self.wrong_tree.setColumnWidth(2, 100)
        self.wrong_tree.setColumnWidth(3, 100)
        self.wrong_tree.setSelectionMode(QAbstractItemView.SingleSelection)
        layout.addWidget(self.wrong_tree)
        
        # 按钮区域
        btn_layout = QHBoxLayout()
        
        self.btn_detail = QPushButton(" 查看详情")
        self.btn_detail.clicked.connect(self.show_wrong_detail)
        
        self.btn_delete_wrong = QPushButton("️ 删除错题")
        self.btn_delete_wrong.clicked.connect(self.delete_wrong_question)
        
        self.btn_back_wrong = QPushButton(" 返回")
        self.btn_back_wrong.clicked.connect(self.back_from_wrong_page)
        
        buttons = [self.btn_detail, self.btn_delete_wrong, self.btn_back_wrong]
        for btn in buttons:
            btn_layout.addWidget(btn)
        
        layout.addLayout(btn_layout)
        
        return page
    
    def create_stats_page(self):
        """创建学习统计页面"""
        page = QWidget()
        layout = QVBoxLayout(page)
        
        # 标题
        title = QLabel(" 学习统计")
        title.setStyleSheet("""
            QLabel {
                font-size: 20px;
                font-weight: bold;
                margin-bottom: 20px;
            }
        """)
        title.setAlignment(Qt.AlignCenter)
        layout.addWidget(title)
        
        # 图表区域
        self.stats_canvas = FigureCanvas(plt.Figure(figsize=(10, 5)))
        layout.addWidget(self.stats_canvas)
        
        # 返回按钮
        self.btn_back_stats = QPushButton(" 返回")
        self.btn_back_stats.clicked.connect(self.show_main_page)
        layout.addWidget(self.btn_back_stats, alignment=Qt.AlignRight)
        
        return page
    
    def create_practice_setup_page(self):
        """创建刷题设置页面"""
        page = QWidget()
        layout = QVBoxLayout(page)
        
        # 标题
        title = QLabel(" 题海刷题模式")
        title.setStyleSheet("""
            QLabel {
                font-size: 20px;
                font-weight: bold;
                margin-bottom: 20px;
            }
        """)
        title.setAlignment(Qt.AlignCenter)
        layout.addWidget(title)
        
        # 题库选择
        form_layout = QFormLayout()
        self.practice_bank_combo = QComboBox()
        form_layout.addRow("选择题库:", self.practice_bank_combo)
        
        # 刷题顺序选择
        self.order_combo = QComboBox()
        self.order_combo.addItems(["顺序", "逆序", "随机"])
        form_layout.addRow("刷题顺序:", self.order_combo)
        
        layout.addLayout(form_layout)
        
        # 按钮区域
        btn_layout = QHBoxLayout()
        
        self.btn_start_practice = QPushButton("开始刷题")
        self.btn_start_practice.clicked.connect(self.start_practice)
        
        self.btn_back_practice = QPushButton("返回")
        self.btn_back_practice.clicked.connect(self.show_main_page)
        
  www.chinasem.cn      btn_layout.addWidget(self.btn_start_practice)
        btn_layout.addWidget(self.btn_back_practice)
        
        layout.addLayout(btn_layout)
        
        return page
    
    def create_practice_page(self):
        """创建刷题页面"""
        page = QWidget()
        layout = QVBoxLayout(page)
        
        # 进度显示
        self.practice_progress_label = QLabel()
        self.practice_progress_label.setStyleSheet("font-size: 16px; font-weight: bold;")
        layout.addWidget(self.practice_progress_label, alignment=Qt.AlignLeft)
        
        # 题目内容
        self.practice_question_group = QGroupBox()
        self.practice_question_group.setStyleSheet("QGroupBox { font-size: 14px; }")
        question_layout = QVBoxLayout(self.practice_question_group)
        
        self.practice_question_type_label = QLabel()
        self.practice_question_type_label.setStyleSheet("font-weight: bold; color: #3498db;")
        
        self.practice_question_text = QLabel()
        self.practice_question_text.setWordWrap(True)
        self.practice_question_text.setStyleSheet("font-size: 15px;")
        
        self.practice_options_layout = QVBoxLayout()
        
        question_layout.addWidget(self.practice_question_type_label)
        question_layout.addWidget(self.practice_question_text)
        question_layout.addLayout(self.practice_options_layout)
        question_layout.addStretch()
        
        layout.addWidget(self.practice_question_group)
        
        # 答案反馈
        self.answer_feedback = QLabel()
        self.answer_feedback.setStyleSheet("font-size: 14px; font-weight: bold;")
        self.answer_feedback.setWordWrap(True)
        layout.addWidget(self.answer_feedback)
        
        # 按钮区域
        btn_layout = QHBoxLayout()
        
        self.btn_prev_practice = QPushButton("⬅️ 上一题")
        self.btn_prev_practice.clicked.connect(self.prev_practice_question)
        
        self.btn_next_practice = QPushButton("➡️ 下一题")
        self.btn_next_practice.clicked.connect(self.next_practice_question)
        
        self.btn_submit_practice = QPushButton("提交答案")
        self.btn_submit_practice.clicked.connect(self.submit_practice_answer)
        
        self.btn_back_practice_page = QPushButton(" 返回")
        self.btn_back_practice_page.clicked.connect(self.confirm_exit_practice)
        
        buttons = [self.btn_prev_practice, self.btn_next_practice, 
                  self.btn_submit_practice, self.btn_back_practice_page]
        for btn in buttons:
            btn_layout.addWidget(btn)
        
        layout.addLayout(btn_layout)
        
        return page
    
    # 页面导航方法
    def show_main_page(self):
        """显示主页面"""
        self.content_area.setCurrentWidget(self.main_page)
    
    def show_bank_manager(self):
        """显示题库管理页面"""
        self.bank_list.clear()
        for bank_name in self.question_banks.keys():
            self.bank_list.addItem(bank_name)
        self.content_area.setCurrentWidget(self.bank_manager_page)
    
    def show_bank_editor(self, bank_name):
        """显示题库编辑器页面"""
        self.bank_editor_title.setText(f"✏️ 编辑题库: {bank_name}")
        self.current_question_bank = bank_name
        self.question_tree.clear()
        
        for question in self.question_banks[bank_name]:
            q_type = question['type']
            q_text = question['question']
            options = "\n".join(question['options']) if 'options' in question else ""
            answer = ", ".join(question['answer']) if isinstance(question['answer'], list) else question['answer']
            score = question.get('score', 1)
            
            item = QTreeWidgetItem(self.question_tree)
            item.setText(0, q_type)
            item.setText(1, q_text)
            item.setText(2, options)
            item.setText(3, answer)
            item.setText(4, str(score))
        
        self.content_area.setCurrentWidget(self.bank_editor_page)
    
    def show_exam_setup(self):
        """显示组卷设置页面"""
        self.bank_combo.clear()
        self.bank_combo.addItems(self.question_banks.keys())
        
        if self.current_question_bank and self.current_question_bank in self.question_banks:
            index = self.bank_combo.findText(self.current_question_bank)
            if index >= 0:
                self.bank_combo.setCurrentIndex(index)
        
        self.content_area.setCurrentWidget(self.exam_setup_page)
    
    def show_practice_setup(self):
        """显示刷题设置页面"""
        self.practice_bank_combo.clear()
        self.practice_bank_combo.addItems(self.question_banks.keys())
        self.content_area.setCurrentWidget(self.practice_setup_page)
    
    def show_question(self):
        """显示当前题目"""
        if not self.current_questions:
            QMessageBox.warning(self, "错误", "没有可用的题目!")
            return
        
        self.content_area.setCurrentWidget(self.exam_page)
        
        # 显示计时器
        self.timer_layout.parentWidget().show()
        self.update_exam_timer()
        
        # 更新进度
        self.progress_label.setText(f"题目 {self.current_index + 1}/{len(self.current_questions)}")
        
        # 获取当前题目
        current_question = self.current_questions[self.current_index]
        q_type = current_question['type']
        q_text = current_question['question']
        options = current_question.get('options', [])
        q_score = current_question.get('score', 1)
        
        # 显示题目信息
        self.question_type_label.setText(f"[{q_type}] (分值: {q_score})")
        self.question_text.setText(q_text)
        
        # 清除之前的选项
        while self.options_layout.count():
            child = self.options_layout.takeAt(0)
            if child.widget():
                child.widget().deleteLater()
        
        # 用户答案变量
        if q_type == "单选题":
            self.user_answer_rbs = []
            
            for i, option in enumerate(options):
                rb = QRadioButton(f"{chr(65+i)}. {option}")
                if str(self.current_index) in self.user_answers:
                    if self.user_answers[str(self.current_index)] == chr(65+i):
                        rb.setChecked(True)
                self.user_answer_rbs.append(rb)
                self.options_layout.addWidget(rb)
        
        elif q_type == "多选题":
            self.user_answer_cbs = []
            
            for i, option in enumerate(options):
                cb = QCheckBox(f"{chr(65+i)}. {option}")
                if str(self.current_index) in self.user_answers:
                    if chr(65+i) in self.user_answers[str(self.current_index)]:
                        cb.setChecked(True)
                self.user_answer_cbs.append(cb)
                self.options_layout.addWidget(cb)
        
        elif q_type == "判断题":
            self.user_answer_rbs = []
            
            rb_true = QRadioButton("正确")
            rb_false = QRadioButton("错误")
            
            if str(self.current_index) in self.user_answers:
                if self.user_answers[str(self.current_index)] == "正确":
                    rb_true.setChecked(True)
                else:
                    rb_false.setChecked(True)
            
            self.user_answer_rbs.append(rb_true)
            self.user_answer_rbs.append(rb_false)
            
            self.options_layout.addWidget(rb_true)
            self.options_layout.addWidget(rb_false)
        
        # 更新按钮状态
        self.btn_prev.setEnabled(self.current_index > 0)
        
        if self.current_index < len(self.current_questions) - 1:
            self.btn_next.show()
            self.btn_submit.hide()
        else:
            self.btn_next.hide()
            self.btn_submit.show()
    
    def show_practice_question(self):
        """显示刷题题目"""
        if not self.current_questions:
            QMessageBox.warning(self, "错误", "没有可用的题目!")
            return
        
        self.content_area.setCurrentWidget(self.practice_page)
        
        # 更新进度
        self.practice_progress_label.setText(f"题目 {self.current_index + 1}/{len(self.current_questions)}")
        self.answer_feedback.clear()
        
        # 获取当前题目
        current_question = self.current_questions[self.current_index]
        q_type = current_question['type']
        q_text = current_question['question']
        options = current_question.get('options', [])
        
        # 显示题目信息
        self.practice_question_type_label.setText(f"[{q_type}]")
        self.practice_question_text.setText(q_text)
        
        # 清除之前的选项
        while self.practice_options_layout.count():
            child = self.practice_options_layout.takeAt(0)
            if child.widget():
                child.widget().deleteLater()
        
        # 用户答案变量
        if q_type == "单选题":
            self.user_answer_rbs = []
            
            for i, option in enumerate(options):
                rb = QRadioButton(f"{chr(65+i)}. {option}")
                if str(self.current_index) in self.user_answers:
                    if self.user_answers[str(self.current_index)] == chr(65+i):
                        rb.setChecked(True)
                self.user_answer_rbs.append(rb)
                self.practice_options_layout.addWidget(rb)
        
        elif q_type == "多选题":
            self.user_answer_cbs = []
            
            for i, option in enumerate(options):
                cb = QCheckBox(f"{chr(65+i)}. {option}")
                if str(self.current_index) in self.user_answers:
                    if chr(65+i) in self.user_answers[str(self.current_index)]:
                        cb.setChecked(True)
                self.user_answer_cbs.append(cb)
                self.practice_options_layout.addWidget(cb)
        
        elif q_type == "判断题":
            self.user_answer_rbs = []
            
            rb_true = QRadioButton("正确")
            rb_false = QRadioButton("错误")
            
            if str(self.current_index) in self.user_answers:
                if self.user_answers[str(self.current_index)] == "正确":
                    rb_true.setChecked(True)
                else:
                    rb_false.setChecked(True)
            
            self.user_answer_rbs.append(rb_true)
            self.user_answer_rbs.append(rb_false)
            
            self.practice_options_layout.addWidget(rb_true)
            self.practice_options_layout.addWidget(rb_false)
        
        # 更新按钮状态
        self.btn_prev_practice.setEnabled(self.current_index > 0)
        self.btn_next_practice.setEnabled(self.current_index < len(self.current_questions) - 1)
        self.btn_submit_practice.setEnabled(True)
    
    def show_exam_result(self, correct_count):
        """显示测验结果"""
        total_questions = len(self.current_questions)
        accuracy = (correct_count / total_questions) * 100 if total_questions > 0 else 0
        
        self.total_score_label.setText(f"{self.score}/{self.total_score}")
        self.accuracy_label.setText(f"{accuracy:.1f}% ({correct_count}/{total_questions})")
        
        # 如果没有错题,隐藏查看错题按钮
        self.btn_wrong_answers.setVisible(correct_count < total_questions)
        
        self.content_area.setCurrentWidget(self.result_page)
    
    def show_wrong_questions(self):
        """显示错题集页面"""
        if not self.wrong_questions:
            QMessageBox.information(self, "提示", "错题集为空!")
            return
        
        self.wrong_bank_combo.clear()
        self.wrong_bank_combo.addItems(self.wrong_questions.keys())
        self.update_wrong_list()
        
        self.content_area.setCurrentWidget(self.wrong_questions_page)
    
    def show_stats(self):
        """显示学习统计页面"""
        if not self.wrong_questions:
            QMessageBox.information(self, "提示", "暂无统计数据!")
            return
        
        # 创建图表
        fig = self.stats_canvas.figure
        fig.clear()
        
        ax1 = fig.add_subplot(121)
        ax2 = fig.add_subplot(122)
        
        # 设置中文字体
        plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
        plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号
        
        # 错题题型分布
        type_counts = {'单选题': 0, '多选题': 0, '判断题': 0}
        for bank_name, questions in self.wrong_questions.items():
            for question in questions:
                q_type = question['type']
                type_counts[q_type] += 1
        
        labels = list(type_counts.keys())
        sizes = list(type_counts.values())
        
        ax1.pie(sizes, labels=labels, autopct='%1.1f%%', startangle=90)
        ax1.set_title('错题题型分布')
        
        # 各题库错题数量
        bank_names = list(self.wrong_questions.keys())
        wrong_counts = [len(questions) for questions in self.wrong_questions.values()]
        
        ax2.bar(bank_names, wrong_counts)
        ax2.set_title('各题库错题数量')
        ax2.set_ylabel('数量')
        ax2.tick_params(axis='x', rotation=45)
        
        fig.tight_layout()
        self.stats_canvas.draw()
        
        self.content_area.setCurrentWidget(self.stats_page)
    
    # 题库管理相关方法
    def import_question_bank(self):
        """导入题库文件"""
        filename, _ = QFileDialog.getOpenFileName(
            self,
            "导入题库",
            "",
            "题库文件 (*.json *.xlsx)"
        )
        
        if not filename:
            return
        
        try:
            if filename.endswith('.json'):
                with open(filename, 'r', encoding='utf-8') as f:
                    bank_name = filename.split('/')[-1].replace('.json', '')
                    self.question_banks[bank_name] = json.load(f)
            elif filename.endswith('.xlsx'):
                bank_name = filename.split('/')[-1].replace('.xlsx', '')
                self.question_banks[bank_name] = self.load_excel_bank(filename)
            
            QMessageBox.information(self, "成功", f"题库 '{bank_name}' 导入成功!")
            self.show_bank_manager()
        except Exception as e:
            QMessageBox.warning(self, "错误", f"导入失败: {str(e)}")
    
    def create_new_bank(self):
        """创建新题库"""
        bank_name, ok = QInputDialog.getText(self, "新建题库", "请输入题库名称:")
        if ok and bank_name:
            if bank_name in self.question_banks:
                QMessageBox.warning(self, "错误", "题库已存在!")
            else:
                self.question_banks[bank_name] = []
                self.bank_list.addItem(bank_name)
                QMessageBox.information(self, "成功", f"题库 '{bank_name}' 创建成功!")
    
    def load_selected_bank(self):
        """加载选中的题库"""
        selected_items = self.bank_list.selectedItems()
        if not selected_items:
            QMessageBox.warning(self, "错误", "请先选择一个题库!")
            return
        
        bank_name = selected_items[0].text()
        self.current_question_bank = bank_name
        QMessageBox.information(self, "成功", f"题库 '{bank_name}' 已加载!")
    
    def edit_selected_bank(self):
        """编辑选中的题库"""
        selected_items = self.bank_list.selectedItems()
        if not selected_items:
            QMessageBox.warning(self, "错误", "请先选择一个题库!")
            return
        
        bank_name = selected_items[0].text()
        self.show_bank_editor(bank_name)
    
    def export_selected_bank(self):
        """导出选中的题库"""
        selected_items = self.bank_list.selectedItems()
        if not selected_items:
            QMessageBox.warning(self, "错误", "请先选择一个题库!")
            return
        
        bank_name = selected_items[0].text()
        
        # 选择导出格式
        formats = ["JSON (.json)", "Excel (.xlsx)"]
        format_choice, ok = QInputDialog.getItem(
            self, "选择导出格式", "请选择导出格式:", formats, 0, False
        )
        
        if ok:
            export_format = "json" if "JSON" in format_choice else "xlsx"
            self.save_question_bank(bank_name, export_format)
    
    def delete_selected_bank(self):
        """删除选中的题库"""
        selected_items = self.bank_list.selectedItems()
        if not selected_items:
            QMessageBox.warning(self, "错误", "请先选择一个题库!")
            return
        
        bank_name = selected_items[0].text()
        
        reply = QMessageBox.question(
            self, 
            "确认", 
            f"确定要删除题库 '{bank_name}' 吗?", 
            QMessageBox.Yes | QMessageBox.No
        )
        
        if reply == QMessageBox.Yes:
            # 从内存中删除
            del self.question_banks[bank_name]
            
            # 从列表框中删除
            self.bank_list.takeItem(self.bank_list.row(selected_items[0]))
            
            QMessageBox.information(self, "成功", f"题库 '{bank_name}' 已删除!")
    
    def add_question_dialog(self):
        """添加题目对话框"""
        if not self.current_question_bank:
            return
        
        dialog = QDialog(self)
        dialog.setWindowTitle(python"添加题目")
        dialog.setModal(True)
        dialog.resize(600, 500)
        
        layout = QVBoxLayout(dialog)
        
        # 题型选择
        type_layout = QHBoxLayout()
        type_layout.addWidget(QLabel("题型:"))
        
        q_type_combo = QComboBox()
        q_type_combo.addItems(["单选题", "多选题", "判断题"])
        type_layout.addWidget(q_type_combo)
        
        layout.addLayout(type_layout)
        
        # 题目内容
        layout.addWidget(QLabel("题目:"))
        
        question_edit = QTextEdit()
        layout.addWidget(question_edit)
        
        # 选项框架
        options_group = QGroupBox("选项")
        options_layout = QVBoxLayout(options_group)
        
        self.option_edits = []
        for i in range(4):
            option_layout = QHBoxLayout()
            option_layout.addWidget(QLabel(f"选项 {chr(65+i)}:"))
            
            option_edit = QLineEdit()
            self.option_edits.append(option_edit)
            option_layout.addWidget(option_edit)
            
            options_layout.addLayout(option_layout)
        
        layout.addWidget(options_group)
        
        # 答案框架
        answer_group = QGroupBox("答案")
        answer_layout = QVBoxLayout(answer_group)
        
        # 单选题答案
        self.single_answer_combo = QComboBox()
        self.single_answer_combo.addItems(["A", "B", "C", "D"])
        
        # 多选题答案
        self.multi_answer_checks = []
        multi_layout = QHBoxLayout()
        for i in range(4):
            cb = QCheckBox(chr(65+i))
            self.multi_answer_checks.append(cb)
            multi_layout.addWidget(cb)
        
        # 判断题答案
        self.bool_answer_combo = QComboBox()
        self.bool_answer_combo.addItems(["正确", "错误"])
        
        # 默认显示单选题答案
        answer_layout.addWidget(self.single_answer_combo)
        
        # 分值
        score_layout = QHBoxLayout()
        score_layout.addWidget(QLabel("分值:"))
        
        score_edit = QLineEdit("1")
        score_edit.setValidator(QtGui.QIntValidator(1, 10))
        score_layout.addWidget(score_edit)
        
        layout.addWidget(answer_group)
        layout.addLayout(score_layout)
        
        # 按钮
        btn_layout = QHBoxLayout()
        
        add_btn = QPushButton("添加")
        add_btn.clicked.connect(lambda: self.add_question(
            dialog, q_type_combo, question_edit, score_edit
        ))
        
        cancel_btn = QPushButton("取消")
        cancel_btn.clicked.connect(dialog.reject)
        
        btn_layout.addWidget(add_btn)
        btn_layout.addWidget(cancel_btn)
        
        layout.addLayout(btn_layout)
        
        # 题型变化事件
        def on_type_change(index):
            # 隐藏所有答案控件
            self.single_answer_combo.hide()
            for cb in self.multi_answer_checks:
                cb.hide()
            self.bool_answer_combo.hide()
            
            selected_type = q_type_combo.currentText()
            
            if selected_type == "单选题":
                self.single_answer_combo.show()
            elif selected_type == "多选题":
                for i, cb in enumerate(self.multi_answer_checks):
                    cb.show()
                    if i >= 4:
                        cb.hide()
            elif selected_type == "判断题":
                self.bool_answer_combo.show()
                
            # 显示/隐藏选项
            for i, edit in enumerate(self.option_edits):
                if selected_type == "判断题" and i >= 2:
                    edit.hide()
                    edit.parent().hide()
                else:
                    edit.show()
                    edit.parent().show()
        
        q_type_combo.currentIndexChanged.connect(on_type_change)
        on_type_change(0)  # 初始化
        
        dialog.exec_()
    
    def add_question(self, dialog, q_type_combo, question_edit, score_edit):
        """添加题目到题库"""
        q_type = q_type_combo.currentText()
        q_text = question_edit.toPlainText().strip()
        options = []
        
        if q_type == "判断题":
            options = ["正确", "错误"]
        else:
            for edit in self.option_edits[:4 if q_type != "判断题" else 2]:
                option = edit.text().strip()
                if option:
                    options.append(option)
        
        if not q_text:
            QMessageBox.warning(dialog, "错误", "题目不能为空!")
            return
        
        if not options:
            QMessageBox.warning(dialog, "错误", "至少需要一个选项!")
            return
        
        # 获取答案
        if q_type == "单选题":
            answer = self.single_answer_combo.currentText()
        elif q_type == "多选题":
            answer = []
            for i, cb in enumerate(self.multi_answer_checks):
                if cb.isChecked():
                    answer.append(chr(65+i))
            answer = ", ".join(answer)
        elif q_type == "判断题":
            answer = self.bool_answer_combo.currentText()
        
        if not answer:
            QMessageBox.warning(dialog, "错误", "请设置正确答案!")
            return
        
        try:
            score = int(score_edit.text())
        except ValueError:
            score = 1
        
        # 创建题目字典
        new_question = {
            'type': q_type,
            'question': q_text,
            'options': options,
            'answer': answer,
            'score': score
        }
        
        # 添加到题库
        self.question_banks[self.current_question_bank].append(new_question)
        
        # 更新树形视图
        item = QTreeWidgetItem(self.question_tree)
        item.setText(0, q_type)
        item.setText(1, q_text)
        item.setText(2, "\n".join(options))
        item.setText(3, answer)
        item.setText(4, str(score))
        
        QMessageBox.information(dialog, "成功", "题目添加成功!")
        dialog.accept()
    
    def edit_question_dialog(self):
        """编辑题目对话框"""
        selected_items = self.question_tree.selectedItems()
        if not selected_items:
            QMessageBox.warning(self, "错误", "请先选择要编辑的题目!")
            return
        
        item = selected_items[0]
        q_type = item.text(0)
        q_text = item.text(1)
        options = item.text(2).split("\n")
        answer = item.text(3)
        score = item.text(4)
        
        # 找到题库中的原始题目
        index = self.question_tree.indexOfTopLevelItem(item)
        original_question = self.question_banks[self.current_question_bank][index]
        
        dialog = QDialog(self)
        dialog.setWindowTitle("编辑题目")
        dialog.setModal(True)
        dialog.resize(600, 500)
        
        layout = QVBoxLayout(dialog)
        
        # 题型选择
        type_layout = QHBoxLayout()
        type_layout.addWidget(QLabel("题型:"))
        
        q_type_combo = QComboBox()
        q_type_combo.addItems(["单选题", "多选题", "判断题"])
        q_type_combo.setCurrentText(q_type)
        type_layout.addWidget(q_type_combo)
        
        layout.addLayout(type_layout)
        
        # 题目内容
        layout.addWidget(QLabel("题目:"))
        
        question_edit = QTextEdit()
        question_edit.setText(q_text)
        layout.addWidget(question_edit)
        
        # 选项框架
        options_group = QGroupBox("选项")
        options_layout = QVBoxLayout(options_group)
        
        self.option_edits = []
        for i in range(4):
            option_layout = QHBoxLayout()
            option_layout.addWidget(QLabel(f"选项 {chr(65+i)}:"))
            
            option_edit = QLineEdit()
            if i < len(options):
                option_edit.setText(options[i])
            self.option_edits.append(option_edit)
            option_layout.addWidget(option_edit)
            
            options_layout.addLayout(option_layout)
        
        layout.addWidget(options_group)
        
        # 答案框架
        answer_group = QGroupBox("答案")
        answer_layout = QVBoxLayout(answer_group)
        
        # 单选题答案
        self.single_answer_combo = QComboBox()
        self.single_answer_combo.addItems(["A", "B", "C", "D"])
        
        # 多选题答案
        self.multi_answer_checks = []
        multi_layout = QHBoxLayout()
        for i in range(4):
            cb = QCheckBox(chr(65+i))
            self.multi_answer_checks.append(cb)
            multi_layout.addWidget(cb)
        
        # 判断题答案
        self.bool_answer_combo = QComboBox()
        self.bool_answer_combo.addItems(["正确", "错误"])
        
        # 设置当前答案
        if q_type == "单选题":
            self.single_answer_combo.setCurrentText(answer)
            answer_layout.addWidget(self.single_answer_combo)
        elif q_type == "多选题":
            for char in answer.split(", "):
                if char:
                    idx = ord(char.upper()) - ord('A')
                    if 0 <= idx < 4:
                        self.multi_answer_checks[idx].setChecked(True)
            answer_layout.addLayout(multi_layout)
        elif q_type == "判断题":
            self.bool_answer_combo.setCurrentText(answer)
            answer_layout.addWidget(self.bool_answer_combo)
        
        # 分值
        score_layout = QHBoxLayout()
        score_layout.addWidget(QLabel("分值:"))
        
        score_edit = QLineEdit(score)
        score_edit.setValidator(QtGui.QIntValidator(1, 10))
        score_layout.addWidget(score_edit)
        
        layout.addWidget(answer_group)
        layout.addLayout(score_layout)
        
        # 按钮
        btn_layout = QHBoxLayout()
        
        save_btn = QPushButton("保存")
        save_btn.clicked.connect(lambda: self.save_question_edit(
            dialog, q_type_combo, question_edit, score_edit, index
        ))
        
        cancel_btn = QPushButton("取消")
        cancel_btn.clicked.connect(dialog.reject)
        
        btn_layout.addWidget(save_btn)
        btn_layout.addWidget(cancel_btn)
        
        layout.addLayout(btn_layout)
        
        # 题型变化事件
        def on_type_change(index):
            # 隐藏所有答案控件
            self.single_answer_combo.hide()
            for cb in self.multi_answer_checks:
                cb.hide()
            self.bool_answer_combo.hide()
            
            selected_type = q_type_combo.currentText()
            
            if selected_type == "单选题":
                self.single_answer_combo.show()
            elif selected_type == "多选题":
                for i, cb in enumerate(self.multi_answer_checks):
                    cb.show()
                    if i >= 4:
                        cb.hide()
            elif selected_type == "判断题":
                self.bool_answer_combo.show()
                
            # 显示/隐藏选项
            for i, edit in enumerate(self.option_edits):
                if selected_type == "判断题" and i >= 2:
                    edit.hide()
                    edit.parent().hide()
                else:
                    edit.show()
                    edit.parent().show()
        
        q_type_combo.currentIndexChanged.connect(on_type_change)
        
        dialog.exec_()
    
    def save_question_edit(self, dialog, q_type_combo, question_edit, score_edit, index):
        """保存编辑后的题目"""
        q_type = q_type_combo.currentText()
        q_text = question_edit.toPlainText().strip()
        options = []
        
        if q_type == "判断题":
            options = ["正确", "错误"]
        else:
            for edit in self.option_edits[:4 if q_type != "判断题" else 2]:
                option = edit.text().strip()
                if option:
                    options.append(option)
        
        if not q_text:
            QMessageBox.warning(dialog, "错误", "题目不能为空!")
            return
        
        if not options:
            QMessageBox.warning(dialog, "错误", "至少需要一个选项!")
            return
        
        # 获取答案
        if q_type == "单选题":
            answer = self.single_answer_combo.currentText()
        elif q_type == "多选题":
            answer = []
            for i, cb in enumerate(self.multi_answer_checks):
                if cb.isChecked():
                    answer.append(chr(65+i))
            answer = ", ".join(answer)
        elif q_type == "判断题":
            answer = self.bool_answer_combo.currentText()
        
        if not answer:
            QMessageBox.warning(dialog, "错误", "请设置正确答案!")
            return
        
        try:
            score = int(score_edit.text())
        except ValueError:
            score = 1
        
        # 更新题目字典
        updated_question = {
            'type': q_type,
            'question': q_text,
            'options': options,
            'answer': answer,
            'score': score
        }
        
        # 更新题库
        self.question_banks[self.current_question_bank][index] = updated_question
        
        # 更新树形视图
        item = self.question_tree.topLevelItem(index)
        item.setText(0, q_type)
        item.setText(1, q_text)
        item.setText(2, "\n".join(options))
        item.setText(3, answer)
        item.setText(4, str(score))
        
        QMessageBox.information(dialog, "成功", "题目更新成功!")
        dialog.accept()
    
    def delete_question(self):
        """删除题目"""
        selected_items = self.question_tree.selectedItems()
        if not selected_items:
            QMessageBox.warning(self, "错误", "请先选择要删除的题目!")
            return
        
        reply = QMessageBox.question(
            self, 
            "确认", 
            "确定要删除选中的题目吗?", 
            QMessageBox.Yes | QMessageBox.No
        )
        
        if reply == QMessageBox.No:
            return
        
        # 从树形视图中删除
        index = self.question_tree.indexOfTopLevelItem(selected_items[0])
        self.question_tree.takeTopLevelItem(index)
        
        # 从题库中删除
        if index < len(self.question_banks[self.current_question_bank]):
            self.question_banks[self.current_question_bank].pop(index)
        
        QMessageBox.information(self, "成功", "题目已删除!")
    
    def save_question_bank(self, bank_name=None, export_format="json"):
        """保存题库到文件"""
        if not bank_name:
            bank_name = self.current_question_bank
        
        if export_format == "json":
            filename, _ = QFileDialog.getSaveFileName(
                self,
                "保存题库",
                f"{bank_name}.json",
                "JSON 文件 (*.json)"
            )
            
            if filename:
                try:
                    with open(filename, 'w', encoding='utf-8') as f:
                        json.dump(self.question_banks[bank_name], f, ensure_ascii=False, indent=4)
                    QMessageBox.information(self, "成功", f"题库 '{bank_name}' 已保存为JSON格式!")
                except Exception as e:
                    QMessageBox.warning(self, "错误", f"保存失败: {e}")
        else:  # Excel格式
            filename, _ = QFileDialog.getSaveFileName(
                self,
                "导出题库",
                f"{bank_name}.xlsx",
                "Excel 文件 (*.xlsx)"
            )
            
            if filename:
                try:
                    self.export_to_excel(bank_name, filename)
                    QMessageBox.information(self, "成功", f"题库 '{bank_name}' 已导出为Excel格式!")
                except Exception as e:
                    QMessageBox.warning(self, "错误", f"导出失败: {e}")
    
    def export_to_excel(self, bank_name, filename):
        """将题库导出为Excel文件"""
        workbook = openpyxl.Workbook()
        sheet = workbook.active
        sheet.title = "题库"
        
        # 写入表头
        headers = ["题型", "题目", "选项A", "选项B", "选项C", "选项D", "答案", "分值"]
        sheet.append(headers)
        
        # 写入题目
        for question in self.question_banks[bank_name]:
            row = [
                question["type"],
                question["question"]
            ]
            
            # 添加选项
            options = question.get("options", [])
            for i in range(4):
                row.append(options[i] if i < len(options) else "")
            
            # 添加答案和分值
            row.append(question["answer"])
            row.append(question.get("score", 1))
            
            sheet.append(row)
        
        workbook.save(filename)
    
    def load_question_bank_list(self):
        """加载题库列表"""
        # 尝试加载本地题库文件
        self.load_local_banks()
        
        # 如果没有加载到题库,使用示例题库
        if not self.question_banks:
            self.question_banks = {
                "示例题库": [
                    {
                        "type": "单选题",
                        "question": "Python是什么类型的语言?",
                        "options": ["编译型", "解释型", "混合型", "汇编型"],
                        "answer": "B",
                        "score": 2
                    },
                    {
                        "type": "多选题",
                        "question": "以下哪些是Python的数据类型?",
                        "options": ["int", "float", "string", "boolean"],
                        "answer": "A, B, C, D",
                        "score": 3
                    },
                    {
                        "type": "判断题",
                        "question": "Python是强类型语言。",
                        "options": ["正确", "错误"],
                        "answer": "正确",
                        "score": 1
                    }
                ]
            }
    
    def load_local_banks(self):
        """加载本地题库文件(.json和.xlsx)"""
        # 加载JSON题库
        json_files = self.find_local_files(".json")
        for file in json_files:
            try:
                with open(file, 'r', encoding='utf-8') as f:
                    bank_name = file.split('/')[-1].replace('.json', '')
                    self.question_banks[bank_name] = json.load(f)
            except Exception as e:
                print(f"加载JSON题库失败: {file}, 错误: {e}")
        
        # 加载Excel题库
        excel_files = self.find_local_files(".xlsx")
        for file in excel_files:
            try:
                bank_name = file.split('/')[-1].replace('.xlsx', '')
                self.question_banks[bank_name] = self.load_excel_bank(file)
            except Exception as e:
                print(f"加载Excel题库失败: {file}, 错误: {e}")
    
    def find_local_files(self, extension):
        """查找本地题库文件"""
        import os
        files = []
        for file in os.listdir('.'):
            if file.endswith(extension):
                files.append(file)
        return files
    
    def load_excel_bank(self, file_path):
        """从Excel文件加载题库"""
        workbook = openpyxl.load_workbook(file_path)
        sheet = workbook.active
        questions = []
        
        for row in sheet.iter_rows(min_row=2, values_only=True):
            if not row[0]:  # 跳过空行
                continue
                
            q_type = row[0]
            question = {
                "type": q_type,
                "question": row[1],
                "options": [],
                "answer": row[-2],
                "score": row[-1] if row[-1] else 1
            }
            
            # 处理选项
            if q_type in ["单选题", "多选题"]:
                question["options"] = [opt for opt in row[2:-2] if opt]
            elif q_type == "判断题":
                question["options"] = ["正确", "错误"]
            
            questions.append(question)
        
        return questions
    
    # 考试相关方法
    def start_exam(self):
        """开始考试"""
        bank_name = self.bank_combo.currentText()
        if not bank_name:
            QMessageBox.warning(self, "错误", "请选择题库!")
            return
        
        # 加载题库
        self.current_question_bank = bank_name
        questions = self.question_banks[bank_name]
        
        if not questions:
            QMessageBox.warning(self, "错误", "题库中没有题目!")
            return
            
        try:
            num_questions = int(self.num_questions.text())
            single_ratio = int(self.single_ratio.text())
            multi_ratio = int(self.multi_ratio.text())
            bool_ratio = int(self.bool_ratio.text())
            
            if single_ratio + multi_ratio + bool_ratio != 100:
                QMessageBox.warning(self, "错误", "题型比例总和必须为100%!")
                return
        except ValueError:
            QMessageBox.warning(self, "错误", "请输入有效的数字!")
            return
        
        # 按题型分类题目
        single_questions = [q for q in questions if q['type'] == "单选题"]
        multi_questions = [q for q in questions if q['type'] == "多选题"]
        bool_questions = [q for q in questions if q['type'] == "判断题"]
        
        # 计算各题型题目数量
        single_count = int(num_questions * single_ratio / 100)
        multi_count = int(num_questions * multi_ratio / 100)
        bool_count = num_questions - single_count - multi_count
        
        # 随机选择题目
        selected_questions = []
        
        if single_count > 0:
            if len(single_questions) >= single_count:
                selected_questions.extend(random.sample(single_questions, single_count))
            else:
                selected_questions.extend(single_questions)
        
        if multi_count > 0:
            if len(multi_questions) >= multi_count:
                selected_questions.extend(random.sample(multi_questions, multi_count))
            else:
                selected_questions.extend(multi_questions)
        
        if bool_count > 0:
            if len(bool_questions) >= bool_count:
                selected_questions.extend(random.sample(bool_questions, bool_count))
            else:
                selected_questions.extend(bool_questions)
        
        # 如果题目不足,补充随机题目
        if len(selected_questions) < num_questions:
            remaining = num_questions - len(selected_questions)
            all_questions = [q for q in questions if q not in selected_questions]
            if all_questions:
                selected_questions.extend(random.sample(all_questions, min(remaining, len(all_questions))))
        
        # 随机排序
        random.shuffle(selected_questions)
        
        self.total_score = sum(q.get('score', 1) for q in selected_questions)
        
        # 设置考试计时器
        try:
            time_limit = int(self.time_limit.text())
            self.exam_mode = True
            self.exam_start_time = datetime.now()
            self.exam_time_limit = time_limit * 60  # 转换为秒
        except ValueError:
            QMessageBox.warning(self, "错误", "请输入有效的时间限制!")
            return
        
        # 设置当前题目
        self.current_questions = selected_questions
        self.current_index = 0
        self.user_answers = {}
        self.score = 0
        
        # 显示题目
        self.show_question()
    
    def update_exam_timer(self):
        """更新考试计时器"""
        if not self.exam_mode:
            return
        
        elapsed = (datetime.now() - self.exam_start_time).total_seconds()
        remaining = max(0, self.exam_time_limit - elapsed)
        
        minutes = int(remaining // 60)
        seconds = int(remaining % 60)
        
        self.timer_label.setText(f"剩余时间: {minutes:02d}:{seconds:02d}")
        
        if remaining <= 0:
            self.submit_exam()
        else:
            QTimer.singleShot(1000, self.update_exam_timer)
    
    def prev_question(self):
        """显示上一题"""
        self.save_current_answer()
        if self.current_index > 0:
            self.current_index -= 1
            self.show_question()
    
    def next_question(self):
        """显示下一题"""
        self.save_current_answer()
        if self.current_index < len(self.current_questions) - 1:
            self.current_index += 1
            self.show_question()
        else:
            self.submit_exam()
    
    def save_current_answer(self):
        """保存当前题目的答案"""
        current_question = self.current_questions[self.current_index]
        q_type = current_question['type']
        
        if q_type == "单选题":
            for i, rb in enumerate(self.user_answer_rbs):
                if rb.isChecked():
                    self.user_answers[str(self.current_index)] = chr(65+i)
                    break
        elif q_type == "多选题":
            answer = []
            for i, cb in enumerate(self.user_answer_cbs):
                if cb.isChecked():
                    answer.append(chr(65+i))
            if answer:
                self.user_answers[str(self.current_index)] = "".join(answer)
        elif q_type == "判断题":
            if self.user_answer_rbs[0].isChecked():
                self.user_answers[str(self.current_index)] = "正确"
            elif self.user_answer_rbs[1].isChecked():
                self.user_answers[str(self.current_index)] = "错误"
    
    def submit_exam(self):
        """提交试卷"""
        self.save_current_answer()
        self.exam_mode = False
        
        # 计算得分
        correct_count = 0
        self.score = 0
        
        for i, question in enumerate(self.current_questions):
            user_answer = self.user_answers.get(str(i), "")
            correct_answer = question['answer']
            q_score = question.get('score', 1)
            
            if question['type'] == "多选题":
                # 对多选题答案进行排序比较
                user_answer_sorted = "".join(sorted(user_answer.upper()))
                correct_answer_sorted = "".join(sorted(correct_answer.replace(", ", "").upper()))
                is_correct = user_answer_sorted == correct_answer_sorted
            else:
                is_correct = str(user_answer).upper() == str(correct_answer).upper()
            
            if is_correct:
                self.score += q_score
                correct_count += 1
            else:
                # 添加到错题集
                if self.current_question_bank not in self.wrong_questions:
                    self.wrong_questions[self.current_question_bank] = []
                
                wrong_question = question.copy()
                wrong_question['user_answer'] = user_answer
                wrong_question['index'] = i
                self.wrong_questions[self.current_question_bank].append(wrong_question)
        
        # 显示结果
        self.show_exam_result(correct_count)
    
    def retry_exam(self):
        """重新测验"""
        self.start_exam()
    
    def confirm_exit_exam(self):
        """确认退出考试"""
        reply = QMessageBox.question(
            self, 
            "确认", 
            "确定要退出当前考试吗? 所有进度将丢失!", 
            QMessageBox.Yes | QMessageBox.No
        )
        
        if reply == QMessageBox.Yes:
            self.show_main_page()
    
    # 刷题相关方法
    def start_practice(self):
        """开始刷题"""
        bank_name = self.practice_bank_combo.currentText()
        if not bank_name:
            QMessageBox.warning(self, "错误", "请选择题库!")
            return
        
        # 加载题库
        self.current_question_bank = bank_name
        questions = self.question_banks[bank_name]
        
        if not questions:
            QMessageBox.warning(self, "错误", "题库中没有题目!")
            return
            
        # 设置刷题顺序
        self.practice_order = self.order_combo.currentText()
        
        # 准备题目
        self.current_questions = questions.copy()
        
        if self.practice_order == "逆序":
            self.current_questions.reverse()
        elif self.practice_order == "随机":
            random.shuffle(self.current_questions)
        
        self.current_index = 0
        self.user_answers = {}
        self.practice_mode = True
        
        # 显示第一题
        self.show_practice_question()
    
    def submit_practice_answer(self):
        """提交刷题答案"""
        self.save_current_practice_answer()
        current_question = self.current_questions[self.current_index]
        user_answer = self.user_answers.get(str(self.current_index), "")
        correct_answer = current_question['answer']
        
        # 检查答案
        if current_question['type'] == "多选题":
            # 对多选题答案进行排序比较
            user_answer_sorted = "".join(sorted(user_answer.upper()))
            correct_answer_sorted = "".join(sorted(correct_answer.replace(", ", "").upper()))
            is_correct = user_answer_sorted == correct_answer_sorted
        else:
            is_correct = str(user_answer).upper() == str(correct_answer).upper()
        
        # 显示反馈
        if is_correct:
            self.answer_feedback.setText("✅ 回答正确!")
            self.answer_feedback.setStyleSheet("color: green;")
        else:
            self.answer_feedback.setText(f"❌ 回答错误! 正确答案是: {correct_answer}")
            self.answer_feedback.setStyleSheet("color: red;")
            
            # 记录错题
            if self.current_question_bank not in self.wrong_questions:
                self.wrong_questions[self.current_question_bank] = []
            
            wrong_question = current_question.copy()
            wrong_question['user_answer'] = user_answer
            self.wrong_questions[self.current_question_bank].append(wrong_question)
        
        self.btn_submit_practice.setEnabled(False)
    
    def save_current_practice_answer(self):
        """保存当前刷题答案"""
        current_question = self.current_questions[self.current_index]
        q_type = current_question['type']
        
        if q_type == "单选题":
            for i, rb in enumerate(self.user_answer_rbs):
                if rb.isChecked():
                    self.user_answers[str(self.current_index)] = chr(65+i)
                    break
        elif q_type == "多选题":
            answer = []
            for i, cb in enumerate(self.user_answer_cbs):
                if cb.isChecked():
                    answer.append(chr(65+i))
            if answer:
                self.user_answers[str(self.current_index)] = "".join(answer)
        elif q_type == "判断题":
            if self.user_answer_rbs[0].isChecked():
                self.user_answers[str(self.current_index)] = "正确"
            elif self.user_answer_rbs[1].isChecked():
                self.user_answers[str(self.current_index)] = "错误"
    
    def prev_practice_question(self):
        """上一题"""
        if self.current_index > 0:
            self.current_index -= 1
            self.show_practice_question()
    
    def next_practice_question(self):
        """下一题"""
        if self.current_index < len(self.current_questions) - 1:
            self.current_index += 1
            self.show_practice_question()
    
    def confirm_exit_practice(self):
        """确认退出刷题"""
        reply = QMessageBox.question(
            self, 
            "确认", 
            "确定要退出当前刷题吗?", 
            QMessageBox.Yes | QMessageBox.No
        )
        
        if reply == QMessageBox.Yes:
            self.practice_mode = False
            self.show_main_page()
    
    # 错题集相关方法
    def show_wrong_answers(self):
        """显示错题"""
        self.show_wrong_questions()
    
    def update_wrong_list(self):
        """更新错题列表"""
        bank_name = self.wrong_bank_combo.currentText()
        if not bank_name:
            return
        
        # 清空树形视图
        self.wrong_tree.clear()
        
        # 加载错题
        for question in self.wrong_questions.get(bank_name, []):
            q_type = question['type']
            q_text = question['question']
            answer = question['answer']
            user_answer = question.get('user_answer', '')
            
            item = QTreeWidgetItem(self.wrong_tree)
            item.setText(0, q_type)
            item.setText(1, q_text)
            item.setText(2, answer)
            item.setText(3, user_answer)
    
    def show_wrong_detail(self):
        """显示错题详情"""
        selected_items = self.wrong_tree.selectedItems()
        if not selected_items:
            QMessageBox.warning(self, "错误", "请先选择一道错题!")
            return
        
        item = selected_items[0]
        q_type = item.text(0)
        q_text = item.text(1)
        answer = item.text(2)
        user_answer = item.text(3)
        
        # 找到原始题目
        bank_name = self.wrong_bank_combo.currentText()
        index = None
        for q in self.wrong_questions.get(bank_name, []):
            if q['question'] == q_text and q['answer'] == answer and q.get('user_answer') == user_answer:
                index = q.get('index')
                break
        
        if index is None or index >= len(self.current_questions):
            QMessageBox.warning(self, "错误", "无法找到原始题目!")
            return
        
        original_question = self.current_questions[index]
        
        dialog = QDialog(self)
        dialog.setWindowTitle("错题详情")
        dialog.setModal(True)
        dialog.resize(800, 600)
        
        layout = QVBoxLayout(dialog)
        
        # 题目类型
        type_label = QLabel(f"[{q_type}]")
        type_label.setStyleSheet("font-weight: bold; color: #3498db;")
        layout.addWidget(type_label)
        
        # 题目文本
        question_label = QLabel(q_text)
        question_label.setWordWrap(True)
        question_label.setStyleSheet("font-size: 15px;")
        layout.addWidget(question_label)
        
        # 选项
        options_group = QGroupBox("选项")
        options_layout = QVBoxLayout(options_group)
        
        for i, option in enumerate(original_question['options']):
            option_label = QLabel(f"{chr(65+i)}. {option}")
            options_layout.addWidget(option_label)
        
        layout.addWidget(options_group)
        
        # 正确答案
        correct_frame = QWidget()
        correct_layout = QHBoxLayout(correct_frame)
        
        correct_label = QLabel("正确答案:")
        correct_label.setStyleSheet("font-weight: bold; color: green;")
        
        answer_label = QLabel(answer)
        
        correct_layout.addWidget(correct_label)
        correct_layout.addWidget(answer_label)
        correct_layout.addStretch()
        
        layout.addWidget(correct_frame)
        
        # 你的答案
        user_frame = QWidget()
        user_layout = QHBoxLayout(user_frame)
        
        user_label = QLabel("你的答案:")
        user_label.setStyleSheet("font-weight: bold; color: red;")
        
        user_answer_label = QLabel(user_answer)
        
        user_layout.addWidget(user_label)
        user_layout.addWidget(user_answer_label)
        user_layout.addStretch()
        
        layout.addWidget(user_frame)
        
        # 解析(如果有)
        if 'explanation' in original_question:
            explanation_group = QGroupBox("解析")
            explanation_layout = QVBoxLayout(explanation_group)
            
            explanation_text = QTextEdit(original_question['explanation'])
            explanation_text.setReadOnly(True)
            explanation_layout.addWidget(explanation_text)
            
            layout.addWidget(explanation_group)
        
        # 关闭按钮
        close_btn = QPushButton("关闭")
        close_btn.clicked.connect(dialog.accept)
        layout.addWidget(close_btn, alignment=Qt.AlignRight)
        
        dialog.exec_()
    
    def delete_wrong_question(self):
        """删除错题"""
        selected_items = self.wrong_tree.selectedItems()
        if not selected_items:
            QMessageBox.warning(self, "错误", "请先选择一道错题!")
            return
        
        bank_name = self.wrong_bank_combo.currentText()
        if not bank_name or bank_name not in self.wrong_questions:
            return
        
        reply = QMessageBox.question(
            self, 
            "确认", 
            "确定要删除这道错题吗?", 
            QMessageBox.Yes | QMessageBox.No
        )
        
        if reply == QMessageBox.No:
            return
        
        # 获取选中的错题信息
        item = selected_items[0]
        q_text = item.text(1)
        answer = item.text(2)
        user_answer = item.text(3)
        
        # 从错题集中删除
        for i, question in enumerate(self.wrong_questions[bank_name]):
            if (question['question'] == q_text and 
                question['answer'] == answer and 
                question.get('user_answer') == user_answer):
                self.wrong_questions[bank_name].pop(i)
                break
        
        # 从树形视图中删除
        self.wrong_tree.takeTopLevelItem(self.wrong_tree.indexOfTopLevelItem(item))
        
        QMessageBox.information(self, "成功", "错题已删除!")
    
    def back_from_wrong_page(self):
        """从错题集页面返回"""
        if hasattr(self, 'from_result_page') and self.from_result_page:
            self.content_area.setCurrentWidget(self.result_page)
        else:
            self.show_main_page()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    
    # 尝试设置应用图标,如果没有则忽略
    try:
        app.setWindowIcon(QIcon("icon.ico"))
    except:
        pass
    
    # 设置全局字体
    font = QFont()
    font.setFamily("Arial")
    font.setPointSize(10)
    app.setFont(font)
    
    window = BrainyQuiz()
    window.show()
    sys.exit(app.exec_())

项目结构

brainy-quiz/
├── main.py                # 主程序入口
├── question_banks/        # 默认题库目录
│   ├── sample.json        # 示例JSON题库
│   └── sample.xlsx        # 示例Excel题库
├── icons/                 # 图标资源
│   └── icon.ico           # 应用图标
└── README.md              # 项目说明文档

未来扩展与优化目标

1.功能扩展:

添加用户系统,支持多账户

实现云端同步功能

增加题目解析和知识点标签

2.性能优化:

使用数据库存储大规模题库

添加题目缓存机制

优化界面渲染性能

3.用户体验改进:

添加夜间模式

支持自定义主题

增加快捷键操作

4.教育功能增强:

实现智能出题算法

添加学习进度跟踪

支持题目收藏功能

总结

白泽题库系统通过PyQt5实现了功能完备的桌面端刷题应用,具有以下优势:

  • 跨平台性:基于Python和Qt,可在WindowsMACOS和linux上运行
  • 易用性:直观的界面设计,降低学习成本
  • 扩展性:模块化架构便于功能扩展
  • 数据兼容:支持主流文件格式导入导出

通过本项目的开发,我们不仅掌握了PyQt5的基本使用方法,还深入理解了桌面应用开发的全流程。未来可以进一步探索:

  • 使用PyInstaller打包为独立可执行文件
  • 集成机器学习算法实现智能推荐
  • 开发移动端配套应用

以上就是基于Python实现一个简单的题库与在线考试系统的详细内容,更多关于Python在线考试系统的资料请关注China编程(www.chinasem.cn)其它相关文章!

这篇关于基于Python实现一个简单的题库与在线考试系统的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1154997

相关文章

使用SpringBoot整合Sharding Sphere实现数据脱敏的示例

《使用SpringBoot整合ShardingSphere实现数据脱敏的示例》ApacheShardingSphere数据脱敏模块,通过SQL拦截与改写实现敏感信息加密存储,解决手动处理繁琐及系统改... 目录痛点一:痛点二:脱敏配置Quick Start——Spring 显示配置:1.引入依赖2.创建脱敏

Python使用smtplib库开发一个邮件自动发送工具

《Python使用smtplib库开发一个邮件自动发送工具》在现代软件开发中,自动化邮件发送是一个非常实用的功能,无论是系统通知、营销邮件、还是日常工作报告,Python的smtplib库都能帮助我们... 目录代码实现与知识点解析1. 导入必要的库2. 配置邮件服务器参数3. 创建邮件发送类4. 实现邮件

基于Python构建一个高效词汇表

《基于Python构建一个高效词汇表》在自然语言处理(NLP)领域,构建高效的词汇表是文本预处理的关键步骤,本文将解析一个使用Python实现的n-gram词频统计工具,感兴趣的可以了解下... 目录一、项目背景与目标1.1 技术需求1.2 核心技术栈二、核心代码解析2.1 数据处理函数2.2 数据处理流程

C#之List集合去重复对象的实现方法

《C#之List集合去重复对象的实现方法》:本文主要介绍C#之List集合去重复对象的实现方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C# List集合去重复对象方法1、测试数据2、测试数据3、知识点补充总结C# List集合去重复对象方法1、测试数据

Linux系统中的firewall-offline-cmd详解(收藏版)

《Linux系统中的firewall-offline-cmd详解(收藏版)》firewall-offline-cmd是firewalld的一个命令行工具,专门设计用于在没有运行firewalld服务的... 目录主要用途基本语法选项1. 状态管理2. 区域管理3. 服务管理4. 端口管理5. ICMP 阻断

Linux实现线程同步的多种方式汇总

《Linux实现线程同步的多种方式汇总》本文详细介绍了Linux下线程同步的多种方法,包括互斥锁、自旋锁、信号量以及它们的使用示例,通过这些同步机制,可以解决线程安全问题,防止资源竞争导致的错误,示例... 目录什么是线程同步?一、互斥锁(单人洗手间规则)适用场景:特点:二、条件变量(咖啡厅取餐系统)工作流

SpringBoot读取ZooKeeper(ZK)属性的方法实现

《SpringBoot读取ZooKeeper(ZK)属性的方法实现》本文主要介绍了SpringBoot读取ZooKeeper(ZK)属性的方法实现,强调使用@ConfigurationProperti... 目录1. 在配置文件中定义 ZK 属性application.propertiesapplicati

Java Multimap实现类与操作的具体示例

《JavaMultimap实现类与操作的具体示例》Multimap出现在Google的Guava库中,它为Java提供了更加灵活的集合操作,:本文主要介绍JavaMultimap实现类与操作的... 目录一、Multimap 概述Multimap 主要特点:二、Multimap 实现类1. ListMult

C#实现将Office文档(Word/Excel/PDF/PPT)转为Markdown格式

《C#实现将Office文档(Word/Excel/PDF/PPT)转为Markdown格式》Markdown凭借简洁的语法、优良的可读性,以及对版本控制系统的高度兼容性,逐渐成为最受欢迎的文档格式... 目录为什么要将文档转换为 Markdown 格式使用工具将 Word 文档转换为 Markdown(.

Java反射实现多属性去重与分组功能

《Java反射实现多属性去重与分组功能》在Java开发中,​​List是一种非常常用的数据结构,通常我们会遇到这样的问题:如何处理​​List​​​中的相同字段?无论是去重还是分组,合理的操作可以提高... 目录一、开发环境与基础组件准备1.环境配置:2. 代码结构说明:二、基础反射工具:BeanUtils