# coding=utf-8
import os
import xlrd # Excel读数据相关库
import xlwt # Excel写数据相关库
import _thread # 线程库
import tkinter as tk # 窗体相关库
from tkinter import ttk
import tkinter.font as tf
import tkinter.filedialog
import tkinter.messagebox as mes
class MyApp(tk.Tk):
# 自定义窗口类的构造函数
def __init__(self,title='Windows'):
super().__init__()
self.title(title) # 设置窗体标题
self.ft = tf.Font(family='微软雅黑',size = 18) # 定义字体格式
# 定义窗口大小并居中显示
def setSize(self,width=320,height=240):
self.width,self.height = width,height # 将窗口宽高定义为类成员变量
x = (self.winfo_screenwidth()-width) / 2 # 计算窗体屏幕居中位置坐标
y = (self.winfo_screenheight()-height) / 2
self.geometry("%dx%d+%d+%d" % (width,height,x,y)) # 定义窗体大小及位置
self.resizable(0,0) # 禁止调整窗口大小
self.setupUI() # 组件布局
# 禁用/启用组件方法
def setEnabled(self,obj,flag=1):
if flag == 1:
obj['state'] = tk.NORMAL # 启用组件
else:
obj['state'] = tk.DISABLED # 禁用组件
# 组件内容
def setupUI(self):
'''**********容器①***********'''
bt,fm1 = [],tk.Frame(width=self.width)
bt.append(tk.Button(fm1,text='添加文件',command=self.addFile,font=self.ft)) # 创建按钮
bt.append(tk.Button(fm1,text='清空列表',command=self.clearList,font=self.ft))
bt.append(tk.Button(fm1,text='开始整理',command=self.startTask,font=self.ft))
bt.append(tk.Button(fm1,text='查看说明',command=self.viewNotes,font=self.ft))
bt.append(tk.Button(fm1,text='关于',command=self.about,font=self.ft))
for i in range(len(bt)): # 循环布局
bt[i].grid(row=0,column=i,padx=5,pady=5)
fm1.pack()
'''**********容器②***********'''
fm2 = tk.Frame(width=self.width)
form = ttk.Treeview(fm2,height=7,show="headings") # 创建表格
form.bind('<Double-1>', self.set_cell_value) # 双击鼠标左键事件
form.bind('<Button-1>', self.handle_click)
style = ttk.Style()
style.configure("Treeview.Heading", font=self.ft) # 定义表头字体样式
style.configure("Treeview",rowheight=34,font=self.ft) # 定义表格单元字体样式
form['columns'] = ['num','file','times','notes','tasks'] # 定义列
form.column(form['columns'][0],width=70,anchor='center') # 设置列宽度
for ID in form['columns'][1:]:
form.column(ID,width=128,anchor='center')
row_name = ['序号','文件名称','学习时长','笔记记录','作业提交'] # 添加列名
for i in range(len(row_name)):
form.heading(form['columns'][i],text=row_name[i])
form.pack() # 布局
fm2.pack()
'''**********容器③***********'''
fm3 = tk.Frame(width=self.width)
tk.Label(fm3, text="输出文件",font=self.ft).grid(row=0,column=0,padx=5,pady=5)
outfile = tk.Entry(fm3, bd=1, width=28,font=self.ft) # 创建输入框
outfile.insert(0, 'D:/') # 定义默认路径
outfile.grid(row=0,column=1,columnspan=3,padx=5,pady=5) # 布局
bt.append(tk.Button(fm3,text='浏览',command=self.outPath,font=self.ft)) # 创建按钮
bt[-1].grid(row=0,column=4,columnspan=2,padx=5,pady=5) # 布局
fm3.pack()
'''********关联类成员*********'''
self.bt = bt # 定义类成员——按钮
self.flist = [] # 定义类成员——文件列表
self.form = form # 定义类成员——文件表格
self.outfile = outfile # 定义类成员——输入框
self.outpath = outfile.get() # 定义类成员——输出文件路径
'''********默认设置*********'''
self.setEnabled(bt[1],0) # 默认禁用【清楚列表】按钮
self.setEnabled(bt[2],0) # 默认禁用【开始整理】按钮
# 禁止调整表单列宽
def handle_click(self,event):
if self.form.identify_region(event.x, event.y) == "separator":
return "break"
# 设置表格数据方法
def set_cell_value(self,event):
item = self.form.selection() # 获取被选中的行
row = self.form.identify_row(event.y) # 行数据
column = self.form.identify_column(event.x) # 列数据
try:
rn = int(str(row).replace('I','')) # 去除行数据前缀,只留行号
cn = int(str(column).replace('#','')) # 去除列数据前缀,只留列号
except:
pass
else:
if cn > 2 and rn > 0:
def saveedit(event):# 保存数据
if edit.get().isdigit(): # 判断输入框中是否为数字
flag,num = 0,int(edit.get()) # 将字符转数字
self.form.set(item, column=column, value=num) # 将修改好的数据存入单元格
self.flist[rn-1][cn-2] = num # 将修改的参数存入文件列表
if cn == 4: # 如果输入的是【笔记记录】,则自动将【作业提交】填入相同值
self.form.set(item, column='#5', value=num)
self.flist[rn-1][cn-1] = num
for i in range(len(self.flist)): # 循环判断文件列表参数是否设置正确
for j in range(1,len(self.flist[i])):
if self.flist[i][j] <= 0:
flag = 1
if flag == 0: # 若参数设置完成,则启用【开始整理】按钮
self.setEnabled(self.bt[2])
else:
self.setEnabled(self.bt[2],0) # 否则,禁用【开始整理】按钮
edit.destroy() # 销毁输入框组件
edit = tk.Entry(self,width=9,font=self.ft) # 创建输入框
edit.insert(0,self.form.item(item, "values")[cn-1:cn]) # 将原始数据赋值给输入框
edit.place(x=(cn-1)*128-50,y=rn*34+60) # 将输入框与单元格位置重叠
edit.focus_set() # 焦点定位到输入框
edit.bind('<Return>',saveedit) # 绑定回车键事件
edit.bind('<FocusOut>',saveedit) # 绑定脱离焦点事件
# 读取表的科目名称
def getExcelName(self,path):
sheet = xlrd.open_workbook(path).sheet_by_index(0)# 打开指定工作表
for i in range(sheet.ncols): # 按列遍历表头,找到【科目名称】列
if '科目名称' in sheet.cell(0,i).value:
return sheet.cell(1,i).value # 若找到则返回科目名称
return None # 若未找到,则说明是无效文件,返回None
# 添加文件操作
def addFile(self):
# 打开文件选择对话框,可多选
path = tk.filedialog.askopenfilenames(filetypes=[("Excel 工作薄", "*.xlsx"),("Excel 97-2003 工作薄", "*.xls"),("所有文件","*.*")])
for i in range(len(path)):
name = self.getExcelName(path[i]) # 获取科目名称
if name == None: # 排除无效文件
mes.showinfo('提示',path[i]+'为无效文件!') # 弹出提示框
else:
length = len(self.flist)
if str(self.flist).find(path[i]) >= 0: # 排除重复添加
mes.showinfo('提示',path[i]+'文件已在列表中!') # 弹出提示框
continue
self.form.insert('',length,text=length+1,values=(length+1,name,0,0,0)) # 将文件信息添加到表格
self.flist.append([path[i],0,0,0]) # 存储文件路径
self.setEnabled(self.bt[1]) # 启用【清理列表】按钮
if len(self.flist) == 7: # 限制一次最多处理6个文件
mes.showinfo('提示','只能处理'+str(len(self.flist))+'个文件!') # 弹出提示框
self.setEnabled(self.bt[0],0) # 禁用【添加文件】按钮
return None
# 清除列表操作
def clearList(self):
self.flist = [] # 清空文件列表
x = self.form.get_children() # 获取表格数据域
for item in x: # 循环删除数据项
self.form.delete(item)
self.setEnabled(self.bt[0]) # 启用【添加文件】按钮
self.setEnabled(self.bt[1],0) # 禁用【清理列表】按钮
self.setEnabled(self.bt[2],0) # 禁用【开始整理】按钮
# 开始整理操作
def startTask(self):
# 用线程执行整理操作
def runThread():
self.setEnabled(self.bt[2],0) # 禁用【开始整理】按钮
for lis in self.flist:
data = self.readExcel(lis[0])
(filepath,filename) = os.path.split(lis[0])
filename = filename.replace('.xlsx','.xls')
if os.path.exists(self.outpath + filename):
filename = filename.replace('.xls','_new.xls')
self.outExcel(data,self.outpath + filename,lis[1],lis[2],lis[3])
mes.showinfo('提示', '操作成功!')
self.setEnabled(self.bt[2]) # 启用【开始整理】按钮
os.startfile(self.outpath)
try:
_thread.start_new_thread(runThread,())
except:
mes.showinfo('提示', '操作异常!')
# 查看说明操作
def viewNotes(self):
mes.showinfo('说明','添加文件后,在文件列表对应参数上\n通过双击鼠标设置整理参数!')
# 关于操作
def about(self):
mes.showinfo('关于', 'Jason制作\nQQ158095153')
# 修改输出目录
def outPath(self):
path = tk.filedialog.askdirectory() + '/' # 弹出目录选择对话框
if path != '/':
self.outpath = path # 赋值给类成员
self.outfile.delete(0, tk.END) # 清楚输入框的内容
self.outfile.insert(0, self.outpath) # 将新选择的目录存入输入框
# 设置表格样式
def setStyle(self,color=0,bold=False,color_fore=1,font_name='宋体',font_size=12):
style = xlwt.XFStyle()
'''********字体样式*********'''
fnt = xlwt.Font()
fnt.name = font_name
fnt.height = 20 * font_size
fnt.colour_index = color
fnt.bold = bold
style.font = fnt
'''********背景样式*********'''
pattern =xlwt.Pattern()
pattern.pattern = xlwt.Pattern.SOLID_PATTERN
pattern.pattern_fore_colour = color_fore
style.pattern = pattern
'''********对齐样式*********'''
al = xlwt.Alignment()
al.horz = 0x02
al.vert = 0x01
style.alignment = al
'''********边框样式*********'''
bds = xlwt.Borders()
bds.left = bds.right = 1
bds.top = bds.bottom = 1
style.borders = bds
return style
# 按需截取Excel数据
def readExcel(self,path):
flag,sheet = [],xlrd.open_workbook(path).sheet_by_index(0)
for i in range(sheet.ncols): # 查找标记列位置
for j in ['学生姓名','科目代码','科目名称','学习时长','笔记记录','作业提交']:
if j in sheet.cell(0,i).value:
flag.append(i)
lis = []
for i in range(sheet.nrows): # 截取标记列数据
temp = []
for j in flag:
temp.append(sheet.cell(i,j).value)
lis.append(temp)
for i in range(1,len(lis)): # 将数据按【笔记记录】降序排序
for j in range(i+1,len(lis)):
if int(lis[i][4]) < int(lis[j][4]):
lis[i],lis[j] = lis[j],lis[i]
for i in range(1,len(lis)): # 将数据按【作业提交】降序排序
for j in range(i+1,len(lis)):
if int(lis[i][5]) < int(lis[j][5]):
lis[i],lis[j] = lis[j],lis[i]
for i in range(1,len(lis)): # 将数据按【学习时长】降序排序
for j in range(i+1,len(lis)):
if int(lis[i][3]) < int(lis[j][3]):
lis[i],lis[j] = lis[j],lis[i]
return lis # 返回整理完的数据
# 按格式输出截取完的数据
def outExcel(self,lis,path,times,notes,tasks):
style = self.setStyle()
wb = xlwt.Workbook(encoding = 'utf-8')
sheet = wb.add_sheet('export',cell_overwrite_ok=True)
#逐个写入数据到单元格并设置样式
for i in range(len(lis)):
for j in range(len(lis[i])):
sheet.write_merge(i,i,j,j,lis[i][j],style)
#设置列宽
for i in range(len(lis[0])):
sheet.col(i).width = 256 * 10
sheet.col(2).width = 256 * 18
#分析数据是否达标
for i in range(1,len(lis)):
#给学习时长不达标的数据添加底色
data = int(lis[i][3])
if data < times:
for j in range(3):
style = self.setStyle(2,True)
sheet.write_merge(i,i,j,j,lis[i][j],style)
style = self.setStyle(2,True,5)
sheet.write_merge(i,i,3,3,data,style)
#给笔记记录不达标的数据添加底色
data = int(lis[i][4])
if data < notes:
style = self.setStyle(0,False,3)
sheet.write_merge(i,i,4,4,data,style)
#给作业提交不达标的数据添加底色
data = int(lis[i][5])
if data < notes:
style = self.setStyle(0,False,7)
sheet.write_merge(i,i,5,5,data,style)
#保存导出文件
wb.save(path)
if __name__ == '__main__':
win = MyApp('华莘学堂数据整理V1.1-Jason制作') # 创建一个自定义窗体
win.setSize(600,400) # 设置窗体大小
win.mainloop() # 进入消息循环
Python处理excle表格
最新推荐文章于 2024-07-23 20:19:57 发布