700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 使用python开发的GUI可视化界面植物名录查询系统 使用python读取xls文件 读取xlsx文

使用python开发的GUI可视化界面植物名录查询系统 使用python读取xls文件 读取xlsx文

时间:2021-07-29 05:06:09

相关推荐

使用python开发的GUI可视化界面植物名录查询系统 使用python读取xls文件 读取xlsx文

python采用excel表格作为数据库开发植物名录查询系统

作者简介项目背景开发整体思路效果图涉及工具涉及python第三方库按开发步骤描述关键代码开发过程渲染初始界面组件主函数执行内容打开文件并传递给读取数据的函数设定全局变量使用xlrd第三方库读取.xls文件使用openpyxl第三方库读取.xlsx文件查询按钮响应函数按钮初始化函数查到数据后,渲染到界面上的函数源代码下载地址总结

作者简介

白天飞,本科毕业于计算机科学与技术专业,致力于水果电商创业,是百熟优质果平台创始合伙人,身负平台产品经理(官方微信小程序:百熟优质果)。企业理念:1.只找最优质的水果。2.客户说的都是对的。鄙人同时兴趣开发微信小程序:帮帮帮个忙。定位是为大家互相帮忙,资源互换(让需求双方及时对接,资源可以是思路、劳动力、技能、资料等等)。

项目背景

一同校生科系学弟请我帮忙开发一个植物名录查询系统,作为毕业设计使用。经过询问,没有编程基础,不会数据库操作,且由于考研复试没有空闲时间学习。那我就用了最笨的办法进行毕业设计操作,即数据库用excel表格充当,这样解决了写入数据的问题,主要涉及到了图片,所以我没有用数据库进行操作,同时,我自己也还没有系统的学过数据库操作,又是学校将来要充当使用的就给了最土的方案,学校的人操作excel表格总会的。

开发整体思路

事先准备好excel表格数据,并准备好要展示的图片。其中图片与表格内的数据需要要对应,且图片存放于表格同级路径下的pictures文件夹下面,图片命名规则为序号.png

使用python语言将表格内的数据注入到变量中(注意:数据量比较小的情况下,可以注入到内存中,否则很烧电脑的性能),通过指定下标的变量数组值与所查询的值匹配来获取同数组内的所有值,并使用tkinter可视化工具输出到界面中。

界面所需显示的关键内容:

按钮:打开文件、查询、退出、下一个输入输出文本框显示图片统计查询到同名的数量,并通过下一个按钮进行切换展示

效果图

老规矩,先上图

刚打开时候避免查询报错,需要用户一定要先打开文件才能显示查询按钮。

打开之后的界面

涉及工具

pycharm编辑器python3.7excel表格(.xls;.xlsx)

涉及python第三方库

import xlrd #openpyxl模块 无法打开xls文件 故用这个模块# import openpyxl #可读取表格文件里的 图片 且读取的数量比较大import PILfrom PIL import Image, ImageTkfrom openpyxl import workbookfrom openpyxl import load_workbookimport osfrom tkinter import *import tkinter as tkfrom tkinter import scrolledtextfrom tkinter import filedialog #导入打开文件夹对话框# 导入消息对话框子模块import tkinter.messagebox

按开发步骤描述关键代码开发过程

渲染初始界面组件

由于可视化界面全局多处要被调用修改值,故应该放在全局变量中。部分标题类的标签可以在函数中只运行一次即可。

全局组件变量

#创建主窗口root = Tk()root.title('植物名录查询——运城学院园林植物数据库')# 设置窗口大小root.minsize(750,750)# 设置单选按钮全局变量接收值radioVal = StringVar() # 获取值方式 radioVal.get()radioVal.set('中文名') # 默认选择中文名#定义变量path = StringVar()path.set('')count = StringVar()count.set('')num = StringVar()num.set('')Cname = StringVar()Cname.set('')Fname = StringVar()Fname.set('')Gname = StringVar()Gname.set('')pic = StringVar()pic.set('')Lname = StringVar()Lname.set('')#右侧输出内容的标签进行占位#num text, name text, habit textle_path = Label(root, textvariable = path,padx=30,anchor='w').place(x = 100, y = 80) #le_count = Label(root, textvariable = count,padx=30,anchor='w').place(x = 100, y = 110) # 注意不是没显示,而是被挡住了 故加外边框le_num = Label(root, textvariable = num).place(x = 100, y = 140) #le_Cname = Label(root, textvariable = Cname).place(x = 100, y = 170) #le_Lname = Label(root, textvariable = Lname).place(x = 100, y = 200) #le_Fname = Label(root, textvariable = Fname).place(x = 100, y = 230) #le_Gname = Label(root, textvariable = Gname).place(x = 100, y = 260) ##设置滚动窗口文本#习性habits = tk.LabelFrame(root, text="习 性", padx=10, pady=10 ) # 水平,垂直方向上的边距均为 10habits.place(x=40, y=290)habits_Window = scrolledtext.ScrolledText(habits, width=85, height=5, padx=10, pady=10, wrap=tk.WORD)habits_Window.grid()#用途use = tk.LabelFrame(root, text="用 途", padx=10, pady=10 ) # 水平,垂直方向上的边距均为 10use.place(x=40, y=430)use_Window = scrolledtext.ScrolledText(use, width=85, height=5, padx=10, pady=10, wrap=tk.WORD)use_Window.grid()#植物志flora = tk.LabelFrame(root, text="植物志", padx=10, pady=10 ) # 水平,垂直方向上的边距均为 10flora.place(x=40, y=570)flora_Window = scrolledtext.ScrolledText(flora, width=85, height=5, padx=10, pady=10, wrap=tk.WORD)flora_Window.grid()###############全局变量结束###################

函数部分组件变量

def main():# 打开文件openFile_button = Button(root,bg='white',text='打开文件',width=8,height=1,command=lambda :openFile()).place(x=40,y=50,anchor='nw')#初始化窗体界面Label(root, text='文件所在路径:').place(x=40, y=80) # 数量Label(root, text = '查询到的数量:').place(x=40, y=110) # 数量Label(root, text = '序 号:').place(x = 40, y = 140)Label(root, text = '中文名:').place(x = 40, y = 170)Label(root, text = '拉丁名:').place(x = 40, y = 200)Label(root, text = '科 名:').place(x = 40, y = 230)Label(root, text = '属 名:').place(x = 40, y = 260)Label(root, text = '图 片:').place(x = 40, y = 710)root.mainloop() # 开启后才会显示

主函数执行内容

if __name__ == '__main__':main()

打开文件并传递给读取数据的函数

#打开文件对话框def openFile():# 打开文件选择对话框root =tk.Tk()root.withdraw()# Windows 可以filetypes=[('表格', '*.xls;*.xlsx')] ,但MAC上仅可以filetypes=[('表格', '*.xls'),('表格','*.xlsx')Filepath = filedialog.askopenfilename(filetypes=[('表格', '*.xls'),('表格','*.xlsx')]) #过滤文件后缀类型# print(os.path.split(Filepath))(filepath, tempfilename) = os.path.split(Filepath) # 右侧的值是 ('E:/0wt/副本', '植物学名录.xlsx') 元组类型# print(filepath)# Filepath 当路径存在的时候继续if Filepath:global pic_path # 全局路径# 拼凑图片文件夹路径pic_path = filepath+'/pictures'# 如果目录不存在,则进行创建if os.path.exists(pic_path) is False:os.mkdir(pic_path)# 将文件所在路径输出到界面上path.set(Filepath)# 将不可使用的按钮激活buttonInit()# 传输excel表格的路径# 调用读取数据的函数,趁着用户正在查看查询条件的时候 将数据注入到全局变量中 减少查询等待# 由于两种表格文件的读取模块不同,需要做处理判断属于哪种文件类型,故采用下边的方式进行判断# 从文件名中分离出后缀(filename, extension) = os.path.splitext(tempfilename)if extension=='.xls' or extension=='.XLS':read_xls(Filepath)elif extension=='.xlsx' or extension=='.XLSX':read_xlsx(Filepath)else:print('未选择任何文件,程序运行结束')exit_program()

设定全局变量

#数据使用的全局变量########data = [] # 将表格数据注入到该变量findData = [] # 查询到的数据注入到该变量searchIndex = 0 # 查到的数据变量索引,默认0 即第一个pic_path = '' # 图片文件价路径

由于两个第三方库都只能读取一种表格类型,所以这边需要用到两种读取文件数据的函数。

注意:xlrd能处理的最大行数为65535,超过这个行数的文件就需要用到openpyxl,openpyxl最大支持1048576行。openpyxl不支持xls格式文件。

使用xlrd第三方库读取.xls文件

# 将读取的数据注入全局变量数组中 data[]def read_xls(xls_path):# 由于时采用追加形式的,如果打开了新的文件,那么需要将全局变量清空global datadata = []print('文件所在路径:%s'%xls_path)data_xls = xlrd.open_workbook(xls_path) #打开此地址下的.xls文档sheet_name = data_xls.sheets()[0] #进入第一张表# print(sheet_name)# sheet_name1 = data_xsls.sheet_by_index(0) #获取第一张表格 下标值从0开始# print(sheet_name1)count_nrows = sheet_name.nrows #获取总行数 包括表头print('数据总行数:',count_nrows-1)count_nocls = sheet_name.ncols #获得总列数print('数据总列数:', count_nocls)line_value = sheet_name.row_values(0)print('文档各列数名:',line_value)# 循环遍历获取内容for i in range(1,count_nrows):data_1 = {}for j in range(0,count_nocls):data_1[line_value[j]]=sheet_name.cell(i,j).value #根据行数来取对应列的值,并添加到字典中# print(data_1)# 将一行数据资料拼凑完整后压入全局数组变量中data.append(data_1)# print(data)

使用openpyxl第三方库读取.xlsx文件

def read_xlsx(xlsx_path):# 由于时采用追加形式的,如果打开了新的文件,那么需要将全局变量清空global datadata = []print('文件所在路径:%s' % xlsx_path)#open a workbookwb = load_workbook(xlsx_path)# 获取表格标签名称列表# wb.get_sheet_names() #已弃用sheet_names = wb.sheetnames# print(sheet_names[0])# 访问指定sheet #获取第一张表wb.active # 调用wb._active_sheet_index# ws = wb.get_sheet_by_name(sheet_name) #已弃用sheet = wb[sheet_names[0]] #名字可能会变,故通过索引0获取第一张表# 需要编写自己的代码以找到第一个完全空的代码.当然,最有效的方法是从max_row开始并向后工作,但以下内容可能就足够了# 循环判断空值到来的前一行 索引值for max_row, row in enumerate(sheet, 1):if all(c.value is None for c in row):# print(max_row)breakcount_nrows = max_row #sheet.max_row # 获取总行数 包括表头 由于 sheet.max_row 会报出更大的错误 故不采用这个报行数print('数据总行数:', count_nrows - 1) #扣除表头那一行,剩下的都是数据行# 按行获取数据转换成列表# 先定义一个总的列表所有的行先放在列表中rows_data = list(sheet.rows)# print(rows_data[0])# 获取表单的表头信息(第一行),也就是列表的第一个元素titles = []for temp in rows_data[0]:if temp.value: #布尔值不为 False的时候进行读取表头数据titles.append(temp.value)else: # 表头第一行列中间出现空值 或者 已经到最后一列了 ,为了节省性能进行退出循环 判断为表头加载结束break# print(titles) # 打出来看看# 通过判断刚才打印好的表头数组长度来判断 总列数count_nocls = len(titles) # sheet.max_column # 获得总列数 直接返回的值过大print('数据总列数:', count_nocls)print('文档各列数名:', titles)k = [] # 接收遍历出来的每一行数据# 循环遍历获取内容for i in range(1, count_nrows-1): # 由于我不是用 cell() 函数进行取值,采用的是遍历,所以我要减除1,而另一个函数 是需要用最大行数进行定位,故不需要减data_1 = []for temp_row in rows_data[i]:if temp_row.value or len(data_1)<count_nocls: # 布尔值不为 False的时候 或者所循环的列数还小于总列数进行读取表头数据data_1.append(temp_row.value)else: # 表头第一行列中间出现空值 或者 已经到最后一列了 ,为了节省性能进行退出循环 判断为表头加载结束break# 将一行数据资料拼凑完整后压入全局数组变量中k.append(data_1)# print(k)# 将k 数组变量中的每一行数据 转换成字典for item in k:# print(item)# 将表头和该条数据内容,打包成一个字典rows_dict = dict(list(zip(titles, item)))# 将转换后的字典压入 全局data数组变量中 即注入内存data.append(rows_dict)# print(rows_dict)# print(data)

查询按钮响应函数

#查询按钮响应函数def select(root, label):#清空全局变量global findDatafindData = []global searchIndexsearchIndex = 0# print('result: ', findData)sname = label.get()# print(sname)## print(data)# 遍历数据各个函数,看是否有相等的数据,并打印出来for index in range(0,len(data)):# partData = []if data[index][radioVal.get()]==sname:# print(data[index])# for temp in data[index].items():#partData.append(temp[1])## print(partData)findData.append(data[index]) #将查询到的数据压入一会儿渲染用的数组中# print(findData)if len(findData) <= 0:tkinter.messagebox.showinfo('提示',sname+'植物名不存在,请输入其他植物名!')elif len(findData) >1:Button(root, bg='white', text='下一个', width=10, height=1, command=lambda: next_button()).place(x=280, y=100,anchor='nw')# 数据成功提取出来了show(searchIndex)else:print('查找到的数量: %d 个' %len(findData))#数据成功提取出来了show(searchIndex)

按钮初始化函数

#定义一个返回按钮调用的返回函数:callbackdef exit_program():os._exit(9)def buttonInit():input_name = Label(root, text='请输入植物名字:').place(x=30, y=30)label = StringVar()entry = Entry(root, bg='#ffffff', width=20, textvariable=label).place(x=130, y=30, anchor='nw')# 按钮 除了打开文件 全部初始化为不可用# command是Button中的option项,可以指定点击button时调用的callback函数# 查询select_button = Button(root, bg='white', text='查询', width=10, height=1, command=lambda: select(root, label)).place(x=430, y=26, anchor='nw')# 退出exit_button = Button(root, bg='white', text='退出', width=10, height=1, command=lambda: exit_program()).place(x=530,y=26,anchor='nw')# print(radioVal.get())#初始化单选按钮Radiobutton(root, text='中文名', variable=radioVal, value='中文名', ).place(x=280, y=26,anchor='nw') #当 variable == value 时 为选中状态Radiobutton(root, text='拉丁名', variable=radioVal, value='拉丁名', ).place(x=350, y=26,anchor='nw')# 下一个数据值 只控制索引值的变化def next_button():global findData #全局查找到的数组变量 0 1 2 3global searchIndex #前面关键字声明 表示使用全局变量# print(findData)# print(searchIndex)if searchIndex <len(findData)-1:searchIndex+=1show(searchIndex)elif searchIndex == len(findData)-1:searchIndex = 0show(searchIndex)

查到数据后,渲染到界面上的函数

注意:scrolledtext组件网上很多用到的都是“插入”数据,一开始没有注意到这个细节,导致查询的时候,框内的信息一直是增加的,没有正常显示。后来,查询了手册没有发现能够直接更新组件内容的内部方法,只好采用“清空”输入框数据,后进行插入操作,来完成整个更新数据过程。同时,注意那个删除方法内的索引值要求是浮点数,即0.0,整数0不起作用还会报错。

对于图片的显示,采用pillow库进行打开读取并转化为tk能够接收的文件编码格式渲染到界面上。考虑到有些植物信息可能没有事先保存好图片,对于这部分数据显示图片的标签框进行覆盖空数据,达到让上一张图片隐藏起来的目的。注意当label标签不是使用文本的时候,长宽的像素是根据字符来的,反正是根据像素来的,所以不能直接使用显示图片的长宽比参数进行覆盖。覆盖后必须启动root.mainloop()进行触发才可以。

# 将查询到的数据渲染到页面上def show(searchIndex):# print(searchIndex)count.set(len(findData)) # 将查询到的数量输出到界面上num.set(findData[searchIndex]['序号']) #Cname.set(findData[searchIndex]['中文名']) #Fname.set(findData[searchIndex]['科名']) #Gname.set(findData[searchIndex]['属名']) #Lname.set(findData[searchIndex]['拉丁名']) #pic.set(findData[searchIndex]['图片']) ## 将各个滚动窗口的内容进行刷入 findData[searchIndex]['习性']habits_Window.delete(0.0,"end") #清空滚动框habits_Window.insert("end", str(findData[searchIndex]['习性']) + '\n') #插入数据habits_Window.see(0.0) # end 让光标显示在结尾 索引值不能用整数0 置为开头,必须用小数点的 0.0 才可以use_Window.delete(0.0, "end") # 清空滚动框use_Window.insert("end", str(findData[searchIndex]['用途']) + '\n')use_Window.see(0.0)flora_Window.delete(0.0, "end") # 清空滚动框flora_Window.insert("end", str(findData[searchIndex]['植物志']) + '\n')flora_Window.see(0.0)########################global pic_path # 引用全局变量# 拼凑图片路径 当前路径的 pictures 文件夹下边picture = pic_path+'/'+findData[searchIndex]['序号']+'.png'# print(picture)# 检查文件是否存在 存在则进行图片刷新if os.path.exists(picture):#显示图片pilImage = PIL.Image.open(picture)# pilImage.show()tkImage = ImageTk.PhotoImage(image=pilImage)label_pic = Label(root, image=tkImage,width=200,height=200).place(x=420, y=75, anchor='nw') # 图像像素要求 200x200 否则将会只有一角root.mainloop() # 开启后才会显示else:label_pic = Label(root,width=30, height=12).place(x=420, y=75,anchor='nw') # 没有图片存在的时候,关闭之前显示的图片root.mainloop() # 开启后才会显示

源代码下载地址

python植物名录查询系统源代码下载

总结

本次开发主要关键技术在于读取excel表格数据、可视化组件的创建以及渲染数据。开发一共用了一天时间,今天早上优化了下昨天留下的数据框不更新多次打开文件会重复叠加数据等bug。

中间涉及到的许多技术可以通用,欢迎各位大佬进行参考。系统开发仓促,中间可能还有很多隐藏的bug未能及时发现,欢迎各位评论区留言或私信给我。

使用python开发的GUI可视化界面植物名录查询系统 使用python读取xls文件 读取xlsx文件。tkinter使用

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。