tkinter实践之:写一个测验英语的.exe程序
笔者最近在学英语(天道好轮回,终于意识到了学英语的重要性),想写一个小工具把学习中遇到的生词、句子记录下来,能够实现中英抽验,并统计历史作答的成绩。虽然市面上有更成熟的单词类app,但自己写一个当练手也好。
【功能】
1、词句录入,删除;
2、词句抽验;
3、成绩统计;
4、一键初始化。
【拆分】
主界面。
包括词句录入、开始测试两个功能按钮;展示成绩统计;展示共有多少个词、句。
子界面-词句录入。
主界面触发,弹窗二级界面。包含两个文本框给用户输入英文和中文,提交前验证是否已存在,验证通过后保存。
子界面-抽验。
主界面触发,弹窗二级界面。包含一个Label标签,用于显示题目,一个Entry框用于用户作答。主界面选择不同功能,Label标签显示内容不同,以实现词句的中英文互考翻译。实时统计本次的作答结果并显示,点击“退出”将本次成绩和历史成绩合并。
一键初始化。
数据需要操作本地文件的读写,所以一键生成初始化json(保存词句)、ini文件(保存成绩);同时一键清空系统数据。
【语言/模块】
python,tkinter,json,os,random
代码实现
【主界面】
定义两个Button,回调录入函数;
- 单词录入;
- 句子录入;
w = tk.Tk()
w.title('快乐背单词')
tk.Label(w, text='开始测试吧!').grid(row=1, column=0)
tk.Button(w, text='单词录入', bg='gray', command=lambda :ui_record_page(choice='words')).grid(row=3, column=3)
定义两个Label显示词库数量(程序启动即读取词库数量):
- 目前系统有多少单词;
- 目前系统有多少句子;
tk.Label(w, text=wordnum).grid(row=2,column=0)
tk.Label(w, text=sentencenum).grid(row=2, column=1)
定义四个Button,回调抽验函数;
- 单词中考英;
- 单词英考中;
- 句子中考英;
- 句子英考中;
tk.Button(w, text='出英文单词', bg='pink', command=lambda :ui_test_page(choice='enword')).grid(row=3,column=0)
tk.Button(w, text='出中文单词', bg='yellow', command=lambda :ui_test_page(choice='cnword')).grid(row=3, column=1)
tk.Button(w, text='出英文句子', bg='pink', command=lambda :ui_test_page(choice='ensentence')).grid(row=4, column=0)
tk.Button(w, text='出中文句子', bg='yellow', command=lambda :ui_test_page(choice='cnsentence')).grid(row=4, column=1)
定义两个Label分别显示单词和句子的作答统计(程序启动即读历史成绩):
- 共答过m个单词,累计正确率n%;
- 共答过q个句子,累计正确率p%;
tk.Label(w, text=words).grid(row=6, column=0, columnspan=4)
tk.Label(w, text=sentences).grid(row=7, column=0, columnspan=4)
【子界面:录入词句】
将词、句以字典的形式分别保存进word.json、sentence.json。{'key': '值'},根据用户在主界面的不同选择,回调函数时携带一个参数,告诉函数读写哪个json文件。参数如下:
- ‘words’,读写word.json;
- ‘sentences’,读写sentence.json;
if choice == 'words':
filename = './happylearningini/words.json'
else:
filename = './happylearningini/sentences.json'
with open (filename, mode='r',encoding='utf-8') as f:
try:
datas = json.load(f)
except:
datas = {}
if enText.get('1.0', tk.END).strip() in datas.keys():
tk.messagebox.showinfo(title='警告', message='单词或句子已存在。')
else:
with open(filename, mode='w', encoding='utf-8') as f:
datas[enText.get('1.0', tk.END).strip()] = cnText.get('1.0', tk.END).strip()
json.dump(datas, f)
enText.delete('1.0', tk.END)
cnText.delete('1.0', tk.END)
tk.messagebox.showinfo(title='提醒', message='保存成功。')
定义两个Entry,让用户输入英文和中文;
englishText = tk.Text(w, height=5, width=50)
chineseText = tk.Text(w, height=5, width=50)
定义一个Button,回调submit功能读取json文件;函数校验英文key是否存在,是则弹窗提醒,否则保存。
tk.Button(w, text=f'保存{types}',command=lambda :/
save_record(choice=choice,enText=englishText, cnText=chineseText)).grid(row=4, column=1)
【子界面:抽验】
定义一个Entry和Text,分别用于展示题目和接收用户的输入(这里展示题目可以用Label标签,但测试发现子界面的StringVar对象无法实时更新,有兴趣的可以自行试试用StringVar写);
qEntry = tk.Entry(w1)
answerText = tk.Text(w1, height=5, width=50)
根据用户在主界面的不同选择,回调抽验函数时携带一个参数,告诉函数往题目Entry里放什么内容。参数如下:
- ‘enwords’,单词英考中,读取word.json并将key放入题目Entry;
- ‘cnwords’,单词中考英,读取word.json并将value放入题目Entry;
- ‘ensentences’,句子英考中,读取sentence.json并将key放入题目Entry;
- ‘cnsentences’,句子英考中,读取sentence.json并将value放入题目Entry;
if choice.endswith('word'):
filename = './happylearningini/words.json'
else:
filename = './happylearningini/sentences.json'
定义三个button,回调delete、submit和quit&save功能:
- delete弹窗,提醒用户是否删除。返回True执行json文件读取,删除该数据并覆盖更新文件;
tk.Button(w1, text='删除本词句', bg='blue', fg='white', command=lambda :/
delete_items(choice=choice, datas=datas, keylist=keylist, /
qEntry=qEntry, doneEntry=doneEntry, rightEntry=doneEntry)).grid(row=2,column=1)
check = tk.messagebox.askyesno(title='警告', message='是否将该词句从系统中删除?')
if not check:
return
if choice.startswith('en'):
key = qEntry.get()
print(key)
else:
key = [k for (k, v) in datas.items() if v == qEntry.get()][0]
del datas[key]
if choice.endswith('word'):
filename = './happylearningini/words.json'
else:
filename = './happylearningini/sentences.json'
with open(filename, mode='wt', encoding='utf-8') as f:
json.dump(datas,f)
tk.messagebox.showinfo(title='提醒', message='删除成功。')
select_test(choice, datas, keylist, qEntry, doneEntry, rightEntry)
- submit弹窗,展示正确答案,让用户选择是否答对,根据选择结果计算成绩;方式为不放回抽样,题库抽空则弹窗提醒终止抽验;
这里在提交时没有自动判断,主要因为用户写入的作答结果为非标数据,容易引发函数误判;
tk.Button(w1, text='提交', command=lambda: /
check_test(choice, datas, keylist, qEntry, answerText, doneEntry, rightEntry, percentEntry)) /
.grid(row=4, column=0)
done = int(doneEntry.get()[3:]) # 目前共作答了多少道
right = int(rightEntry.get()[3:]) # 共对了多少道
answer = answerText.get('1.0', tk.END).strip()
if choice.startswith('en'):
result = datas[qEntry.get()]
else:
result = [k for (k, v) in datas.items() if v == qEntry.get()]
check = tk.messagebox.askyesno(title='检查', message=f'{answer}/n正确答案:{result}/n答对了吗?')
if check:
done += 1
right += 1
else:
done += 1
- quit读取grade.ini,调出历史成绩与本次成绩计算合并。覆盖历史成绩;
tk.Button(w1, text='保存测验结果', bg='red', command= lambda :save_grade(choice, doneEntry, rightEntry))/
.grid(row=4, column=1)
done = int(doneEntry.get()[3:])
right = int(rightEntry.get()[3:])
grade_data = []
with open('./happylearningini/grades.ini', mode='r+t',encoding='utf-8') as f:
res = f.readline()
while res:
if choice.endswith('word'):
seek = '单词作答量'
else:
seek = '句子作答量'
if res.startswith(seek):
oldnums, oldpercent, null = res[6:].split('-')
done += int(oldnums)
oldpercent = round(float(oldpercent[:-1]) * 0.01,4)
right += int(int(oldnums) * oldpercent)
percent = round(right/done,4)
res = f'{seek}={done}-{percent*100}%-/n'
grade_data.append(res)
res = f.readline()
with open('./happylearningini/grades.ini', mode='wt',encoding='utf-8') as f:
for i in grade_data:
f.write(i)
tk.messagebox.showinfo(title='提醒',message='作答记录已保存。/n【可直接关闭作答页面,无需重复保存】')
【一键初始化】
os查找当前目录是否存在以下文件,有则清空,无则创建:
- words.json
- sentences.json
- grades.ini
res = tk.messagebox.askyesno(title='警告!', message='''
是否初始化程序?
历史数据都将清除,程序将被重置。“否”取消操作。
''', )
if not res:
return
dirs = os.listdir() # 查询当前的目录是否存在happylearningini文件夹
if 'happylearningini' in dirs: # 如果存在就清空happylearningini文件夹
sondirs = os.listdir('./happylearningini')
for file in sondirs:
os.remove(f'./happylearningini/{file}')
else: # 不存在就创建空文件夹
os.mkdir('happylearningini')
with open('./happylearningini/words.json', mode='wt', encoding='utf-8') as f:
pass # 创建初始化的words配置文件
with open('./happylearningini/sentences.json', mode='wt', encoding='utf-8') as f:
pass # 创建初始化的sentences配置文件
with open('./happylearningini/grades.ini', mode='wt', encoding='utf-8') as f:
res = '作答数-正确率-/n单词作答量=0-0%-/n句子作答量=0-0%-/n'
f.write(res) # 创建初始化的grades配置文件
tk.messagebox.showinfo(title='提醒', message='初始化成功!/n您可以正常使用了。')
前端效果
【一些更丰富的构想】
- 稍加改动可以继续构建更加方便的联网翻译。大致思路为:选择翻译网址,将用户要翻译的内容作为参数带入请求,解析响应结果并展示。再增加一个Button用于回调录入函数,将翻译结果自动保存。
- 为了增加联网请求的健壮度,这里使用try-except,或者构建一个请求网址pool会更好些,笔者更倾向后者。最后命令符pyinstaller -F filename.py打包成exe(需要提前pip安装打包模块)。
到此程序的功能就很完善了,快拿去背英语吧
共有 0 条评论