基于Python 实现自动组卷评卷考试系统【100010913】
自动组卷评卷考试系统
实验内容
用Python语言编程实现自动组卷评卷考试系统,软件主要功能包括:从题库中随机抽取试题自动组成试卷(满分100分);实现考生考试答题操作界面;实现自动阅卷评分功能;等等。
实验要求
- 题型包括单项选择题、填空题、判断题等等。
- 题库可以采用文本文件、CSV文件或数据库等来实现。
- 要求在源程序中标注必要的注释。
- 要求对程序的使用和运行方法进行必要说明。
- 课程设计要提交程序源代码及附属的测试题库文档等(便于阅卷测试)。
实验器材(设备、元器件)
处理器:Intel Core i5-8300H CPU @ 2.30GHz 2.30GHz
已安装的内存(RAM):8GB
系统类型:64位操作系统,基于x64的处理器
IDE:JetBrains PyCharm (Community Version) 2020.2.1
Python解释器:Python 3.8
实验步骤
系统组成
系统总体上由前端、后端、防作弊演示程序、配置程序、文档结构树图生成程序和数据等部分组成。
框架设计
系统的总体框架与结构如图1所示。
图1 系统总体框架与结构示意图
下面分别介绍各个部分的作用及功能:
FrontEnd.py,主要包括的是前端类,主要实现了注册和答题两个界面和数据调用等功能。
BackEnd.py,主要包括的是后端类,主要实现了检验用户是否存在、账号密码是否正确、添加新用户、产生随机题目序号、获取题目并返回给前端等功能。
AntiCheating.py,主要包括的是防作弊演示程序,主要实现了强行关闭主流文本编辑器和网页浏览器、强制清空系统剪切板等功能。
ProjectTreee.py,主要包括的是防作弊演示,主要实现了产生项目结构树图功能。
Config.py,主要实现了获取当前路径,找寻用户信息表和题库等功能。
data文件夹里有题库question.xlsx,考试结束自动播放的音乐music.mp3,存储管理员账号和测试账号的文本account_file.txt,反映考试成绩的图片fail.png、pass.png、great.png。
具体实现
FrontEnd.py
# -*- coding: utf-8 -*-
# @Time : 2021/1/20 19:55
# @Author : UestcXiye
# @File : FrontEnd.py
# @Software: PyCharm import tkinter as tk
from tkinter import messagebox
from tkinter import scrolledtext
from threading import Timer
from playsound import playsound
from BackEnd import BackEnd, checkAccount, addUser
from Config import *
from pil import Image, ImageTk dataList = BackEnd() # 存储得到的考题 for i in range(5): print(dataList.SingleList[i]['参考答案'])
im = [] # 读取文件
img = [] # 转换格式后 for i in range(5): # 初始化读取的图片列表 im.append(None) img.append(None) class FrontEnd: """ 前端类,完成注册和答题两个界面和数据调用 """ def __init__(self): self.state = STATE_INIT # 有限状态机,完成题目衔接和变化 self.count = 0 # 计数到第几道题了 self.minute = 60 self.second = 0 # 设定考试时间60min self.ans = [] # 存放考生的结果,确认后判断 self.score = 0 # 分数 self.loginWindow = tk.Tk() self.initialLoginWindow(self.loginWindow) def initialLoginWindow(self, loginWindow): """for login""" loginWindow['bg'] = 'skyblue' # background color loginWindow.title('考试系统登陆界面') loginWindow.resizable(width=True, height=True) width = loginWindow.winfo_screenwidth() height = loginWindow.winfo_screenheight() loginWindow.geometry( "400x200+%d+%d" % (width / 2 - 200, height / 2 - 200)) self.varAccount = tk.StringVar() self.varAccount.set('') self.varKey = tk.StringVar() self.varKey.set('') # 创建标签 self.labelAccount = tk.Label( loginWindow, text='用户名:', justify=tk.RIGHT, width=80) self.labelKey = tk.Label( loginWindow, text='密 码:', justify=tk.RIGHT, width=80) self.labelRegister = tk.Label( loginWindow, text='注 册', justify=tk.RIGHT, width=80) # 将标签放到窗口上 self.labelAccount.place(x=20, y=10, width=160, height=40) self.labelKey.place(x=20, y=60, width=160, height=40) # 创建账号文本框,同时设置关联的变量 self.account = tk.Entry( loginWindow, width=80, textvariable=self.varAccount) self.account.place(x=200, y=10, width=160, height=40) # 创建密码文本框 self.key = tk.Entry( loginWindow, show='*', width=80, textvariable=self.varKey) self.key.place(x=200, y=60, width=160, height=40) # 创建按钮组件,同时设置按钮事件处理函数 buttonOk = tk.Button(loginWindow, text='登录', command=self.login) buttonOk.place(x=20, y=140, width=100, height=40) buttonCancel = tk.Button( loginWindow, text='取消', command=self.cancelLogin) buttonCancel.place(x=140, y=140, width=100, height=40) buttonRegister = tk.Button(loginWindow, text='注册', command=self.regist) buttonRegister.place(x=260, y=140, width=100, height=40) # make Esc exit the program loginWindow.bind('<Escape>', lambda e: loginWindow.destroy()) # 启动消息循环 loginWindow.mainloop() def login(self): """ 获取用户名和密码 """ name = self.account.get() passwd = self.key.get() nameList, passwordList = checkAccount('account_file.txt') # for test for i in range(len(nameList)): if name == nameList[i]: if passwd == passwordList[i]: tk.messagebox.showinfo(title='提示', message='登录成功!') self.loginWindow.destroy() self.mainWindow = tk.Tk() self.initialMainWindow(self.mainWindow) return tk.messagebox.showerror('Python tk', message='账号或密码错误!') def cancelLogin(self): """ 清空用户输入的用户名和密码 """ self.varAccount.set('') self.varKey.set('') def regist(self): name = self.account.get() passwd = self.key.get() userNameList, userPasswordList = checkAccount('account_file.txt') if not userNameList or not userPasswordList: addUser('account_file.txt', name, passwd) return for userName in userNameList: if name == userName: tk.messagebox.showerror('Python tk', message='已有该用户名!') registerSuccessful = addUser('account_file.txt', name, passwd) if registerSuccessful: messagebox.showinfo('提示信息', message='注册成功!') def initialMainWindow(self, mainWindow): """ initialize window and the window settings""" self.width = mainWindow.winfo_screenwidth() self.height = mainWindow.winfo_screenheight() print('[Function: initialMainWindow]') mainWindow.geometry("%dx%d" % (self.width, self.height)) mainWindow['bg'] = 'skyblue' # background color mainWindow.title('考试系统考试界面') mainWindow.resizable(width=True, height=True) mainWindow.protocol('WM_DELETE_WINDOW', self.closeMainWindow) self.setMenu(mainWindow) # make Esc exit the program mainWindow.bind('<Escape>', lambda e: mainWindow.destroy()) self.totalCount = dataList.Single.totalNum + \ dataList.Multi.totalNum + dataList.Judge.totalNum self.showInitFsm() self.watchDog() mainWindow.mainloop() def showInitFsm(self): nextState = STATE_SINGLE print('[Function: Init_fsm] startup') self.varScore = tk.StringVar() # 已获得分数 self.varScore.set(str(self.score) + '\100') self.showScoreName = tk.Label(self.mainWindow, text='已获得分数: ', width=150, # 设置label的宽度:30 height=50, # 设置label的高度:10 justify='left', # 设置文本对齐方式:左对齐 anchor='nw', # 设置文本在label的方位:西北方位 font=('微软雅黑', 18), # 设置字体:微软雅黑,字号:18 fg='white', # 设置前景色:白色 bg='grey', # 设置背景色:灰色 ) self.showScoreName.place(x=10, y=10, width=150, height=50) self.showScore = tk.Label(self.mainWindow, textvariable=self.varScore) self.showScore.place(x=10, y=70, width=150, height=50) self.varTimeLft = tk.StringVar() # self.varTimeLft.set(str(min) + '分' + str(sec) + '秒') self.timeLeft = tk.Label(self.mainWindow, textvariable=self.varTimeLft) self.timeLeft.place(x=self.width - 200, y=70, width=150, height=50) # 剩余时间见函数 watchDog # self.watchDog(10, 00) # 考试时间10min self.showTimeLeft = tk.Label(self.mainWindow, text='剩余时间', # 设置文本内容 width=150, # 设置label的宽度:30 height=50, # 设置label的高度:10 justify='left', # 设置文本对齐方式:左对齐 anchor='ne', # 设置文本在label的方位:西北方位 font=('微软雅黑', 18), # 设置字体:微软雅黑,字号:18 fg='white', # 设置前景色:白色 bg='grey', # 设置背景色:灰色 padx=20, # 设置x方向内边距:20 pady=10) # 设置y方向内边距:10 self.showTimeLeft.place(x=self.width - 200, y=10, width=150, height=60) self.varButtonA = tk.StringVar() self.varButtonA.set( 'A. ' + str(dataList.SingleList[self.count % 10]['A'])) self.varButtonB = tk.StringVar() self.varButtonB.set( 'B. ' + str(dataList.SingleList[self.count % 10]['B'])) self.varButtonC = tk.StringVar() self.varButtonC.set( 'C. ' + str(dataList.SingleList[self.count % 10]['C'])) self.varButtonD = tk.StringVar() self.varButtonD.set( 'D. ' + str(dataList.SingleList[self.count % 10]['D'])) self.varButtonE = tk.StringVar() self.varButtonE.set('') self.buttonA = tk.Button(self.mainWindow, textvariable=self.varButtonA, command=self.buttonAFsm) self.buttonB = tk.Button(self.mainWindow, textvariable=self.varButtonB, command=self.buttonBFsm) self.buttonC = tk.Button(self.mainWindow, textvariable=self.varButtonC, command=self.buttonCFsm) self.buttonD = tk.Button(self.mainWindow, textvariable=self.varButtonD, command=self.buttonDFsm) self.buttonOK = tk.Button(self.mainWindow, text='确认', command=self.buttonOKFsm) # 确认按钮,确认不再更改答案 self.buttonA.place(x=100, y=400, width=750, height=50) self.buttonB.place(x=100, y=500, width=750, height=50) self.buttonC.place(x=100, y=600, width=750, height=50) self.buttonD.place(x=100, y=700, width=750, height=50) self.buttonOK.place(x=1000, y=400, width=300, height=50) self.varChoice = tk.StringVar() self.varChoice.set(list2str(self.ans)) # 显示考生选择的选项 self.showChoice = tk.Label( self.mainWindow, textvariable=self.varChoice) self.showChoice.place(x=1000, y=600, width=150, height=50) self.subject = scrolledtext.ScrolledText( self.mainWindow, relief="solid") self.subject.place(x=self.width / 3, y=10) self.subject.insert('end', str(self.count + 1) + '. ' + dataList.SingleList[self.count]['题目内容'] + '\n') self.count = 0 print('[Function: Init_fsm] complicated') self.state = nextState def buttonAFsm(self): print(' [Event: buttonA clicked]') if self.state == STATE_SINGLE: # 单选 self.ans = [] self.ans.append('A') elif self.state == STATE_MULTI: # 多选 if 'A' not in self.ans: self.ans.append('A') self.ans = sorted(self.ans) else: self.ans.remove('A') else: # 判断题 self.ans = [] self.ans.append('对') self.varChoice.set(list2str(self.ans)) def buttonBFsm(self): print(' [Event: buttonB clicked]') if self.state == STATE_SINGLE: # 单选 self.ans = [] self.ans.append('B') elif self.state == STATE_MULTI: # 多选 if 'B' not in self.ans: self.ans.append('B') self.ans = sorted(self.ans) else: self.ans.remove('B') sorted(self.ans) else: self.ans = [] self.ans.append('对') self.varChoice.set(list2str(self.ans)) def buttonCFsm(self): print(' [Event: buttonC clicked]') if self.state == STATE_SINGLE: # 单选 self.ans = [] self.ans.append('C') elif self.state == STATE_MULTI: # 多选 if 'C' not in self.ans: self.ans.append('C') self.ans = sorted(self.ans) else: self.ans.remove('C') sorted(self.ans) else: # 判断 self.ans = [] self.ans.append('错') self.varChoice.set(list2str(self.ans)) def buttonDFsm(self): print(' [Event: buttonD clicked]') if self.state == STATE_SINGLE: # 单选 self.ans = [] self.ans.append('D') elif self.state == STATE_MULTI: # 多选 if 'D' not in self.ans: self.ans.append('D') self.ans = sorted(self.ans) else: self.ans.remove('D') sorted(self.ans) else: # 判断 self.ans = [] self.ans.append('错') self.varChoice.set(list2str(self.ans)) def buttonEFsm(self): print(' [Event: buttonE clicked]') if self.state == STATE_SINGLE: # 单选 self.ans = [] self.ans.append('E') elif self.state == STATE_MULTI: # 多选 if 'E' not in self.ans: self.ans.append('E') self.ans = sorted(self.ans) else: self.ans.remove('E') sorted(self.ans) else: # 判断 self.ans = [] self.ans.append('错') self.varChoice.set(list2str(self.ans)) def buttonOKFsm(self): """ 确认按钮,点击后进入下一状态 """ print(' [Event: buttonOK clicked]') self.score += self.checkAns() self.varScore.set(str(self.score) + '/100') # 显示得分 self.count = self.count + 1 # 下一题 self.varChoice.set('') # 清空显示的考生选项,准备下一题 self.ans = [] # 清空内部存储的考生选项,准备下一题 if self.state == STATE_SINGLE: self.showSingleFsm() elif self.state == STATE_MULTI: self.showMultiFsm() elif self.state == STATE_JUDGE: self.showJudgeFsm() else: # 结束,分数不再变动 self.showDoneFsm() def checkAns(self) -> int: """ 检查结果,返回本题得分 """ if self.state == STATE_SINGLE: print(' [Debug: your choice:] ' + str(self.ans)) if list2str( self.ans) == dataList.SingleList[self.count % 10]['参考答案']: # self.score = self.score + 3 # 本题得分 return 3 else: return 0 elif self.state == STATE_MULTI: print(' [Debug: your choice:] ' + str(self.ans)) if list2str( self.ans) == dataList.MultiList[self.count % 10]['参考答案']: # self.score += 5 # 本题得分 return 5 else: return 0 else: print(' [Debug: your choice:] ' + str(self.ans)) if list2str( self.ans) == dataList.JudgeList[self.count % 10]['参考答案']: # self.score += 2 # 本题得分 return 2 else: return 0 def updateSubject(self, listName): self.subject.delete(0.0, tk.END) self.subject.insert('end', str(self.count + 1) + '. ' + listName[self.count % 10]['题目内容'] + '\n') self.varButtonA.set( 'A. ' + str(listName[self.count % 10]['A'])) self.varButtonB.set( 'B. ' + str(listName[self.count % 10]['B'])) self.varButtonC.set( 'C. ' + str(listName[self.count % 10]['C'])) self.varButtonD.set( 'D. ' + str(listName[self.count % 10]['D'])) if self.state == STATE_MULTI: self.varButtonE.set( 'E. ' + str(listName[self.count % 10]['E'])) def showSingleFsm(self): if self.count < self.totalCount / 3 - 1: nextState = STATE_SINGLE else: nextState = STATE_MULTI self.buttonE = tk.Button(self.mainWindow, textvariable=self.varButtonE, command=self.buttonEFsm) self.buttonA.place(x=100, y=400, width=750, height=50) self.buttonB.place(x=100, y=480, width=750, height=50) self.buttonC.place(x=100, y=560, width=750, height=50) self.buttonD.place(x=100, y=640, width=750, height=50) self.buttonE.place(x=100, y=720, width=750, height=50) self.updateSubject(dataList.SingleList) self.state = nextState def showMultiFsm(self): if self.totalCount / 3 <= self.count < 2 * self.totalCount / 3: nextState = STATE_MULTI else: nextState = STATE_JUDGE self.buttonA.destroy() self.buttonB.destroy() self.buttonC.destroy() self.buttonD.destroy() self.buttonE.destroy() self.buttonTrue = tk.Button(self.mainWindow, text='对', command=self.buttonAFsm) self.buttonFalse = tk.Button(self.mainWindow, text='错', command=self.buttonEFsm) self.buttonTrue.place(x=100, y=400, width=750, height=50) self.buttonFalse.place(x=100, y=600, width=750, height=50) self.updateSubject(dataList.MultiList) # 刷新题目和选项 self.state = nextState def showJudgeFsm(self): print('total count: ', self.totalCount) if self.count < self.totalCount: nextState = STATE_JUDGE else: nextState = STATE_DONE self.subject.delete(0.0, tk.END) # 清空上一题 self.subject.insert('end', str(self.count + 1) + '. ' + dataList.JudgeList[self.count % 10]['题目内容'] + '\n') self.state = nextState def showDoneFsm(self): """ 结束状态 """ # 清除所有无用控件 self.buttonTrue.destroy() self.buttonFalse.destroy() self.buttonOK.destroy() self.showChoice.destroy() self.subject.destroy() # 播放音乐 playsound(getCurrentPath() + DataPath + 'music.mp3', block=False) # 计时结束,清零 self.timeCount.cancel() # self.varTimeLft.set('0:00') self.showScoreName = tk.Label(self.mainWindow, text='最终得分: ', width=150, # 设置label的宽度:30 height=50, # 设置label的高度:10 justify='left', # 设置文本对齐方式:左对齐 anchor='nw', # 设置文本在label的方位:西北方位 font=('微软雅黑', 18), # 设置字体:微软雅黑,字号:18 fg='white', # 设置前景色:白色 bg='grey', # 设置背景色:灰色 ) self.showScoreName.place(x=10, y=10, width=150, height=50) # 加载图像 global im global img if self.score < 60: im[0] = Image.open(getCurrentPath() + DataPath + "fail.png") img[0] = ImageTk.PhotoImage(im[0]) imLabel = tk.Label(self.mainWindow, image=img[0]).pack() elif 60 <= self.score <= 85: im[1] = Image.open(getCurrentPath() + DataPath + "pass.png") img[1] = ImageTk.PhotoImage(im[1]) imLabel = tk.Label(self.mainWindow, image=img[1]).pack() else: im[2] = Image.open(getCurrentPath() + DataPath + "great.png") img[2] = ImageTk.PhotoImage(im[2]) imLabel = tk.Label(self.mainWindow, image=img[2]).pack() def setMenu(self, window): """create a menu bar with Exit command and version info""" menubar = tk.Menu(window) filemenu = tk.Menu(menubar, tearoff=0) filemenu.add_command(label="Exit", command=window.destroy) infoMenu = tk.Menu(menubar, tearoff=0) infoMenu.add_command(label="Version Info", command=self.menuInfo) menubar.add_cascade(label="File", menu=filemenu) menubar.add_cascade(label="Info", menu=infoMenu) window.config(menu=menubar) def menuInfo(self): messagebox.showinfo( 'info', 'Created By UestcXiye \n version 1.0') def watchDog(self): """ 定时程序,考试时间最多一小时,结束终止答题,显示分数,播放音乐 """ timeLeft = 60 * self.minute + self.second timeLeft -= 1 self.second = self.second - 1 if self.second < 0: self.minute = self.minute - 1 self.second = 59 if self.minute < 0 or timeLeft == 0: self.state = STATE_DONE playsound( getCurrentPath() + DataPath + 'music.mp3', block=False ) # 倒计时结束,播放提示音乐。 self.showDoneFsm() self.varTimeLft.set(str(self.minute) + ':' + str(self.second)) self.timeCount = Timer(1, self.watchDog, ()) self.timeCount.start() # 计时器启动 def closeMainWindow(self): """ to check if you really wanna exit """ ans = messagebox.askyesno(title='Quit', message='要关闭窗口吗?您所做的修改不会保存') if ans: self.mainWindow.destroy() else: pass if __name__ == '__main__': test = FrontEnd()
BackEnd.py
# -*- coding: utf-8 -*-
# @Time : 2021/1/20 20:12
# @Author : UestcXiye
# @File : BackEnd.py
# @Software: PyCharm import pandas as pd
import random
from Config import * def checkAccount(filename) -> tuple: """ 检验用户是否存在,账号密码是否正确 """ path = getCurrentPath() + DataPath + filename fid = open(path, 'r+') accountList = [] userNameList, userPasswordList = [], [] line = fid.readlines() for child in line: # print('[Function checkAccount]: ' + child) # if not (line.startswith("@")): # 注释行开头为@ accountList.append(child.strip("\n").split('\t')) # print(accountList) for name, password in accountList: userNameList.append(name) userPasswordList.append(password) # print(userNameList) # print(userPasswordList) fid.close() return userNameList, userPasswordList def addUser(filename, userName: str, userPassword: str) -> int: """ 添加新用户,在用户名不重读的情况下才会调用 """ path = getCurrentPath() + DataPath + filename txtfile = open(path, 'a') data = '\n' + userName + '\t' + userPassword txtfile.write(data) txtfile.close() return 1 class SingleChoiceSubject: def __init__(self): self.scorePer = 3 # 每道题的分值 self.totalNum = 10 # 总共10道单选 self.subjectList = {} # 存放所有题目信息 self.path = getCurrentPath() + DataPath + 'question.xlsx' self.df = pd.read_excel(self.path, sheet_name='单选') self.tempList = [] # 存储一行信息 self.randList = [] # 存储已经选用的题目,防止随机题目 def generateRand(self): """ 产生随机题目序号 """ count = 0 while count < self.totalNum: randCount = random.randint(0, 519) # 共520道单选题 if randCount not in self.randList: self.randList.append(randCount) count = count + 1 else: continue def getData(self): """ 获取题目,返回数据给前端 """ self.generateRand() count = 0 for randCount in self.randList: # 还有记得,是不是要canvas上面分布这些按钮,然后随着canvas销毁而消失 self.subjectList[count] = {} self.subjectList[count]['题目内容'] = self.df['题目内容'][randCount] self.subjectList[count]['A'] = self.df['A'][randCount] self.subjectList[count]['B'] = self.df['B'][randCount] self.subjectList[count]['C'] = self.df['C'][randCount] self.subjectList[count]['D'] = self.df['D'][randCount] self.subjectList[count]['参考答案'] = self.df['参考答案'][randCount] count = count + 1 return self.subjectList class MultiChoiceSubject: def __init__(self): self.scorePer = 5 # 每道题的分值 self.totalNum = 10 # 总共10道单选 self.subjectList = {} # 存放所有题目信息 self.path = getCurrentPath() + DataPath + 'question.xlsx' self.df = pd.read_excel(self.path, sheet_name='多选') self.randList = [] def generateRand(self): """ 产生随机题目序号 """ count = 0 while count < self.totalNum: randCount = random.randint(0, 265) # 共520道单选题 if randCount not in self.randList: self.randList.append(randCount) count = count + 1 else: continue def getData(self): """ 获取题目,返回数据给前端 """ self.generateRand() count = 0 for randCount in self.randList: # 还有记得,是不是要canvas上面分布这些按钮,然后随着canvas销毁而消失 self.subjectList[count] = {} self.subjectList[count]['题目内容'] = self.df['题目内容'][randCount] self.subjectList[count]['A'] = self.df['A'][randCount] self.subjectList[count]['B'] = self.df['B'][randCount] self.subjectList[count]['C'] = self.df['C'][randCount] self.subjectList[count]['D'] = self.df['D'][randCount] self.subjectList[count]['E'] = self.df['E'][randCount] self.subjectList[count]['参考答案'] = self.df['参考答案'][randCount] count = count + 1 return self.subjectList class JudgeSubject: def __init__(self): self.scorePer = 2 # 每道题的分值 self.totalNum = 10 # 总共10道单选 self.subjectList = {} # 存放所有题目信息 self.path = getCurrentPath() + DataPath + 'question.xlsx' self.df = pd.read_excel(self.path, sheet_name='判断') self.randList = [] def generateRand(self): """ 产生随机题目序号 """ count = 0 while count < self.totalNum: randCount = random.randint(0, 362) # 共520道单选题 if randCount not in self.randList: self.randList.append(randCount) count = count + 1 else: continue def getData(self): """ 获取题目,返回数据给前端 """ self.generateRand() count = 0 for randCount in self.randList: self.subjectList[count] = {} self.subjectList[count]['题目内容'] = self.df['题目内容'][randCount] self.subjectList[count]['参考答案'] = self.df['参考答案'][randCount] count = count + 1 return self.subjectList class BackEnd: """ 与前端的数据接口 """ def __init__(self): self.Single = SingleChoiceSubject() self.Multi = MultiChoiceSubject() self.Judge = JudgeSubject() self.SingleList = self.Single.getData() self.MultiList = self.Multi.getData() self.JudgeList = self.Judge.getData() def test(self): print("SingleList:", self.SingleList) print("MultiList:", self.MultiList) print("JudgeList:", self.JudgeList) if __name__ == '__main__': test = BackEnd() test.test() print(test.SingleList[0]['A']) print(test.MultiList[2]['参考答案']) print(test.JudgeList[9]['题目内容']) print(type(test.MultiList[2]['参考答案'])) print(test.SingleList[2]['参考答案']) if test.SingleList[2]['参考答案'] == 'A': print('aaaa') if test.SingleList[2]['参考答案'] == 'B': print('bb') if test.SingleList[2]['参考答案'] == 'C': print('cc') if test.SingleList[2]['参考答案'] == 'D': print('dd')
AntiCheating.py
# -*- coding: utf-8 -*-
# @Time : 2021/1/20 18:34
# @Author : UestcXiye
# @File : AntiCheating.py
# @Software: PyCharm import os
import time
import tkinter
import threading
import ctypes
import psutil root = tkinter.Tk() root.title('防作弊演示') # 窗口初始大小和位置
root.geometry('250x80+300+100') # 不允许改变窗口大小
root.resizable(False, False) ban = tkinter.IntVar(root, 0) def funcBan(): while ban.get() == 1: # 强行关闭主流文本编辑器和网页浏览器 for pid in psutil.pids(): try: p = psutil.Process(pid) exeName = os.path.basename(p.exe()).lower() if exeName in ('notepad.exe', 'winword.exe', 'wps.exe', 'wordpad.exe', 'iexplore.exe', 'chrome.exe', 'qqbrowser.exe', '360chrome.exe', '360se.exe', 'sogouexplorer.exe', 'firefox.exe', 'opera.exe', 'maxthon.exe', 'netscape.exe', 'baidubrowser.exe', '2345Explorer.exe'): p.kill() except: pass # 清空系统剪切板 ctypes.windll.user32.OpenClipboard(None) ctypes.windll.user32.EmptyClipboard() ctypes.windll.user32.CloseClipboard() time.sleep(1) def start(): ban.set(1) t = threading.Thread(target=funcBan) t.start() buttonStart = tkinter.Button(root, text='开始考试', command=start)
buttonStart.place(x=20, y=10, width=100, height=20) def stop(): ban.set(0) buttonStop = tkinter.Button(root, text='结束考试', command=stop)
buttonStop.place(x=130, y=10, width=100, height=20)
# 模拟用,开启考试模式以后,所有内容都不再允许复制
entryMessage = tkinter.Entry(root)
entryMessage.place(x=10, y=40, width=230, height=20)
root.mainloop()
ProjectTree.py
# -*- coding: utf-8 -*-
# @Time : 2021/1/20 21:01
# @Author : UestcXiye
# @File : ProjectTree.py
# @Software: PyCharm from pathlib import Path tree_str = '' def generate_tree(pathname, n=0): """ 产生项目结构树图 """ global tree_str if pathname.is_file(): tree_str += ' |' * n + '-' * 4 + pathname.name + '\n' elif pathname.is_dir(): tree_str += ' |' * n + '-' * 4 + \ str(pathname.relative_to(pathname.parent)) + '\\' + '\n' for cp in pathname.iterdir(): generate_tree(cp, n + 1) if __name__ == '__main__': generate_tree(Path.cwd()) print(tree_str)
Config.py
# -*- coding: utf-8 -*-
# @Time : 2021/1/20 20:23
# @Author : UestcXiye
# @File : Config.py
# @Software: PyCharm import os DataPath = '\\' + 'data' + '\\' STATE_INIT = 1
STATE_SINGLE = 2
STATE_MULTI = 3
STATE_JUDGE = 4
STATE_DONE = 5 def getCurrentPath(): """ 获取当前路径,找寻用户信息表和题库 """ path = os.getcwd() # 当前工作路径 # path = os.path.abspath('..') #当前工作目录的父目录路径 return path def list2str(changList) -> str: """ 为tkinter的varString显示处理准备,可以显示考生选择的选项 """ res = '' for index in range(len(changList)): res = res + str(changList[index]) return res if __name__ == '__main__': from pil import Image, ImageTk pilImage = Image.open(getCurrentPath() + DataPath + "fail.png") img = pilImage.resize((600, 500), Image.ANTIALIAS) tkImage = ImageTk.PhotoImage(image=img) print(pilImage[0])
数据及项目演示
数据
account_file.txt的内容如图2所示。
图2 account_file.txt的内容示意图
第一列为账号名,第二列为账号密码。其中,管理员账号admin,对应密码123456;测试账号test1和test2,密码和账号一样。
题库question.xlsx包含单选题、多选题和判断题,它的内容如图3所示。
图3 题库question.xlsx的内容示意图
反映考试成绩的图片fail.png、pass.png、great.png分别如图4、图5、图6所示。
图4 fail.png
图5 pass.png
图6 great.png
项目演示
运行FrontEnd.py,首先进入考试系统的登陆界面,输入用户名和密码,点击登录,如图7所示。
图7 考试系统登录界面
提示登录成功后,进入考试系统答题界面,如图8所示。
图8 考试系统答题界面
答题完成后,显示成绩,播放音乐,如图9所示。
图9 考试系统显示成绩界面
总结及心得体会:
本次课程设计完成了用Python语言编程实现自动组卷评卷考试系统,项目主要实现了从题库中随机抽取试题自动组成试卷、实现考生考试答题操作界面、自动阅卷评分、防作弊演示等功能,加强了Python语言的编程能力。
对本实验过程及方法、手段的改进建议:
前端界面改用更高级的模块,如graphic;
采用遗传算法甚至粒子群算法进行自动组卷,使每次生成的试卷难度基本一致;
利用数据库存储题库;
更加完善代码注释,提高代码的可读性。
♻️ 资源
大小: 5.68MB
➡️ 资源下载:https://download.csdn.net/download/s1t16/87484804
注:如当前文章或代码侵犯了您的权益,请私信作者删除!
基于Python 实现自动组卷评卷考试系统【100010913】相关推荐
- Python版自动组卷评卷考试系统,具有考试定时、自动组卷、客观题自动判卷、自动评分和考试界面设计功能
一.实验项目名称: 自动组卷评卷考试系统 二.实验内容 用Python语言编程实现自动组卷评卷考试系统,软件主要功能包括:从题库中随机抽取试题自动组成试卷(满分100分):实现考生考试答题操作界面:实 ...
- Python课程设计:Python语言实现自动组卷评卷考试系统
Python实现自动组卷评卷考试系统 课程设计要求 一.各个模块介绍及实现 1. Configure.py 2. File_Texture_Tree.py 3. 后端:Test_Problem_Con ...
- 基于遗传算法实现自动组卷
转自 http://www.cnblogs.com/liulang/articles/1614311.html 1 遗传算法介绍 1.1 遗传算法概要 遗传算法是模拟达尔文的遗传选择和自然淘汰的生物 ...
- java实现自动组卷要用什么算法_基于Java的自动组卷系统的实现
基于 的 自动组卷系统的实现李桂玲 四平职业 大学计葬机工程 系 引言 考试作为教学测评的最主要的手段 , 其规范性 . 科学性以及考试工作的组织 . 管理等 , 直接关系到教学测评的准确性和客观性 ...
- 组卷与考试系统_题库添加选择题模块
action部分: String questionkindId; String questionCon; String choicea; String choiceb; String choicec; ...
- 基于python的在线考试系统-基于 Python 的电子教室和在线考试系统源代码
Python 是近年倍受推崇的语言,这里介绍的是两个与教学相关的开源项目.一个是用于计算机教室管理的电子教室系统,第二个是与之关联的在线考试系统,当然,这个系统也可以单列出来.这两个项目还只是个雏形, ...
- 随机组卷python_一种自动组卷算法的实现
摘要:结合遗传算法的原理和思想,对考试自动出题组卷的问题进行了深入的研究,找到了一种获得与考试试题控制指标符合的试题模型的解决方法. 关键词:遗传算法:全局寻优:自动化组卷 中图分类号:TP18文献标 ...
- 遗传算法在自动组卷中的应用
遗传算法 遗传算法(Genetic Algorithm)是一种模拟自然界的进化规律-优胜劣汰演化来的随机搜索算法,其在解决多种约束条件下的最优解这类问题上具有优秀的表现. 1. 基本概念 在遗传算法中 ...
- python自动组卷系统_基于遗传算法(C#编写)的智能组卷系统优化
原创 guodongwe1991 机器学习算法与Python学习 2016-08-25 最近由于项目的需要,基于.Net 4.0框架和WPF开发window的客户端(开发环境为win7 旗舰版:Vis ...
最新文章
- 2021年大数据ELK(十九):使用FileBeat采集Kafka日志到Elasticsearch
- Nginx负载均衡+tomcat+session共享
- 分享这两年从事Linux系统运维行业的感受
- VTK:Points之PointOccupancy
- html5jqueryl轮播图,基于JQuery的实现图片轮播效果(焦点图)
- 一文读懂VictoriaMetrics集群方案
- Struts2 result type(结果类型)
- 电脑分辨率设置工具_打印不求人:我猜你并不会设置“分辨率”!
- Docker生产环境配置——设置direct-lvm模式
- win7精简_微软从未公开的win10版本,3GB+极度精简,老爷机总算有救了!
- 自定义Behavior
- 股票交易接口的开发工具?
- bzoj4567【SCOI2016】背单词
- linux下C语言抓包程序,带图形界面
- (附源码)计算机毕业设计ssm党史知识竞赛系统
- PPT学习整理(三)合并形状
- MPC、LMPC、LEMPC、NMPC、 NEMPC
- 测试案例:如何测试一把椅子?
- 水位报警器c语言程序,简易水位报警器制作_制作简单的水满报警器 - 全文
- 单片机按键设计的四个方案
热门文章
- 一个男人在婚礼上的致辞——关于爱情,关于婚姻
- jQuery实现html的table表的th和td标签的显示和隐藏
- 他们说找合伙人就像找女朋友!
- 论文阅读《Revisiting Stereo Depth Estimation From a Sequence-to-Sequence Perspective with Transformers》
- 图形学基础 (五) 着色及着色频率
- mysql数据库中的存储引擎
- 解密游戏推荐系统的建设之路
- MBG 实战 零基础
- MyBatis Generator(MBG)使用
- 普通人的计算机水平,鉴于计算机这么火,普通人谈谈普通人的看法,仅供参考...