用 python+tkinter+有道云API 写一个自用的背单词软件

您所在的位置:网站首页 默默背单词可以建立自己的单词库吗知乎 用 python+tkinter+有道云API 写一个自用的背单词软件

用 python+tkinter+有道云API 写一个自用的背单词软件

2024-06-14 21:40| 来源: 网络整理| 查看: 265

目录

导语

程序功能

需要用的工具

需要安装的python包

程序代码

功能类函数Functions

键盘响应

窗体设置

Sql连接

Gui程序打包

结语

参考文献

导语

最近在学英语,新单词需要反复背,所以就想写一个可以自己存储单词的应用。在开始写之前搜索了一下有没有人已经做好了,只查到一篇文章有比较相关的思路,但是功能只有随机抽取单词,以及需要从外部构建好CSV表格导入进去,不够方便,所以得自己写了。刚好tkinter也一直没有学过,就趁这次的机会边学边写。

程序功能

程序主要分为三个功能区,分别是:

Search模块:用于检索词库中的单词

Upload模块:用于向词库加入新单词

Next模块:用于背单词,由Next和Answer两个按键共同实现

最后呈现的功能如图所示:

需要用的工具

python 3.6

postgreSQL(用于存储词库,也可以用excel,但是响应速度慢,个人不建议)

需要安装的python包

tkinter:构建python中的Gui窗体

sqlalchemy:用于sql的连接

playsound:播放单词发音

urllib.request:从有道的API接口获取单词发音

pyinstaller:将py文件打包成exe

程序代码

程序的整体结构如图所示

功能类函数Functions

类Functions 是程序的功能集合,由Add_Words(新增单词),Next_Random(随机抽取单词),Play_Sound(播放单词发音),Search_Word(检索单词)和Show_Answer(显示单词释义)五个子函数构成。

这部分遇到的几个问题汇总如下:

单词发音获取:有道API获取单词发音十分方便,直接通过API接口加单词访问,参数type表示美音(0)/英音(1),audio表示对应单词。本来希望通过爬虫直接实现连网播放,尝试未果,最后还是老老实实地将单词下载到本地文件夹,每次查询单词的时候调用playsound包实现MP3的发音播放。接口也支持词组,但是词组里有空格,在访问网页时,需要将词组中的空格替换为‘%20’.发音包的选择:在实现发音功能的时候尝试了许多包,playsound, pyaudio, playgame等,许多包运行出错,playsound运行成功但是响应速度大概需要肉眼可见的0.5秒。这边可以再试一下其他的包效果是否会更好。 class Functions(): def __init__(self): self.count = 0 # 用于单词计数 self.sql = Connect('English_words') # 数据库连接 self.url = 'http://dict.youdao.com/dictvoice?type=0&audio=' # 有道云单词发音API data = self.sql.load('word_list') self.word_list = list(set(data['单词'])) # 词库单词列表 def Next_Random(self): ''' 从词库中随机抽取单词 ''' global text, word, counter, url, ans self.count += 1 word = choice(self.word_list) # 随机选择一个词 text.configure(text=word) # 在text控件中显示单词 counter.configure(text='第' + str(self.count) + '个单词') # 在counter控件中显示计数 ans.config(text='') root.update_idletasks() try: # 尝试播放单词发音 self.Play_Sound() except: pass def Search_Word(self): ''' 在词库中检索单词并返回释义、发音 ''' global key, word word = key.get().lower() # 从key控件中获取输入 text.configure(text=word) # 在text控件中显示单词 try: # 若词库中有该词,则显示释义并播放声音,否则 提示未收录 self.Show_Answer() self.Play_Sound() except: ans.config(text='未收录') def Show_Answer(self): """ 显示单词释义 """ global word, ans l = self.sql.load('word_list where 单词 = \'' + word + '\'') # 从词库中检索单词 # 单词有可能因为词性不同,在词库中出现多次,因此每次根据单词在词库中按照where语句进行茶盅,并显示多层释义 if len(l) == 1: # 若只有单层释义,直接拼接词性词义等信息 c = l.iloc[0] s = c['词性'] + ' ' + c['词义'] if c['补充信息'] is not None: s = s + '\n' + '\n'.join(c['补充信息'].split(';')) else: # 否则,依次拼接词性词义等信息 s = '' for k, c in l.iterrows(): s = s + str(k + 1) + '. ' + c['词性'] + ' ' + c['词义'] if c['补充信息'] is not None: s = s + '\n ' + '\n '.join(c['补充信息'].split(';')) s += '\n' ans.configure(text=s) root.update_idletasks() def Play_Sound(self): """ 播放单词发音 """ global word playsound.playsound(u'D:\\python\\py3\\interest\\English_words\\sound\\' + word + u'.mp3') def Add_Words(self): """ 向词库增加新单词 """ global e1, e2, e3, e4, text, word, counter, ans word = e1.get().lower() # 为方便大小写输入,将词库中单词统一为小写字母 gender = e2.get() meaning = e3.get() info = e4.get() if word is '': messagebox.showinfo("提示:", "请先输入内容!") return data = pd.DataFrame({'单词': [word], '词性': [gender], '词义': [meaning], '补充信息': [info]}) if ' ' in word: # 根据HTML访问规则,将词组中的空格符号进行替换 urlword = word.replace(' ', '%20') else: urlword = word urllib.request.urlretrieve(url + urlword, r'D:\python\py3\interest\English_words\sound\%s.mp3' % word) # 从接口获取发音并存在本地文件夹 self.sql.upload(data, 'word_list', if_exists='append') e1.delete(0, END) # 上传后自动情况输入框文本 e2.delete(0, END) e3.delete(0, END) e4.delete(0, END) data = self.sql.load('word_list') self.word_list = list(set(data['单词'])) return 键盘响应

这一模块是使得可以用键盘按键控制程序,方便使用。主要用到的是tkinter的bind语句功能。

class Running_process(): def __init__(self, func): self.func = func def eventhandler(self, event): """ :param event:键盘事件 :return: 响应键盘事件 """ if event.keysym == 'Up': self.func.Add_Words() e1.focus_set() elif event.keysym == 'Down': self.func.Next_Random() elif event.keysym == 'Return': self.func.Search_Word() elif event.keysym == 'space': self.func.Show_Answer() 窗体设置

窗体设置由函数main实现,也是程序运行的主函数。本文用到的tkinter控件主要有Button,Text,Label和Enrty,网上有许多关于这几个控件的参数和功能的基本介绍,可以在使用前先了解。

写这一部分的时候,由于边写边学tkinter怎么用,再加上自己的强迫症,所以遇到了很多细节设置问题,找到了一些比较有用的网页:

Button的字体设置:和Text可以直接在语句中设置字体不同,Button需要通过调用tkinter.font包实现字体设置;Button事件的参数传递:Button可以用command参数绑定对应的功能函数。在功能函数参数传递方面,我直接用global变量略去了参数传递的问题,有看到文章采用的是lambda函数进行传递,也可以尝试这种方法。控件的布局:布局这部分花了最多时间来满足强迫症的需求。tkinter主要有三种布局方式,分别是pack, grid以及place。 pack也就是把控件作为组合进行摆放,但是控件组合(frame)内部位置不好调整,倒腾了很久还是放弃了;place的参数是空间的像素坐标,设置起来比较麻烦,灵活度低;grid也就是按照网格进行定位:把整个界面划分为若干块,再通过row和column两个参数确定控件的位置。可以参考这篇文章,解释得比较readable。  def main(): ''' word: 当前检索单词 root: TK窗体 counter: 计数器显示控件 text: 单词文本显示控件 ans: 单词释义显示控件 key: 单词检索输入控件 e1, e2, e3, e4: 新单词输入控件 :return: 窗体构建与功能实现 ''' global e1, e2, e3, e4, ans, count, text, counter, key, root # ======窗体基本设置========================================= root = Tk() root.iconbitmap('rainbow.ico') root.title('彩虹单词本') root.geometry("850x350") root.resizable(0, 0) # ======调用类============================================== func = Functions() proc = Running_process(func) # ======背单词模块=========================================== counter = Label(root, fg='red', anchor='se', font=('Arial', 13)) text = Label(root, text='开始背单词吧', font=('Arial', 15, 'bold'), width=20, height=10, wraplength=280) ans = Label(root, text='', font=('Arial', 13), width=30, wraplength=280, justify='left', height=10, anchor=W) Button(root, text="Next", bg='#43A102', fg='white', font=font.Font(family='Helvetica', size=10, weight="bold"), width=15, command=func.Next_Random).grid(row=7, column=0) Button(root, text="Answer", bg='#A2B700', fg='white', font=font.Font(family='Helvetica', size=10, weight="bold"), width=15, command=func.Show_Answer).grid(row=7, column=1) counter.grid(row=1, column=0) text.grid(row=3, column=0, rowspan=4) ans.grid(row=3, column=1, rowspan=4, sticky=W, columnspan=2) Button(root, text="Sound", bg='#EED205', fg='white', font=font.Font(family='Helvetica', size=10, weight="bold"), width=15, command=func.Play_Sound).grid(row=7, column=2) # =======搜索单词模块======================================== Label(root, text="搜索单词:", anchor=E).grid(row=0, column=0) key = Entry(root) Button(root, text="Search", bg='#FF8C05', fg='white', font=font.Font(family='Helvetica', size=10, weight="bold"), width=15, command=func.Search_Word).grid(row=0, column=2) key.grid(row=0, column=1) # =======上传单词模块======================================== Label(root, text="请输入单词:").grid(row=3, column=3) Label(root, text="请输入词性:").grid(row=4, column=3) Label(root, text="请输入词义:").grid(row=5, column=3) Label(root, text="请输入补充信息:").grid(row=6, column=3) e1 = Entry(root) e2 = Entry(root) e3 = Entry(root) e4 = Entry(root) upload = Button(root, bg='#FDD283', fg='white', font=font.Font(family='Helvetica', size=10, weight="bold"), text="Upload", width=15, command=func.Add_Words).grid(row=7, column=3, columnspan=2) e1.grid(row=3, column=4) e2.grid(row=4, column=4) e3.grid(row=5, column=4) e4.grid(row=6, column=4) # ========键盘响应模块======================================== btn = Button(root, text='button') btn.bind_all('', proc.eventhandler) # ============================================================= root.mainloop() # 进入消息循环 Sql连接

我将sql连接单独写成了Sql_Connect.py文件,使用的时候直接调用,并通过upload或者load语句与数据库进行交互,响应速度比较快,几乎可以忽略不计。

from sqlalchemy import create_engine import pandas as pd import io import datetime from string import Template class Connect(object): def __init__(self,database): self.database=database self.pg_username="postgres" self.pg_password='******' self.pg_port="5432" self.pg_host="localhost" self.engine = create_engine('postgresql+psycopg2://' + self.pg_username + ':' + self.pg_password + '@' + self.pg_host + ':' + str( self.pg_port) + '/' + database) print('连接数据库成功') # query_sql = 'select * from $arg1' # query_sql = Template(query_sql) # template方法 def load(self,file): query_sql = Template('select * from $arg1') df = pd.read_sql_query(query_sql.substitute(arg1=file),self.engine) # 配合pandas的方法读取数据库值 return df # 配合pandas的to_sql方法使用十分方便(dataframe对象直接入库) def upload(self,file,table_name, if_exists='fail'): if type(file)==str: df=pd.read_excel(file) print('获取本地表完成') else: df=file df['update_time']=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") string_data_io = io.StringIO() df.to_csv(string_data_io, sep='|', index=False) pd_sql_engine = pd.io.sql.pandasSQL_builder(self.engine) table = pd.io.sql.SQLTable(table_name, pd_sql_engine, frame=df, index=False, if_exists=if_exists) table.create() string_data_io.seek(0) # string_data_io.readline() # remove header with self.engine.connect() as connection: with connection.connection.cursor() as cursor: copy_cmd = "COPY %s FROM STDIN HEADER DELIMITER '|' CSV" % table_name cursor.copy_expert(copy_cmd, string_data_io) connection.connection.commit() print('数据上传完成') Gui程序打包

程序打包成exe文件可以方便日常使用,无需每次都打开pycharm。可以用cxfreeze或者pyinstaller,cxfreeze我没有运行成功,pyinstaller打包成功,但是exe打开需要肉眼可见的5-10秒,搜了一下似乎没有好的解决方法。

完成pyinstaller的安装后,通过CMD进入程序所在目录,并输入语句:pyinstaller -F -w ****.py,再回车后即可自动打包。

程序中若有设置icon,打包完成后有可能无法使用,并提示:failed to execute script. 解决方法可参考: pyinstaller打包

结语

完整版程序已发布在Github: https://github.com/Trista2017/English_words/tree/master

2020-04-20 新增 根据A4纸背单词法 新增模块

2020-04-20 新增 新增单词已掌握按键,sql_connect中新增delete函数

2020-04-20 新增 新增日志记录

2020-04-19 新增 LabelFrame的使用(玄学调位置= =)

参考文献

1. 珠玉在前 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3