Python处理excle表格

# 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()                        # 进入消息循环
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值