疯狂刷题python版 | 使用PySide6自制刷题软件【源码+解析】

2024-06-21 09:20

本文主要是介绍疯狂刷题python版 | 使用PySide6自制刷题软件【源码+解析】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

疯狂刷题python版 | 使用PySide6自制刷题软件【源码+解析】

    • 一、前言
    • 二、思考
    • 三、软件设计
    • 四、软件实现
      • (一)使用QWebEngineView控件通过JavaScript代码和chrome内核进行数据交互和逻辑控制
      • (二)用户分别通过浏览器 GUI和PySide6 GUI进行操作
      • (三)使用PySide6 GUI获取用户计算机本地资源
    • 五、遇到问题及解决方案
      • (一)如何把excel数据转换成json数据?
      • (二)如何向chrome内核传递带大量json数据参数的JavaScript代码?
      • (三)如何区分不同的题库,同样的题库只保存一份?
      • (四)如何保存来自浏览器GUI的答题数据,同样的答题记录只保存一份,并与对应的题库关联?
    • 六、完整源代码和可执行程序

一、前言

考试作为一种衡量个人知识和技能水平的评估方式,已融入了我们的工作和生活。求学时期,从幼儿园到高中、大学,每个教育阶段都免不了考试;工作前后,考试以笔试、面试等形式频繁出现,并作为企业选拔和晋升的主要依据;生活中,我们的驾驶证,一些行业专业资格,也是通过考试获取。

在此过程中,复习题库成了大家通过考试的重要法宝,但大多数行业、政企提供的复习题库多是word、excel等文档形式,复习起来效率不高,由此催生出了各类刷题网站、软件、手机app等工具。这些工具提供了导入题库、保存历史答题记录、错题本、标记重点、答题分析评估等功能,极大提高了考试的复习效率和通过率。

这里先放放程序运行效果,如下图:
在这里插入图片描述
在这里插入图片描述

二、思考

最近在学习PySide6开发,在学习到QT的QWebEngineView控件时,发现PySide6提供了比较完整的浏览器控件支持,而在2017年博主也使用过HTML+JavaScript+IE浏览器AX控件开发了HTML版的刷题工具,但当时没学Python所以只能在IE浏览器中运行。JavaScript|免费导入题库,考试复习工具,刷题神器,支持导入excel题库

缕一缕思路,如使用PySide6提供的QWebEngineView控件改造原来的刷题程序,基于HTML+JavaScript做界面(第一层)UI,基于PySide6 GUI做控制(第二层)UI,使用Python做算法逻辑开发,就能实现类似electron的能力,使得python开发也能利用HTML+JavaScript+CSS海量的UI界面的资源。

三、软件设计

根据以上思路,对原来写的程序设计进行了调整,得到一下程序实现方式简图,这个图是用Markdown提供的mermaid流程图做的,感觉还是挺厉害的,感觉有时间要好好研究一下Markdown。只要简单写写mermaid脚本,就可生成以下简洁明了的流程图。

程序实现方式
signal
JavaScript
Json
chrome内核
浏览器 GUI
HTML+CSS+JavaScript
PySide6 GUI
python
QWebEngineView控件
题库数据文件
用户

四、软件实现

(一)使用QWebEngineView控件通过JavaScript代码和chrome内核进行数据交互和逻辑控制

runJavaScript()函数可以在QWebEngineView或QWebEnginePage对象上调用,以在 Web页面上执行JavaScript代码。它接受一个字符串参数,该字符串是要执行的JavaScript代码。

在PySide6中可以使用以下Python代码向浏览器发送待执行的JavaScript脚本(带参)。self.webEngineView.page().runJavaScript(js_string, 0, js_callback)中js_string是需要执行的JavaScript脚本,js_callback是回调函数,若浏览器执行代码成功则调用js_callback代码。

    self.js_Button = QPushButton()self.js_Button.setText('运行JS脚本')self.js_Button.clicked.connect(self.run_js)def run_js(self):js_string, ok = QInputDialog.getMultiLineText(self, "请输入JS脚本", "可输入多行脚本", '')def js_callback(result):if result != '':QMessageBox.information(self, "JS脚本返回信息", str(result))self.statusBar().showMessage(f'成功执行JS脚本')self.webEngineView.page().runJavaScript(js_string, 0, js_callback)

(二)用户分别通过浏览器 GUI和PySide6 GUI进行操作

在该程序中,用户使用浏览器GUI进行题目选择、答题、提交答卷等点击交互操作,这里使用HTML+JavaScript+CSS实现交互界面的设计、渲染和浏览器内部逻辑处理。如以下JavaScript脚本,用以判断用户选择的答案是否正确。

JavaScript代码:

	function chooseAnswer() {let A = '';let B = '';let C = '';let D = '';let valueA = document.getElementById("radioA").checked;let valueB = document.getElementById("radioB").checked;let valueC = document.getElementById("radioC").checked;let valueD = document.getElementById("radioD").checked;if (valueA === true) {A = 'A';}if (valueB === true) {B = 'B';}if (valueC === true) {C = 'C';}if (valueD === true) {D = 'D';}let user_choose_answer = A + B + C + D;let daan = ti_ku.ti_mu[user_choose_number - 1].daan;let x = user_choose_answer.toLowerCase();let y = daan.toLowerCase();answers[user_choose_number - 1] = user_choose_answer;if (x === y) {alert('回答正确');fen_shu[user_choose_number - 1] = 1;if_answer[user_choose_number - 1] = 1;} else {alert('回答错误');fen_shu[user_choose_number - 1] = 0;if_answer[user_choose_number - 1] = 1;}}

该程序使用PySide6 GUI进行导入题库、保存答题记录、查看历史答题等交互操作,由于这些操作需要使用用户计算机本地资源,所以应使用python代码进行逻辑控制。如以代码,实现保存本次用户答题记录。

Python代码:

    def submit_answer_js(self):def js_callback(result):# js返回的本轮答题json结果,写入本地系统文件,并以系统用户名+时间.js形式保存在history文件夹中if result != '':js_result = str(result)save_dir = self.environment["answer_record_dir"] + os.path.sep + self.environment["cur_question_bank_id"]save_file_name = save_dir + os.path.sep + datetime.now().strftime('-%Y-%m-%d %H-%M-%S') + '.js'print(self.environment["cur_question_bank_id"])if not os.path.exists(save_dir):os.makedirs(save_dir)f = open(save_file_name, 'w', encoding='utf-8')f.write(js_result)f.close()self.statusBar().showMessage(f'已保存本次答题记录,如想查看历史答题记录可点"查看答题历史"按钮')if self.environment["cur_question_bank_id"] != '':self.webEngineView.page().runJavaScript("submit()", 0, js_callback)else:self.statusBar().showMessage(f'请先导入题库')

(三)使用PySide6 GUI获取用户计算机本地资源

由于安全问题,一般情况下浏览器是禁止直接获取用户计算机本地资源的,因此浏览器GUI无法通过JavaScript代码来获取存储在用户计算机中的题库。在该程序中,我们使用PySide6 GUI控件QFileDialog,通过编写Python代码来获取题库文件路径,接着使用os库读取题库数据。如以下python代码,实现获取题库文件路径,并读取excel题库文件数据传递到浏览器中。

Python代码:

    def import_question_bank(self):def js_callback(result):if result != '':self.statusBar().showMessage(f'成功导入选中题库')file_name, ok = QFileDialog.getOpenFileName(self, caption='Open file', dir=os.path.abspath('.'),filter="(*.xls)")if file_name != "":excel_data = excel2json.read_excel_to_json(file_name)# 根据内容生成唯一哈希码,并用该哈希码作为题库文件名,用于未来区分导入的是那一份题库save_file_name = hashlib.sha256(excel_data.encode()).hexdigest()self.environment["cur_question_bank_id"] = save_file_namejson_save_path = self.environment["question_bank_dir"] + os.path.sep + save_file_name + '.js'# 判断目录是否存在,不存在则新增if not os.path.exists(self.environment["question_bank_dir"]):os.makedirs(self.environment["question_bank_dir"])# 写入json格式内容,并保存为js文件encoded_data = base64.b64encode(excel_data.encode("gbk"))f = open(json_save_path, 'w', encoding='gbk')f.write(encoded_data.decode("gbk"))f.close()self.statusBar().showMessage(f'成功加载选中题库')self.webEngineView.page().runJavaScript("init('{}','{}');".format(self.environment["cur_question_bank_id"], encoded_data.decode("gbk")), 0,js_callback)else:self.statusBar().showMessage(f'导入题库失败,未选择文件或文件格式错误,当前仅支持xls格式')

五、遇到问题及解决方案

(一)如何把excel数据转换成json数据?

该程序的一个主要功能是导入本地题库,题库文件格式一般为:xls、xlsx、word、pdf等。但word和pdf文件涉及图像识别,实现起来比较复杂,所以博主这里只实现了xls和xlsx格式的导入功能。另外,由于后续需用浏览器GUI进行界面渲染,所以导入题库的数据最终应转成json格式,以便在浏览器环境中开展数据处理

excel格式数据转json格式数据博主之前也研究过,并写过一个python小脚本,所以这里直接拿来微调使用即可。具体实现可以参考下面这篇文章。Python|excel表格数据一键转json格式小工具|支持xlsx、xls格式转json|【源码+解析】

(二)如何向chrome内核传递带大量json数据参数的JavaScript代码?

上文提到,在PySide6中,可用webEngineView.page().runJavaScript()方法向浏览器传递待执行的JavaScript脚本。

查阅PySide6资料发现webEngineView的page()方法返回QWebEnginePage()对象,该对象拥有runJavaScript()方法,可以异步向浏览器发送js代码。

但该方法好像只支持传递字符串,不支持传递对象。因此,基于当前的对该方法的理解,只能把需向浏览器传递的脚本和数据都统一封装成一段完整代码字符串,再执行该方法。这过程还涉及统一字符串编码、保证JavaScript语法正确等问题,需要使用base64、encode等方法进行数据传递前后的编码处理,通过一系列编码转换,最终实现把题库中的大量excel数据传递至浏览器并处理,主要代码如下。

Python代码:

    def import_question_bank(self):def js_callback(result):if result != '':self.statusBar().showMessage(f'成功导入选中题库')file_name, ok = QFileDialog.getOpenFileName(self, caption='Open file', dir=os.path.abspath('.'),filter="(*.xls)")if file_name != "":excel_data = excel2json.read_excel_to_json(file_name)# 根据内容生成唯一哈希码,并用该哈希码作为题库文件名,用于未来区分导入的是那一份题库save_file_name = hashlib.sha256(excel_data.encode()).hexdigest()self.environment["cur_question_bank_id"] = save_file_namejson_save_path = self.environment["question_bank_dir"] + os.path.sep + save_file_name + '.js'# 判断目录是否存在,不存在则新增if not os.path.exists(self.environment["question_bank_dir"]):os.makedirs(self.environment["question_bank_dir"])# 写入json格式内容,并保存为js文件encoded_data = base64.b64encode(excel_data.encode("gbk"))f = open(json_save_path, 'w', encoding='gbk')f.write(encoded_data.decode("gbk"))f.close()self.statusBar().showMessage(f'成功加载选中题库')self.webEngineView.page().runJavaScript("init('{}','{}');".format(self.environment["cur_question_bank_id"], encoded_data.decode("gbk")), 0,js_callback)else:self.statusBar().showMessage(f'导入题库失败,未选择文件或文件格式错误,当前仅支持xls格式')

功能实现后对应的软件截图:
在这里插入图片描述

(三)如何区分不同的题库,同样的题库只保存一份?

hashlib.sha256是Python的一个内置模块,用于计算信息的SHA-256哈希值。这是一个计算机加密哈希函数的实例,它可以用于验证数据的完整性,或者生成数据的唯一哈希值。

该程序一开始只考虑到用户只导入一个题库进行复习,但如果用户导入第二份题库就会覆盖第一次导入的题库。为了解决该问题,使得程序支持多题库导入,首先要解决如何确保导入的题库具有唯一的标识,无论用户后续导入多少份、多少次题库,程序都可自动对题库进行去重,只保留不重复题库。博主这里使用python的hashlib库sha256()方法,使用题库内容生成唯一哈希码,并以此哈希码作为题库.js文件名,用以保证唯一性

Python代码:

        if file_name != "":excel_data = excel2json.read_excel_to_json(file_name)# 根据内容生成唯一哈希码,并用该哈希码作为题库文件名,用于未来区分导入的是那一份题库save_file_name = hashlib.sha256(excel_data.encode()).hexdigest()self.environment["cur_question_bank_id"] = save_file_namejson_save_path = self.environment["question_bank_dir"] + os.path.sep + save_file_name + '.js'# 判断目录是否存在,不存在则新增if not os.path.exists(self.environment["question_bank_dir"]):os.makedirs(self.environment["question_bank_dir"])# 写入json格式内容,并保存为js文件encoded_data = base64.b64encode(excel_data.encode("gbk"))f = open(json_save_path, 'w', encoding='gbk')f.write(encoded_data.decode("gbk"))f.close()self.statusBar().showMessage(f'成功加载选中题库')self.webEngineView.page().runJavaScript("init('{}','{}');".format(self.environment["cur_question_bank_id"], encoded_data.decode("gbk")), 0,js_callback)else:self.statusBar().showMessage(f'导入题库失败,未选择文件或文件格式错误,当前仅支持xls格式')

功能实现后对应的软件截图:
在这里插入图片描述

(四)如何保存来自浏览器GUI的答题数据,同样的答题记录只保存一份,并与对应的题库关联?

strftime()是“string format time”的缩写,主要用于格式化日期时间对象为自定义的字符串表示形式。

支持多题库导入,衍生除了另外一个新问题,当用户读取答题记录与当前所选题库不对应时,界面会出现显示错乱的BUG。因此,在每次保存答题记录、查看历史答题记录时应进行校验,保证答题记录和题库一一对应。这里有几个关键点:①用户的答题结果;②用户当前选择的题库;③当前的系统时间。需做以下几个操作:①把用户的答题结果转换成json格式字符串,用于保存为.js格式文件;②获取当前用户选择题库的哈希码,用于建立对应的答题记录一级目录,并以该哈希码命名目录;③获取当前系统时间,使用strftime()将系统时间转换成年月日时分秒格式,并以该格式命名答题记录.js文件的文件名。以下是参考代码:

JavaScript代码:

function submit() {let x = 0;let y = 0;for (let i = 0; i < ti_ku.number; i++) {x = x + fen_shu[i];y = y + if_answer[i];}alert('正确题数:' + x + '\n错误题数:' + (y - x) + '\n未作答:' + (ti_ku.number - y) );let de_fen = new Object();de_fen.answers=answers;de_fen.fen_shu=fen_shu;de_fen.if_answer=if_answer;return JSON.stringify(de_fen);de_fen = null}

Python代码:

    def submit_answer_js(self):def js_callback(result):# js返回的本轮答题json结果,写入本地系统文件,并以系统用户名+时间.js形式保存在history文件夹中if result != '':js_result = str(result)save_dir = self.environment["answer_record_dir"] + os.path.sep + self.environment["cur_question_bank_id"]save_file_name = save_dir + os.path.sep + datetime.now().strftime('-%Y-%m-%d %H-%M-%S') + '.js'print(self.environment["cur_question_bank_id"])if not os.path.exists(save_dir):os.makedirs(save_dir)f = open(save_file_name, 'w', encoding='utf-8')f.write(js_result)f.close()self.statusBar().showMessage(f'已保存本次答题记录,如想查看历史答题记录可点"查看答题历史"按钮')if self.environment["cur_question_bank_id"] != '':self.webEngineView.page().runJavaScript("submit()", 0, js_callback)else:self.statusBar().showMessage(f'请先导入题库')

功能实现后对应的软件截图:
在这里插入图片描述

六、完整源代码和可执行程序

到这里,对原来的纯HTML+JavaScript+CSS刷题程序的PySide6改造基本完成了,实现了文章开头所说的几个主要功能:
①导入题库功能,支持xlsx和xls格式;
②支持多题库导入及选择;
③支持保存答题记录;
④支持查看历史答题记录;
⑤界面由HTML+CSS实现,支持选题、标记正确、错误、未答题目、统计答题情况;
⑥逻辑由Python+JavaScript代码实现。

如果未来有时间还会继续研究和优化该程序。
以下是该Python程序的可执行文件下载地址:
疯狂刷题软件python版 - 使用PySide6自制刷题软件

这篇关于疯狂刷题python版 | 使用PySide6自制刷题软件【源码+解析】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

python: 多模块(.py)中全局变量的导入

文章目录 global关键字可变类型和不可变类型数据的内存地址单模块(单个py文件)的全局变量示例总结 多模块(多个py文件)的全局变量from x import x导入全局变量示例 import x导入全局变量示例 总结 global关键字 global 的作用范围是模块(.py)级别: 当你在一个模块(文件)中使用 global 声明变量时,这个变量只在该模块的全局命名空

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal