目录
一、背景介绍
二、代码及成果
三、代码分析
四、打包为exe
本实例主要使用了QTableWidget,openpyxl,利用openpyxl实现对excel文件的读取,QTableWidget实现对excel文件内容读取后,择需要信息在窗体上显示。之所以写该代码,是因为在同一项目的设计过程中(本人的工作是水工设计),常使用纬地软件设计多段道路,每段道路的数模都会生成土方计算表,软件的默认命名难以区分桩号,且挖填方量需要依次点开各个excel文件查看,不太方便。该段代码专门处理纬地软件生成的土方计算表,功能有
- 自动获取桩号,并批量将文件以起止桩号重命名
- 自动批量计算总挖填方,并将各段路的方量显示在QTableWidget中。
一、背景介绍
纬地软件生成的土方计算表通常会给定默认命名(如下图),我懒得一个个去改,但是仅根据默认据文件名难以确定是那段桩号的文件。如果改成以桩号来命名,方便最后统计工程量
纬地生成的土方计算表,同一个工作簿内通常有多个工作表,但最后一个是没有用的。需要从每个工作表的A列提取桩号,然后再倒数第二个工作表内提取累计的挖方量和填方量。
二、代码及成果
主要代码及操作界面如下
from PyQt5.QtWidgets import QWidget, QApplication, QFileDialog, QPushButton, QLineEdit, QHBoxLayout, \ QVBoxLayout, QLabel, QTableWidget, QHeaderView, QTableWidgetItem from PyQt5.QtGui import QIcon import sys, os, openpyxl path = os.path.dirname(os.path.dirname(__file__)) class MyWin(QWidget): def __init__(self): super(MyWin, self).__init__() self.initui() def initui(self): self.setWindowTitle('土方计算表计算') self.setWindowIcon(QIcon(r'%s\4.mypython\chuan.ico' % path)) self.resize(600, 500) btn_open = QPushButton('打开文件') btn_rename = QPushButton('重命名') lbl_excavation = QLabel('总挖方量', self) lineedit_excavation = QLineEdit() lbl_fill = QLabel('总填方量', self) lineedit_fill = QLineEdit() tableWidget = QTableWidget() # =======布局====== hbx1 = QHBoxLayout() hbx1.addWidget(lbl_excavation) hbx1.addWidget(lineedit_excavation) hbx1.addWidget(lbl_fill) hbx1.addWidget(lineedit_fill) hbx1.addWidget(btn_open) hbx1.addWidget(btn_rename) vbx = QVBoxLayout() vbx.addWidget(tableWidget) vbx.addLayout(hbx1) self.setLayout(vbx) btn_rename.clicked.connect(self.rename_files) btn_open.clicked.connect(lambda: self.open_files(lineedit_excavation, lineedit_fill, tableWidget)) # 调用函数 # btn_open.clicked.connect(lambda: self.table_init(tableWidget)) def rename_files(self): f_names, _ = QFileDialog.getOpenFileNames(self, '打开文件', 'D:\\', 'XLSX Files(*.xlsx)') if f_names: # f_names返回值为列表,元素为文件地址 for f_name in f_names: wb = openpyxl.load_workbook(f_name) # 打开工作簿 stake_numbers = [] for sheetname in wb.sheetnames[:-1]: # 依次获取工作表,提取起止桩号。最后一个工作表无内容,因此舍去 ws = wb[sheetname] stake_numbers += [i.value for i in ws['A'] if i.value != None][4:-2] # 获取桩号列表 start_station, end_station = stake_numbers[0], stake_numbers[-1] # 赋值起点桩号和终点桩号 os.rename(f_name.split('/')[-1], '%s~%s.xlsx' % (start_station, end_station)) # os.rename(旧名字,新名字) def open_files(self, lineedit_excavation, lineedit_fill, table): f_names, _ = QFileDialog.getOpenFileNames(self, '打开文件', 'D:\\', 'XLSX Files(*.xlsx)') if f_names: total_excavation, total_fill = 0, 0 self.quantity_all = [] # 起止桩号和挖填方 for f_name in f_names: wb = openpyxl.load_workbook(f_name, data_only=True) # 只返回数值,不返回公式 # cell_range = ws['A1':'C2'] #通过切片访问多个单元格 ws = wb[wb.sheetnames[-2]] quantity_fill, quantity_excavation = ws['R37'].value, ws['E37'].value total_excavation += quantity_excavation total_fill += quantity_fill stake_numbers = [] for sheetname in wb.sheetnames[:-1]: # 依次获取工作表,提取起止桩号。最后一个工作表无内容,因此舍去 ws = wb[sheetname] stake_numbers += [i.value for i in ws['A'] if i.value != None][4:-2] # 获取桩号列表 start_station, end_station = stake_numbers[0], stake_numbers[-1] # 赋值起点桩号和终点桩号 self.quantity_all.append( [start_station, end_station, round(quantity_excavation, 2), round(quantity_fill, 2)]) lineedit_excavation.setText(str(round(total_excavation, 2))) # 必须转换为字符串才能赋值 lineedit_fill.setText(str(round(total_fill, 2))) # 必须转换为字符串才能赋值 self.table_init(table) def table_init(self, table): table.setColumnCount(4) table.setRowCount(len(self.quantity_all)) table.setHorizontalHeaderLabels(['起点桩号', '终点桩号', '挖方量', '填方量']) table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) # print(self.quantity_all) for i in range(len(self.quantity_all)): for j in range(4): newitem = QTableWidgetItem(str(self.quantity_all[i][j])) # 必须转成字符串,否则不报错,也不显示 table.setItem(i, j, newitem) if __name__ == '__main__': app = QApplication(sys.argv) win = MyWin() win.show() sys.exit(app.exec_())
处理后的文件:
三、代码分析
f_names, _ = QFileDialog.getOpenFileNames(self, '打开文件', 'D:\\', 'XLSX Files(*.xlsx)')
这里_为占位符,f_names范围的是元组,元素为各个文件的绝对路径。如果使用getOpenFileName就只能打开单个文件,getOpenFileNames可以打开多个文件,实现批量处理。
stake_numbers += [i.value for i in ws['A'] if i.value != None][4:-2]
这里使用了列表生成器,if i.value != None可以将空白单元格排除掉
wb = openpyxl.load_workbook(f_name, data_only=True)
这里必须加data_only=True,因为excel里有些单元格是有公式的,不加这个返回的就是单元格的公式而不是单元格的值。
table.setRowCount(len(self.quantity_all)) #行数随文件自动变化 table.setHorizontalHeaderLabels(['起点桩号', '终点桩号', '挖方量', '填方量']) table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) #列宽自动拉伸
四、打包为exe
可以使用pyinstaller将代码打包为exe。windows平台打包后,想要运行时不弹出cmd窗口,步骤如下,
1.将文件名改为pyw后缀,例如“表格.py”改为“表格.pyw”
2.启动cmd,切换到py文件所在目录
3. 输入pyinstaller -F 表格.pyw --noconsole 这里的“表格.pyw”是要打包的py文件名,“--noconsole”必须要加上,这样程序运行时不会弹出cmd窗口