自动组卷评卷考试系统

实验内容

用Python语言编程实现自动组卷评卷考试系统,软件主要功能包括:从题库中随机抽取试题自动组成试卷(满分100分);实现考生考试答题操作界面;实现自动阅卷评分功能;等等。

实验要求

  1. 题型包括单项选择题、填空题、判断题等等。
  2. 题库可以采用文本文件、CSV文件或数据库等来实现。
  3. 要求在源程序中标注必要的注释。
  4. 要求对程序的使用和运行方法进行必要说明。
  5. 课程设计要提交程序源代码及附属的测试题库文档等(便于阅卷测试)。

实验器材(设备、元器件)

处理器: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 系统总体框架与结构示意图

下面分别介绍各个部分的作用及功能:

  1. FrontEnd.py,主要包括的是前端类,主要实现了注册和答题两个界面和数据调用等功能。

  2. BackEnd.py,主要包括的是后端类,主要实现了检验用户是否存在、账号密码是否正确、添加新用户、产生随机题目序号、获取题目并返回给前端等功能。

  3. AntiCheating.py,主要包括的是防作弊演示程序,主要实现了强行关闭主流文本编辑器和网页浏览器、强制清空系统剪切板等功能。

  4. ProjectTreee.py,主要包括的是防作弊演示,主要实现了产生项目结构树图功能。

  5. Config.py,主要实现了获取当前路径,找寻用户信息表和题库等功能。

  6. 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语言的编程能力。

对本实验过程及方法、手段的改进建议:

  1. 前端界面改用更高级的模块,如graphic;

  2. 采用遗传算法甚至粒子群算法进行自动组卷,使每次生成的试卷难度基本一致;

  3. 利用数据库存储题库;

  4. 更加完善代码注释,提高代码的可读性。

♻️ 资源

大小: 5.68MB
➡️ 资源下载:https://download.csdn.net/download/s1t16/87484804
注:如当前文章或代码侵犯了您的权益,请私信作者删除!

基于Python 实现自动组卷评卷考试系统【100010913】相关推荐

  1. Python版自动组卷评卷考试系统,具有考试定时、自动组卷、客观题自动判卷、自动评分和考试界面设计功能

    一.实验项目名称: 自动组卷评卷考试系统 二.实验内容 用Python语言编程实现自动组卷评卷考试系统,软件主要功能包括:从题库中随机抽取试题自动组成试卷(满分100分):实现考生考试答题操作界面:实 ...

  2. Python课程设计:Python语言实现自动组卷评卷考试系统

    Python实现自动组卷评卷考试系统 课程设计要求 一.各个模块介绍及实现 1. Configure.py 2. File_Texture_Tree.py 3. 后端:Test_Problem_Con ...

  3. 基于遗传算法实现自动组卷

    转自 http://www.cnblogs.com/liulang/articles/1614311.html 1  遗传算法介绍 1.1 遗传算法概要 遗传算法是模拟达尔文的遗传选择和自然淘汰的生物 ...

  4. java实现自动组卷要用什么算法_基于Java的自动组卷系统的实现

    基于 的 自动组卷系统的实现李桂玲 四平职业 大学计葬机工程 系 引言 考试作为教学测评的最主要的手段 , 其规范性 . 科学性以及考试工作的组织 . 管理等 , 直接关系到教学测评的准确性和客观性 ...

  5. 组卷与考试系统_题库添加选择题模块

    action部分: String questionkindId; String questionCon; String choicea; String choiceb; String choicec; ...

  6. 基于python的在线考试系统-基于 Python 的电子教室和在线考试系统源代码

    Python 是近年倍受推崇的语言,这里介绍的是两个与教学相关的开源项目.一个是用于计算机教室管理的电子教室系统,第二个是与之关联的在线考试系统,当然,这个系统也可以单列出来.这两个项目还只是个雏形, ...

  7. 随机组卷python_一种自动组卷算法的实现

    摘要:结合遗传算法的原理和思想,对考试自动出题组卷的问题进行了深入的研究,找到了一种获得与考试试题控制指标符合的试题模型的解决方法. 关键词:遗传算法:全局寻优:自动化组卷 中图分类号:TP18文献标 ...

  8. 遗传算法在自动组卷中的应用

    遗传算法 遗传算法(Genetic Algorithm)是一种模拟自然界的进化规律-优胜劣汰演化来的随机搜索算法,其在解决多种约束条件下的最优解这类问题上具有优秀的表现. 1. 基本概念 在遗传算法中 ...

  9. python自动组卷系统_基于遗传算法(C#编写)的智能组卷系统优化

    原创 guodongwe1991 机器学习算法与Python学习 2016-08-25 最近由于项目的需要,基于.Net 4.0框架和WPF开发window的客户端(开发环境为win7 旗舰版:Vis ...

最新文章

  1. 2021年大数据ELK(十九):使用FileBeat采集Kafka日志到Elasticsearch
  2. Nginx负载均衡+tomcat+session共享
  3. 分享这两年从事Linux系统运维行业的感受
  4. VTK:Points之PointOccupancy
  5. html5jqueryl轮播图,基于JQuery的实现图片轮播效果(焦点图)
  6. 一文读懂VictoriaMetrics集群方案
  7. Struts2 result type(结果类型)
  8. 电脑分辨率设置工具_打印不求人:我猜你并不会设置“分辨率”!
  9. Docker生产环境配置——设置direct-lvm模式
  10. win7精简_微软从未公开的win10版本,3GB+极度精简,老爷机总算有救了!
  11. 自定义Behavior
  12. 股票交易接口的开发工具?
  13. bzoj4567【SCOI2016】背单词
  14. linux下C语言抓包程序,带图形界面
  15. (附源码)计算机毕业设计ssm党史知识竞赛系统
  16. PPT学习整理(三)合并形状
  17. MPC、LMPC、LEMPC、NMPC、 NEMPC
  18. 测试案例:如何测试一把椅子?
  19. 水位报警器c语言程序,简易水位报警器制作_制作简单的水满报警器 - 全文
  20. 单片机按键设计的四个方案

热门文章

  1. 一个男人在婚礼上的致辞——关于爱情,关于婚姻
  2. jQuery实现html的table表的th和td标签的显示和隐藏
  3. 他们说找合伙人就像找女朋友!
  4. 论文阅读《Revisiting Stereo Depth Estimation From a Sequence-to-Sequence Perspective with Transformers》
  5. 图形学基础 (五) 着色及着色频率
  6. mysql数据库中的存储引擎
  7. 解密游戏推荐系统的建设之路
  8. MBG 实战 零基础
  9. MyBatis Generator(MBG)使用
  10. 普通人的计算机水平,鉴于计算机这么火,普通人谈谈普通人的看法,仅供参考...