实例61使用PyQt5制作了图形用户界面(GUI)。这个香是香,但是打包后的安装文件太大(约80M),感觉有些美中不足啊。由于这个GUI实在简单,从现实角度实在用不着高大上的PyQt5,所以改用Python内置的Tkinter库来做图形用户界面(GUI),理论上可大幅缩小打包后的文件大小。对于简单的图形界面,Tkinter是毫无压力的。
先依葫芦画瓢,把界面搞出来再说。然后再慢慢连接程序,实现操作的交互功能。不要将库中的模块全导入,只导入要用到的模块,这样可以尽量缩小打包后的文件。这里主要用到文本框(Entry)、按钮(Button)、列表框(Listbox)、滚动条(Scrollbar)等组件以及文件目录获取模块(askdirectory),所以只导入这些即可。
定义两个“选择目录”的函数select_dir1及select_dir2,用于选择“输入文件目录”和“输出文件目录”。它们将使用按钮来调用,并将选择的目录显示在文本框中。input_entry和output_entry是后面定义的文本框。先用delete(0, END)方法将文本框清空,0和END对应文本框中的内容的开始和结束值。从0到结束都删除掉,也就等于“清空”了。之所以要清空文本框,是为了避免误操作输入了字符,影响到后面插入的路径。然后就可以通过insert方法插入选择的目录了,其中的参数0表示开始值,即从0这个位置插入选择的目录。askdirectory是选择目录的方法,会弹出对话框让我们操作。
然后通过Tk()创建一个新的界面,存入MyWindow中。然后添加标题,设置窗口大小(单位像素),添加窗口Logo。加上一句myWindow.mainloop()进入消息循环,就能出现如下空界面。
接下来,就要添加一些需要的控件了。先添加两个文本框和三个按钮。以input_entry为例,Entry()即是添加文本框的方法,其中第一个参数是目标窗口,此处是之前定义的窗口myWindow。highlightcolor指鼠标选中后显示高亮的颜色,highlightthickness指高亮的框线的粗细。这两个可有可无。然后通过place()放置到指定位置,其中x,y参数对应窗口myWindow上的坐标,都取10像素,让控件不要紧贴窗口边缘。width和height指文本框的长度和宽度,这个根据需要调整,已保持与其它控件的对齐和整个界面的美观。
然后在文本框input_entry右边添加一个按钮btn_in,它的文字显示为"输入文件目录",指令command连接到函数select_dir1,根据需要设定宽高(这里的宽高指多少个文字,比如width=10指10个字符的宽度)。同样,使用place指定放置位置。同样的方法,再添加一个文本框和两个按钮。
随后添加一个列表框result_show,用于显示操作的进度和结果,并通过bg设置其背景颜色。其背景颜色牌如下图,可根据需要自由选择(看不清楚?没关系,已上传网盘,可到公众号输入“源文件”提取)。
设置好放置位置后,给它添加上横轴及纵轴方向的滚动条。纵轴方向的滚动条通过Scrollbar(result_show,command=result_show.yview)添加,由于是添加进列表框的,所以第一个参数是result_show而不是主窗口myWindow。通过command将滚动条的操作连接到列表框的纵轴方向上yview。通过pack将滚动条放置到右边side=RIGHT,并填充整个纵轴方向fill=Y。最后通过config(yscrollcommand = sbY.set)将滚动条配置到列表框的纵轴方向。类似的方法添加横轴方向的滚动条,因为滚动条的默认方向是垂直方向,所以需要特别指定其方向为水平方向orient = HORIZONTAL。
到此,界面准备完成,运行一下,得到如下结果。看起来还是像那么回事的。
from tkinter import Tk,Entry,Button,Listbox,X,Y,END,Scrollbar,RIGHT,BOTTOM,HORIZONTAL
from tkinter.filedialog import askdirectory
#用于选择“输入文件目录”
def select_dir1():
input_entry.delete(0, END) #先清空文本框
input_entry.insert(0, askdirectory(initialdir= "D:\\")) #选择目录并插入文本框,默认选择D盘
#用于选择“输出文件目录”
def select_dir2():
output_entry.delete(0, END)
output_entry.insert(0, askdirectory(initialdir= "D:\\"))
#初始化Tk()
myWindow = Tk()
myWindow.title("领料记录汇总")#添加窗口标题
myWindow.geometry('590x400')#设置窗口大小
myWindow.iconbitmap("PO.ico")#添加窗口Logo
#添加文本框及按钮
input_entry = Entry(myWindow, highlightcolor='red', highlightthickness=1)#添加文本框
input_entry.place(x=10, y=10,width=480, height=30)#按坐标放置,设定高度和宽度
btn_in = Button(myWindow, text='输入文件目录',command = select_dir1, width=10, height=1)
btn_in.place(x=500,y=10)
output_entry = Entry(myWindow, highlightcolor='blue', highlightthickness=1)
output_entry.place(x=10, y=50,width=480, height=30)
btn_out = Button(myWindow, text='输出文件目录',command = select_dir2, width=10, height=1)
btn_out.place(x=500,y=50)
btn_run = Button(myWindow, text='执行汇总', width=10, height=1)
btn_run.place(x=500,y=90)
#添加列表框及滚动条
result_show = Listbox(myWindow,bg='DarkSeaGreen')
result_show.place(x=10,y=130, width=570, height=260)
sbY = Scrollbar(result_show,command=result_show.yview)#在列表框中增加Y轴滚动条
sbY.pack(side=RIGHT,fill=Y)
result_show.config(yscrollcommand = sbY.set)
sbX = Scrollbar(result_show,command=result_show.xview,orient = HORIZONTAL)#在列表框中增加X轴滚动条
sbX.pack(side=BOTTOM,fill=X)
result_show.config(xscrollcommand = sbX.set)
myWindow.mainloop()
窗口准备好后,就需要将执行程序跟窗口各控件连接起来了。为了方便各函数互相调用,需要将所有函数和窗口设置放入类MainGUI里面。程序运行的步骤如下:
1. 弹出窗口,然后手动选择输入文件目录和输出文件目录
2. 点击“执行汇总按钮”
3. 程序将从输入文件目录对应的文本框中获取目录信息,此处通过self.input_entry.get()获得
4. 然后程序将单个“领料记录”文件的数据写入汇总文件。每写入一个,则在列表框显示信息,此处通过self.result_show.insert("end", f)实现,其中"end"指插入的位置为列表框的末尾,"f"为插入的内容
5. 写入汇总文件完成后,将结果保存到“输出文件目录”,并在列表框显示信息。
from os import listdir
from xlrd import open_workbook, xldate
from datetime import datetime
from time import time, localtime ,strftime
from openpyxl import Workbook
from openpyxl.styles import Border, Side, PatternFill, Font, GradientFill, Alignment
from tkinter import Tk,Entry,Button,Listbox,X,Y,END,Scrollbar,RIGHT,BOTTOM,HORIZONTAL
from tkinter.filedialog import askdirectory
class MainGUI():
def __init__(self):
myWindow = Tk()
myWindow.title("领料记录汇总")
#设置窗口大小
myWindow.geometry('590x400')
myWindow.iconbitmap("PO.ico")
#增加文本框
self.input_entry = Entry(myWindow, highlightcolor='red', highlightthickness=1)
self.input_entry.place(x=10, y=10,width=480, height=30)
self.btn_in = Button(myWindow, text='输入文件目录',command = self.select_dir1, width=10, height=1)
self.btn_in.place(x=500,y=10)
self.output_entry = Entry(myWindow, highlightcolor='blue', highlightthickness=1)
self.output_entry.place(x=10, y=50,width=480, height=30)
self.btn_out = Button(myWindow, text='输出文件目录',command = self.select_dir2, width=10, height=1)
self.btn_out.place(x=500,y=50)
self.btn_run = Button(myWindow, text='执行汇总', width=10, height=1,command = self.Summary_data)
self.btn_run.place(x=500,y=90)
#增加列表框
self.result_show = Listbox(myWindow,bg='DarkSeaGreen') #yscrollcommand = scroll_bar,
self.result_show.place(x=10,y=130, width=570, height=260)
self.sbY = Scrollbar(self.result_show,command=self.result_show.yview)#在列表框中增加Y轴滚动条
self.sbY.pack(side=RIGHT,fill=Y)
self.result_show.config(yscrollcommand = self.sbY.set)
self.sbX = Scrollbar(self.result_show,command=self.result_show.xview,orient = HORIZONTAL)#在列表框中增加X轴滚动条
self.sbX.pack(side=BOTTOM,fill=X)
self.result_show.config(xscrollcommand = self.sbX.set)
myWindow.mainloop()
def select_dir1(self):
self.input_entry.delete(0, END)
self.input_entry.insert(0, askdirectory(initialdir= "D:\\"))
def select_dir2(self):
self.output_entry.delete(0, END)
self.output_entry.insert(0, askdirectory(initialdir= "D:\\"))
#读取xls文件中的数据
def Get_data(self,file):
wb = open_workbook(file) #读取工作簿
ws = wb.sheets()[0] #选第一个工作表
data = {}
for row in range(7, ws.nrows-2):
dept = ws.cell(2, 16).value #部门
dept_id = ws.cell(3, 16).value #部门编号
dt = ws.cell(row, 0).value #时间
if type(dt) is float:
date_time = xldate.xldate_as_datetime(dt, 0)
else:
date_time = datetime.strptime(dt,'%Y-%m-%d %H:%M:%S')
business = ws.cell(row, 2).value #业务类型
model = ws.cell(row, 3).value #品种
qty = ws.cell(row, 4).value #数量
unit_price = ws.cell(row, 6).value #单价
price = ws.cell(row, 8).value #总价
reward = ws.cell(row, 9).value #额外值
discount = ws.cell(row, 11).value #调整
balance = ws.cell(row, 13).value #剩余
location = str(ws.cell(row, 15).value).strip() #库位
operator = ws.cell(row, 17).value #操作员
date = date_time.date() #日期
time = date_time.time() #时间
info_list=[dept,dept_id,date_time,business,model,qty,unit_price,price,reward,discount,
balance,location,operator,date,time]
data.setdefault(date,[]) #以日期为键
if info_list[3] != "备注": #不要业务类型为“备注”的数据
data[date].append(info_list)
#增加当日领取次数
for key in data.keys():
for i in data[key]:
i.append(len(data[key]))
return data
def Get_file_path(self,path):
files=[]
for file in listdir(path):
if file.endswith(".xls"): #排除文件夹内的其它干扰文件
files.append(path+"\\"+file)
return files
def Get_current_time(self):
time_stamp = time() # 当前时间的时间戳
local_time = localtime(time_stamp) #
str_time = strftime('%Y-%m-%d %H.%M.%S', local_time)
return str_time
def Summary_data(self):
thin = Side(border_style="thin", color="000000")#定义边框粗细及颜色
title = ['部门', '部门编号', '时间', '业务类型', '品种', '数量', '单价', '金额', '额外值',
'调整', '剩余', '库位', '操作员', '领取日期', '领取时间', '领取次数']
wb = Workbook()
ws = wb.active
ws.merge_cells("A1:P1")
ws.cell(1,1).value = "领料明细汇总表"
ws.cell(1,1).font = Font(name=u'黑体',bold=True,size=18)
ws.row_dimensions[1].height = 22.2
ws.cell(1,1).alignment = Alignment(horizontal="center", vertical="center")
ws.append(title)
#插入数据
files = self.Get_file_path(self.input_entry.get()) #get()获取文本编辑框中的输入文件目录,并获取目录下的xls文件
for file in files:
data = self.Get_data(file)
for key in data.keys():
for i in data[key]:
ws.append(i)
f = f"{file} 的内容已加入总表." # 创建一个显示项
self.result_show.insert("end", f) #将结果添加到列表框中
#设置字号,对齐,缩小字体填充,加边框
#Font(bold=True)可加粗字体
for row_number in range(2, ws.max_row+1):
for col_number in range(1,17):
c = ws.cell(row=row_number,column=col_number)
c.font = Font(size=9)
c.border = Border(top=thin, left=thin, right=thin, bottom=thin)
c.alignment = Alignment(horizontal="left", vertical="center")
col_name= list("ABCDEFGHIJKLMNOP")
col_width = [8, 8, 16, 8, 16, 8, 8, 9.8, 8, 8, 8, 11, 8.3, 9, 8, 8]
for i in range(len(col_name)):
ws.column_dimensions[col_name[i]].width = col_width[i]
ws.column_dimensions.group('I','K',hidden=True)
ws.column_dimensions.group('N','O',hidden=True)
wb.save(f"{self.output_entry.get()}\\领料明细汇总表{self.Get_current_time()}.xlsx")
f = "-"*100 #创建分割线
self.result_show.insert("end", f) # 将分割线添加到列表框
f = f"领料明细汇总表{self.Get_current_time()}.xlsx 已生成,请去输出文件夹查看."# 创建一个显示项
self.result_show.insert("end", f) # 将结果添加到列表框
f = " "*100
self.result_show.insert("end", f) # 将以上空格添加到列表框
if __name__ == "__main__":
MainGUI()
程序运行的结果图如下,符合要求。
然后开始打包操作,具体操作见实例62 。为避免干扰,还是去纯Python环境的虚拟机中打包。结果只有18.6M,比Qt5的80M,立减77.5%,效果看得见啊!
如果您有需要处理的问题,可发邮件到邮箱:donyo@qq.com,一起探讨解决方案。微信公众号输入“源文件”提取所有源文件及资料。
喜欢此文,点亮“在看”!