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您可以正常使用了。')

前端效果


图片1.png

图片2.png

图片3.png

【一些更丰富的构想】

  • 稍加改动可以继续构建更加方便的联网翻译。大致思路为:选择翻译网址,将用户要翻译的内容作为参数带入请求,解析响应结果并展示。再增加一个Button用于回调录入函数,将翻译结果自动保存。
  • 为了增加联网请求的健壮度,这里使用try-except,或者构建一个请求网址pool会更好些,笔者更倾向后者。最后命令符pyinstaller -F filename.py打包成exe(需要提前pip安装打包模块)。
    到此程序的功能就很完善了,快拿去背英语吧
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容