├── README.md ├── ToolsPackage.py ├── ToolsUnit.py ├── UI_lan.py ├── UI_lan.ui ├── requirements.txt ├── run.py ├── run_v5.0.exe ├── show └── show.mp4.gif └── utils.py /README.md: -------------------------------------------------------------------------------- 1 | # pyqt5-excel 2 | 本项目是使用PyQt5制作的工具,并将其打包成exe文件,可以在任意windows上运行 3 | 4 | ## 展示效果 5 | ![image](https://github.com/makalo/pyqt5-excel/blob/windows/show/show.mp4.gif) 6 | 7 | ## 项目功能 8 | 1. 支持根据关键词拆分excel成多个excel 9 | 10 | ## 组件功能 11 | 1. 支持文件浏览选取 12 | 2. 支持多线程实时显示日志 13 | 3. 支持多线程进度条 14 | 15 | ## 安装运行 16 | 17 | ### 准备工具 18 | 1. [Qt Designer Setup.exe](https://pan.baidu.com/s/1rpjv6gXCFKcRTKXZU3RqLA) 用于可视化设计界面(提取码:3cm6) 19 | 2. [Setup Factory](https://pan.baidu.com/s/1cbqwL1M3MxDjO2UQJ6HpXA) 用于生成exe安装文件(提取码:v543) 20 | 21 | ### 安装环境 22 | ``` 23 | conda create -n pyqt5 python=3.6 24 | conda activate pyqt5 25 | pip install -r requirements.txt 26 | ``` 27 | ### 运行 28 | ``` 29 | python run.py 30 | ``` 31 | ### 设计自己的界面 32 | 1. 安装 Qt Designer Setup.exe 33 | 2. 可以重新设计也可以加载之前设计好的文件 [UI_lan.ui](./UI_lan.ui) 34 | 3. 保存成.ui文件,运行 pyuic5 -o UI_lan.py UI_lan.ui 生成python文件 35 | 36 | ### 打包成exe 37 | ``` 38 | pyinstaller -F -w run.py 39 | ``` 40 | 1. 此时在dist目录下面就是生成的exe 41 | 2. 将exe拷贝到上一级目录下(因为依赖文件在上一级目录下,避免运行exe的时候找不到依赖文件;也可以将exe和依赖文件单独放在一个新的文件夹里面) 42 | 43 | ### 将exe打包成安装包 44 | 1. [安装Setup Factory并制作exe](https://blog.csdn.net/u010188178/article/details/82500833) 45 | 2. 会生成setup.exe 双击就可以安装了 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /ToolsPackage.py: -------------------------------------------------------------------------------- 1 | 2 | from PyQt5.QtWidgets import * 3 | from PyQt5.QtGui import * 4 | from PyQt5.QtCore import * 5 | from ToolsUnit import split_excel 6 | import os 7 | 8 | class splitThread(QThread): 9 | split_signal = pyqtSignal(int) #创建信号 10 | split_signal_lcd = pyqtSignal(int) #创建信号 11 | 12 | def __init__(self, infos): 13 | super(splitThread, self).__init__() 14 | self.infos = infos 15 | def run(self): 16 | files = list(self.infos.keys()) 17 | for i,base_name in enumerate(files): 18 | f = self.infos[base_name]['path'] 19 | print('正在处理:{}'.format(f)) 20 | # try: 21 | base_info = self.infos[base_name]['sheet_names'] 22 | wbs_split,names_split = split_excel(f,base_info = base_info,signal = self.split_signal) 23 | root = os.path.splitext(f)[0] 24 | if not os.path.exists(root): 25 | os.makedirs(root) 26 | for wb,name in zip(wbs_split,names_split): 27 | path = os.path.join(root,name+'_'+base_name) 28 | wb.save(path) 29 | print('保存路径 {}'.format(root)) 30 | print('处理完成 {}'.format(base_name)) 31 | 32 | self.split_signal_lcd.emit(i+1) 33 | # except: 34 | # print('拆分{}出现错误'.format(base_name)) 35 | # self.exit(0) 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /ToolsUnit.py: -------------------------------------------------------------------------------- 1 | from openpyxl import Workbook, load_workbook,styles 2 | from openpyxl.formula.translate import Translator 3 | import os 4 | import copy 5 | import math 6 | from openpyxl.utils import get_column_letter 7 | from utils import get_key,get_merge_cell_list,get_merge_map,set_style,assign_style,idx2letter 8 | 9 | def split_excel(path,base_info,signal = None): 10 | wb = load_workbook(filename=path) 11 | sheet_names = list(base_info.keys()) 12 | valid_sheets = [] 13 | invalid_sheets = [] 14 | for s in sheet_names: 15 | base = base_info[s] 16 | if len(base) != 0 and base[0][0] != '': 17 | valid_sheets.append(s) 18 | else: 19 | invalid_sheets.append(s) 20 | 21 | dict_idx = get_key(wb,valid_sheets,base_info) 22 | 23 | 24 | wbs_split = [] 25 | names_split = [] 26 | 27 | num_keys = len(list(dict_idx.keys())) 28 | count = 0 29 | for k,dict_sheet in dict_idx.items(): 30 | print('开始拆关键词:{}'.format(k)) 31 | wb_tmp = Workbook() 32 | wb_tmp.remove(wb_tmp[wb_tmp.sheetnames[0]]) 33 | 34 | for sheet,idxes in dict_sheet.items(): 35 | ws = wb[sheet] 36 | num_row = ws.max_row 37 | num_column = ws.max_column 38 | 39 | _,rg = base_info[sheet] 40 | head_idx = list(range(1,rg[0])) 41 | tail_idx = list(range(num_row+1 if rg[1] == 'last' else rg[1]+1,num_row+1)) 42 | 43 | # ws_rows = list(ws.values) 44 | ws_tmp = wb_tmp.create_sheet(sheet) 45 | idxes = head_idx+idxes+tail_idx 46 | 47 | #======合并单元格======= 48 | merge_idx = ws.merged_cells 49 | merge_idx = get_merge_cell_list(merge_idx) 50 | for m_idx in merge_idx: 51 | map_idx = get_merge_map(m_idx,idxes) 52 | if map_idx is not None: 53 | ws_tmp.merge_cells(map_idx) 54 | #======合并单元格======= 55 | 56 | for i in range(1,len(idxes)+1): 57 | idx = idxes[i-1] 58 | # print(ws.row_dimensions[idx].height) 59 | # ws_tmp.append(ws_rows[idx-1]) 60 | for j in range(1,num_column+1): 61 | ws_tmp.row_dimensions[i].height = ws.row_dimensions[idx].height 62 | ws_tmp.column_dimensions[get_column_letter(j)].width = ws.column_dimensions[get_column_letter(j)].width 63 | cell = ws.cell(row=idx, column=j) 64 | value = cell.value 65 | #============公式===== 66 | if isinstance(value,str) and '=' in value: 67 | value = Translator(value, origin=idx2letter([idx,j])).translate_formula(idx2letter([i,j])) 68 | #============公式===== 69 | cell_tmp = ws_tmp.cell(row=i, column=j,value = value) 70 | assign_style(cell_tmp,cell) 71 | # set_style(ws_tmp) 72 | for sheet in invalid_sheets: 73 | ws = wb[sheet] 74 | num_row = ws.max_row 75 | num_column = ws.max_column 76 | ws_tmp = wb_tmp.create_sheet(sheet) 77 | rows = ws.values 78 | for row in rows: 79 | ws_tmp.append(row) 80 | 81 | 82 | wbs_split.append(wb_tmp) 83 | names_split.append(k) 84 | count += 1 85 | if signal is not None: 86 | proess = count / num_keys * 100 87 | signal.emit(int(proess)) 88 | wb.close() 89 | return wbs_split,names_split 90 | 91 | 92 | if __name__ == "__main__": 93 | # base_info = {'省级产业集聚区地下水污染防治有关工作开展情况调度表':[[2,2],[3,'last']], 94 | # '以化工等行业为主的产业集聚区':[]} 95 | base_info = {'其他':[],'数据字典':[],'Sheet1':[[3,2],[4,33]]} 96 | wbs_split,names_split = split_excel('20210223.xlsx',base_info) 97 | root = './debug' 98 | if not os.path.exists(root): 99 | os.makedirs(root) 100 | for wb,name in zip(wbs_split,names_split): 101 | print(name) 102 | path = os.path.join(root,name+'.xlsx') 103 | wb.save(path) 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /UI_lan.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'UI_lan.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.15.2 6 | # 7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is 8 | # run again. Do not edit this file unless you know what you are doing. 9 | 10 | 11 | from PyQt5 import QtCore, QtGui, QtWidgets 12 | 13 | 14 | class Ui_MainWindow(object): 15 | def setupUi(self, MainWindow): 16 | MainWindow.setObjectName("MainWindow") 17 | MainWindow.resize(1129, 804) 18 | self.centralwidget = QtWidgets.QWidget(MainWindow) 19 | self.centralwidget.setObjectName("centralwidget") 20 | self.tabWidget = QtWidgets.QTabWidget(self.centralwidget) 21 | self.tabWidget.setGeometry(QtCore.QRect(10, 10, 1141, 771)) 22 | self.tabWidget.setObjectName("tabWidget") 23 | self.tab = QtWidgets.QWidget() 24 | self.tab.setObjectName("tab") 25 | self.groupBox_3 = QtWidgets.QGroupBox(self.tab) 26 | self.groupBox_3.setGeometry(QtCore.QRect(10, 10, 511, 721)) 27 | self.groupBox_3.setObjectName("groupBox_3") 28 | self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.groupBox_3) 29 | self.verticalLayout_2.setObjectName("verticalLayout_2") 30 | self.listWidget = QtWidgets.QListWidget(self.groupBox_3) 31 | self.listWidget.setObjectName("listWidget") 32 | self.verticalLayout_2.addWidget(self.listWidget) 33 | self.groupBox_4 = QtWidgets.QGroupBox(self.tab) 34 | self.groupBox_4.setGeometry(QtCore.QRect(530, 360, 581, 371)) 35 | self.groupBox_4.setObjectName("groupBox_4") 36 | self.textBrowserlog = QtWidgets.QTextBrowser(self.groupBox_4) 37 | self.textBrowserlog.setGeometry(QtCore.QRect(10, 30, 561, 291)) 38 | self.textBrowserlog.setObjectName("textBrowserlog") 39 | self.progressBar = QtWidgets.QProgressBar(self.groupBox_4) 40 | self.progressBar.setGeometry(QtCore.QRect(90, 330, 491, 23)) 41 | self.progressBar.setProperty("value", 0) 42 | self.progressBar.setObjectName("progressBar") 43 | self.lcdNumber = QtWidgets.QLCDNumber(self.groupBox_4) 44 | self.lcdNumber.setGeometry(QtCore.QRect(20, 330, 64, 23)) 45 | self.lcdNumber.setObjectName("lcdNumber") 46 | self.groupBox = QtWidgets.QGroupBox(self.tab) 47 | self.groupBox.setGeometry(QtCore.QRect(528, 42, 581, 311)) 48 | self.groupBox.setObjectName("groupBox") 49 | self.pushButtonbrowse = QtWidgets.QPushButton(self.groupBox) 50 | self.pushButtonbrowse.setGeometry(QtCore.QRect(70, 60, 113, 51)) 51 | self.pushButtonbrowse.setObjectName("pushButtonbrowse") 52 | self.pushButtonclear = QtWidgets.QPushButton(self.groupBox) 53 | self.pushButtonclear.setGeometry(QtCore.QRect(180, 60, 113, 51)) 54 | self.pushButtonclear.setObjectName("pushButtonclear") 55 | self.pushButtonselall = QtWidgets.QPushButton(self.groupBox) 56 | self.pushButtonselall.setGeometry(QtCore.QRect(290, 60, 113, 51)) 57 | self.pushButtonselall.setObjectName("pushButtonselall") 58 | self.pushButtonload = QtWidgets.QPushButton(self.groupBox) 59 | self.pushButtonload.setGeometry(QtCore.QRect(400, 60, 113, 51)) 60 | self.pushButtonload.setObjectName("pushButtonload") 61 | self.pushButtonsplit = QtWidgets.QPushButton(self.groupBox) 62 | self.pushButtonsplit.setGeometry(QtCore.QRect(70, 140, 113, 51)) 63 | self.pushButtonsplit.setObjectName("pushButtonsplit") 64 | self.pushButtonmerge = QtWidgets.QPushButton(self.groupBox) 65 | self.pushButtonmerge.setGeometry(QtCore.QRect(180, 140, 113, 51)) 66 | self.pushButtonmerge.setObjectName("pushButtonmerge") 67 | self.comboBoxfiletype = QtWidgets.QComboBox(self.groupBox) 68 | self.comboBoxfiletype.setGeometry(QtCore.QRect(80, 220, 101, 26)) 69 | self.comboBoxfiletype.setObjectName("comboBoxfiletype") 70 | self.pushButtonanalyse = QtWidgets.QPushButton(self.groupBox) 71 | self.pushButtonanalyse.setGeometry(QtCore.QRect(290, 140, 113, 51)) 72 | self.pushButtonanalyse.setObjectName("pushButtonanalyse") 73 | self.pushButtonmakalo = QtWidgets.QPushButton(self.groupBox) 74 | self.pushButtonmakalo.setGeometry(QtCore.QRect(400, 140, 113, 51)) 75 | self.pushButtonmakalo.setObjectName("pushButtonmakalo") 76 | self.pushButton_link = QtWidgets.QPushButton(self.groupBox) 77 | self.pushButton_link.setGeometry(QtCore.QRect(250, 210, 261, 51)) 78 | self.pushButton_link.setObjectName("pushButton_link") 79 | self.tabWidget.addTab(self.tab, "") 80 | self.tab_2 = QtWidgets.QWidget() 81 | self.tab_2.setObjectName("tab_2") 82 | self.groupBox_2 = QtWidgets.QGroupBox(self.tab_2) 83 | self.groupBox_2.setGeometry(QtCore.QRect(0, 10, 1091, 121)) 84 | self.groupBox_2.setObjectName("groupBox_2") 85 | self.pushButton_confirm_idx = QtWidgets.QPushButton(self.groupBox_2) 86 | self.pushButton_confirm_idx.setGeometry(QtCore.QRect(910, 80, 113, 32)) 87 | self.pushButton_confirm_idx.setObjectName("pushButton_confirm_idx") 88 | self.pushButton_clear_idx = QtWidgets.QPushButton(self.groupBox_2) 89 | self.pushButton_clear_idx.setGeometry(QtCore.QRect(910, 30, 113, 31)) 90 | self.pushButton_clear_idx.setObjectName("pushButton_clear_idx") 91 | self.comboBox_wb = QtWidgets.QComboBox(self.groupBox_2) 92 | self.comboBox_wb.setGeometry(QtCore.QRect(110, 30, 481, 26)) 93 | self.comboBox_wb.setObjectName("comboBox_wb") 94 | self.comboBox_ws = QtWidgets.QComboBox(self.groupBox_2) 95 | self.comboBox_ws.setGeometry(QtCore.QRect(110, 80, 481, 26)) 96 | self.comboBox_ws.setObjectName("comboBox_ws") 97 | self.label_keyidx = QtWidgets.QLabel(self.groupBox_2) 98 | self.label_keyidx.setGeometry(QtCore.QRect(610, 30, 91, 31)) 99 | self.label_keyidx.setObjectName("label_keyidx") 100 | self.label_range = QtWidgets.QLabel(self.groupBox_2) 101 | self.label_range.setGeometry(QtCore.QRect(610, 80, 91, 31)) 102 | self.label_range.setObjectName("label_range") 103 | self.comboBox_x = QtWidgets.QComboBox(self.groupBox_2) 104 | self.comboBox_x.setGeometry(QtCore.QRect(730, 30, 71, 26)) 105 | self.comboBox_x.setObjectName("comboBox_x") 106 | self.comboBox_y = QtWidgets.QComboBox(self.groupBox_2) 107 | self.comboBox_y.setGeometry(QtCore.QRect(800, 30, 91, 26)) 108 | self.comboBox_y.setObjectName("comboBox_y") 109 | self.comboBox_r1 = QtWidgets.QComboBox(self.groupBox_2) 110 | self.comboBox_r1.setGeometry(QtCore.QRect(730, 80, 71, 26)) 111 | self.comboBox_r1.setObjectName("comboBox_r1") 112 | self.comboBox_r2 = QtWidgets.QComboBox(self.groupBox_2) 113 | self.comboBox_r2.setGeometry(QtCore.QRect(800, 80, 91, 26)) 114 | self.comboBox_r2.setObjectName("comboBox_r2") 115 | self.checkBox_book = QtWidgets.QCheckBox(self.groupBox_2) 116 | self.checkBox_book.setGeometry(QtCore.QRect(10, 30, 81, 31)) 117 | self.checkBox_book.setChecked(False) 118 | self.checkBox_book.setObjectName("checkBox_book") 119 | self.checkBox_sheet = QtWidgets.QCheckBox(self.groupBox_2) 120 | self.checkBox_sheet.setGeometry(QtCore.QRect(10, 80, 81, 31)) 121 | self.checkBox_sheet.setChecked(False) 122 | self.checkBox_sheet.setObjectName("checkBox_sheet") 123 | self.groupBox_5 = QtWidgets.QGroupBox(self.tab_2) 124 | self.groupBox_5.setGeometry(QtCore.QRect(0, 139, 1111, 591)) 125 | self.groupBox_5.setObjectName("groupBox_5") 126 | self.tableWidget = QtWidgets.QTableWidget(self.groupBox_5) 127 | self.tableWidget.setGeometry(QtCore.QRect(0, 30, 1111, 581)) 128 | self.tableWidget.setObjectName("tableWidget") 129 | self.tableWidget.setColumnCount(0) 130 | self.tableWidget.setRowCount(0) 131 | self.tabWidget.addTab(self.tab_2, "") 132 | MainWindow.setCentralWidget(self.centralwidget) 133 | self.menubar = QtWidgets.QMenuBar(MainWindow) 134 | self.menubar.setGeometry(QtCore.QRect(0, 0, 1129, 22)) 135 | self.menubar.setObjectName("menubar") 136 | MainWindow.setMenuBar(self.menubar) 137 | self.statusbar = QtWidgets.QStatusBar(MainWindow) 138 | self.statusbar.setObjectName("statusbar") 139 | MainWindow.setStatusBar(self.statusbar) 140 | 141 | self.retranslateUi(MainWindow) 142 | self.tabWidget.setCurrentIndex(0) 143 | QtCore.QMetaObject.connectSlotsByName(MainWindow) 144 | 145 | def retranslateUi(self, MainWindow): 146 | _translate = QtCore.QCoreApplication.translate 147 | MainWindow.setWindowTitle(_translate("MainWindow", "由makalo提供技术支持")) 148 | self.groupBox_3.setTitle(_translate("MainWindow", "兰神专属")) 149 | self.groupBox_4.setTitle(_translate("MainWindow", "显示区")) 150 | self.groupBox.setTitle(_translate("MainWindow", "功能区")) 151 | self.pushButtonbrowse.setText(_translate("MainWindow", "...")) 152 | self.pushButtonclear.setText(_translate("MainWindow", "clear")) 153 | self.pushButtonselall.setText(_translate("MainWindow", "select")) 154 | self.pushButtonload.setText(_translate("MainWindow", "load")) 155 | self.pushButtonsplit.setText(_translate("MainWindow", "split")) 156 | self.pushButtonmerge.setText(_translate("MainWindow", "merge")) 157 | self.pushButtonanalyse.setText(_translate("MainWindow", "analyse")) 158 | self.pushButtonmakalo.setText(_translate("MainWindow", "makalo")) 159 | self.pushButton_link.setText(_translate("MainWindow", "联系makalo 提需求")) 160 | self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "Home")) 161 | self.groupBox_2.setTitle(_translate("MainWindow", "参数配置")) 162 | self.pushButton_confirm_idx.setText(_translate("MainWindow", "confirm")) 163 | self.pushButton_clear_idx.setText(_translate("MainWindow", "clear")) 164 | self.label_keyidx.setText(_translate("MainWindow", " idx")) 165 | self.label_range.setText(_translate("MainWindow", " range")) 166 | self.checkBox_book.setText(_translate("MainWindow", "books")) 167 | self.checkBox_sheet.setText(_translate("MainWindow", "sheets")) 168 | self.groupBox_5.setTitle(_translate("MainWindow", "excel显示")) 169 | self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("MainWindow", "Data")) 170 | -------------------------------------------------------------------------------- /UI_lan.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 1129 10 | 804 11 | 12 | 13 | 14 | 由makalo提供技术支持 15 | 16 | 17 | 18 | 19 | 20 | 10 21 | 10 22 | 1141 23 | 771 24 | 25 | 26 | 27 | 0 28 | 29 | 30 | 31 | Home 32 | 33 | 34 | 35 | 36 | 10 37 | 10 38 | 511 39 | 721 40 | 41 | 42 | 43 | 兰神专属 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 530 55 | 360 56 | 581 57 | 371 58 | 59 | 60 | 61 | 显示区 62 | 63 | 64 | 65 | 66 | 10 67 | 30 68 | 561 69 | 291 70 | 71 | 72 | 73 | 74 | 75 | 76 | 90 77 | 330 78 | 491 79 | 23 80 | 81 | 82 | 83 | 0 84 | 85 | 86 | 87 | 88 | 89 | 20 90 | 330 91 | 64 92 | 23 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 528 101 | 42 102 | 581 103 | 311 104 | 105 | 106 | 107 | 功能区 108 | 109 | 110 | 111 | 112 | 70 113 | 60 114 | 113 115 | 51 116 | 117 | 118 | 119 | ... 120 | 121 | 122 | 123 | 124 | 125 | 180 126 | 60 127 | 113 128 | 51 129 | 130 | 131 | 132 | clear 133 | 134 | 135 | 136 | 137 | 138 | 290 139 | 60 140 | 113 141 | 51 142 | 143 | 144 | 145 | select 146 | 147 | 148 | 149 | 150 | 151 | 400 152 | 60 153 | 113 154 | 51 155 | 156 | 157 | 158 | load 159 | 160 | 161 | 162 | 163 | 164 | 70 165 | 140 166 | 113 167 | 51 168 | 169 | 170 | 171 | split 172 | 173 | 174 | 175 | 176 | 177 | 180 178 | 140 179 | 113 180 | 51 181 | 182 | 183 | 184 | merge 185 | 186 | 187 | 188 | 189 | 190 | 80 191 | 220 192 | 101 193 | 26 194 | 195 | 196 | 197 | 198 | 199 | 200 | 290 201 | 140 202 | 113 203 | 51 204 | 205 | 206 | 207 | analyse 208 | 209 | 210 | 211 | 212 | 213 | 400 214 | 140 215 | 113 216 | 51 217 | 218 | 219 | 220 | makalo 221 | 222 | 223 | 224 | 225 | 226 | 250 227 | 210 228 | 261 229 | 51 230 | 231 | 232 | 233 | 联系makalo 提需求 234 | 235 | 236 | 237 | 238 | 239 | 240 | Data 241 | 242 | 243 | 244 | 245 | 0 246 | 10 247 | 1091 248 | 121 249 | 250 | 251 | 252 | 参数配置 253 | 254 | 255 | 256 | 257 | 910 258 | 80 259 | 113 260 | 32 261 | 262 | 263 | 264 | confirm 265 | 266 | 267 | 268 | 269 | 270 | 910 271 | 30 272 | 113 273 | 31 274 | 275 | 276 | 277 | clear 278 | 279 | 280 | 281 | 282 | 283 | 110 284 | 30 285 | 481 286 | 26 287 | 288 | 289 | 290 | 291 | 292 | 293 | 110 294 | 80 295 | 481 296 | 26 297 | 298 | 299 | 300 | 301 | 302 | 303 | 610 304 | 30 305 | 91 306 | 31 307 | 308 | 309 | 310 | idx 311 | 312 | 313 | 314 | 315 | 316 | 610 317 | 80 318 | 91 319 | 31 320 | 321 | 322 | 323 | range 324 | 325 | 326 | 327 | 328 | 329 | 730 330 | 30 331 | 71 332 | 26 333 | 334 | 335 | 336 | 337 | 338 | 339 | 800 340 | 30 341 | 91 342 | 26 343 | 344 | 345 | 346 | 347 | 348 | 349 | 730 350 | 80 351 | 71 352 | 26 353 | 354 | 355 | 356 | 357 | 358 | 359 | 800 360 | 80 361 | 91 362 | 26 363 | 364 | 365 | 366 | 367 | 368 | 369 | 10 370 | 30 371 | 81 372 | 31 373 | 374 | 375 | 376 | books 377 | 378 | 379 | false 380 | 381 | 382 | 383 | 384 | 385 | 10 386 | 80 387 | 81 388 | 31 389 | 390 | 391 | 392 | sheets 393 | 394 | 395 | false 396 | 397 | 398 | 399 | 400 | 401 | 402 | 0 403 | 139 404 | 1111 405 | 591 406 | 407 | 408 | 409 | excel显示 410 | 411 | 412 | 413 | 414 | 0 415 | 30 416 | 1111 417 | 581 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 0 429 | 0 430 | 1129 431 | 22 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | PyQt5 2 | pyqtchart 3 | qdarkstyle 4 | openpyxl 5 | pyinstaller -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import time 4 | from PyQt5 import QtGui, QtWidgets, QtCore 5 | from PyQt5.QtChart import QChart, QChartView, QPieSeries, QPieSlice 6 | from PyQt5.QtCore import Qt, QTimer, QCoreApplication, QSettings,pyqtSignal,QObject 7 | from PyQt5.QtGui import QPixmap, QPainter 8 | from PyQt5.QtWidgets import QFileDialog, QTableWidgetItem, QGridLayout, QSplashScreen 9 | from PyQt5.QtWidgets import QVBoxLayout,QMessageBox 10 | from PyQt5.QtGui import QTextCursor 11 | import UI_lan 12 | from ToolsPackage import splitThread 13 | from openpyxl import load_workbook 14 | from utils import get_column_letter,assign_style_qt,get_merge_cell_list 15 | import webbrowser 16 | import qdarkstyle 17 | class Stream(QObject): 18 | """Redirects console output to text widget.""" 19 | newText = pyqtSignal(str) 20 | 21 | def write(self, text): 22 | QtWidgets.QApplication.processEvents() 23 | self.newText.emit(str(text)) 24 | 25 | class anaxcelhandler(QtWidgets.QMainWindow, UI_lan.Ui_MainWindow): 26 | 27 | def __init__(self, parent=None): 28 | super(anaxcelhandler, self).__init__(parent) 29 | if getattr(sys, 'frozen', False): 30 | self.frozen = 'ever so' 31 | self.bundle_dir = sys._MEIPASS 32 | else: 33 | self.bundle_dir = os.path.dirname(os.path.abspath(__file__)) 34 | self.setupUi(self) 35 | #self.setWindowIcon(QtGui.QIcon(self.bundle_dir + '/icons/icon.png')) 36 | #self.setStyleSheet(open("Dark/darkstyle.qss", "r").read()) 37 | # self.setStyleSheet(open("qss/1.qss", "r").read()) 38 | 39 | self.listWidget.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) 40 | self.pushButtonbrowse.clicked.connect(self.openFileNamesDialog) 41 | self.pushButtonclear.clicked.connect(self.clearwidget) 42 | self.pushButtonselall.clicked.connect(self.selectall) 43 | self.pushButtonload.clicked.connect(self.LoadProcess) 44 | self.pushButtonsplit.clicked.connect(self.SplitProcess) 45 | self.pushButtonmerge.clicked.connect(self.mergeProcess) 46 | self.pushButtonanalyse.clicked.connect(self.analyseProcess) 47 | self.pushButtonmakalo.clicked.connect(self.makaloProcess) 48 | self.pushButton_link.clicked.connect(self.linkProcess) 49 | 50 | self.statusbar.showMessage('兰神专属') 51 | self.comboBoxfiletype.addItems(['xlsx','xls']) 52 | 53 | #==========log===== 54 | sys.stdout = Stream(newText=self.onUpdateText) 55 | #==========log===== 56 | 57 | #==========show==== 58 | self.flag_confirm = False 59 | self.activate_file = [None,None] 60 | self.comboBox_wb.activated.connect(self.wbActivated) 61 | self.comboBox_ws.activated.connect(self.wsActivated) 62 | self.tableWidget.itemClicked.connect(self.handleItemClick) 63 | self.pushButton_clear_idx.clicked.connect(self.clear_idx) 64 | self.pushButton_confirm_idx.clicked.connect(self.confirm_idx) 65 | #==========show==== 66 | 67 | #==========context=== 68 | self.infos = {} 69 | self.infos_bak = {} 70 | 71 | def use_palette(self): 72 | self.setWindowTitle("设置背景图片") 73 | window_pale = QtGui.QPalette() 74 | window_pale.setBrush(self.backgroundRole(), QtGui.QBrush(QtGui.QPixmap(self.bundle_dir + '/icons/bg.jpeg'))) 75 | self.setPalette(window_pale) 76 | def onUpdateText(self, text): 77 | """Write console output to text widget.""" 78 | cursor = self.textBrowserlog.textCursor() 79 | cursor.movePosition(QTextCursor.End) 80 | cursor.insertText(text) 81 | self.textBrowserlog.setTextCursor(cursor) 82 | self.textBrowserlog.ensureCursorVisible() 83 | 84 | 85 | def openFileNamesDialog(self): 86 | options = QFileDialog.Options() 87 | options |= QFileDialog.DontUseNativeDialog 88 | filterxls = "XLS (*.xls *.XLS)" 89 | filterxlsx = "XLSX (*.xlsx *.XLSX)" 90 | print('打开文件') 91 | if self.comboBoxfiletype.currentIndex() == 1: 92 | files, _ = QFileDialog.getOpenFileNames(self, "Select XLS Files", filter=filterxls, options=options) 93 | if files: 94 | for file in files: 95 | self.listWidget.addItem(file) 96 | elif self.comboBoxfiletype.currentIndex() == 0: 97 | files, _ = QFileDialog.getOpenFileNames(self, "Select XLSX Files", filter=filterxlsx, options=options) 98 | if files: 99 | for file in files: 100 | self.listWidget.addItem(file) 101 | 102 | def clearwidget(self): 103 | self.listWidget.clear() 104 | self.tableWidget.clear() 105 | self.tableWidget.setColumnCount(0) 106 | self.tableWidget.setRowCount(0) 107 | self.comboBox_x.clear() 108 | self.comboBox_y.clear() 109 | self.comboBox_wb.clear() 110 | self.comboBox_ws.clear() 111 | self.comboBox_r1.clear() 112 | self.comboBox_r2.clear() 113 | def clearcontext_all(self): 114 | self.tableWidget.clear() 115 | self.tableWidget.setColumnCount(0) 116 | self.tableWidget.setRowCount(0) 117 | self.comboBox_x.clear() 118 | self.comboBox_y.clear() 119 | self.comboBox_wb.clear() 120 | self.comboBox_ws.clear() 121 | self.comboBox_r1.clear() 122 | self.comboBox_r2.clear() 123 | def clearcontext_show(self): 124 | self.tableWidget.clear() 125 | self.tableWidget.setColumnCount(0) 126 | self.tableWidget.setRowCount(0) 127 | def clear_idx(self): 128 | self.comboBox_x.clear() 129 | self.comboBox_y.clear() 130 | self.comboBox_r1.clear() 131 | self.comboBox_r2.clear() 132 | def assign_dict(self,dict1,dict2): 133 | for k,v in dict1.items(): 134 | if isinstance(v,dict): 135 | dict_tmp = dict() 136 | dict2[k] = self.assign_dict(v,dict_tmp) 137 | else: 138 | dict2[k] = v 139 | return dict2 140 | 141 | def confirm_idx(self): 142 | self.infos_bak = self.assign_dict(self.infos,self.infos_bak) 143 | 144 | x = self.comboBox_x.itemText(self.comboBox_x.currentIndex()) 145 | y = self.comboBox_y.itemText(self.comboBox_y.currentIndex()) 146 | 147 | r1 = self.comboBox_r1.itemText(self.comboBox_r1.currentIndex()) 148 | r2 = self.comboBox_r2.itemText(self.comboBox_r2.currentIndex()) 149 | 150 | wb = self.comboBox_wb.itemText(self.comboBox_wb.currentIndex()) 151 | ws = self.comboBox_ws.itemText(self.comboBox_ws.currentIndex()) 152 | 153 | if wb == '' or ws == '': 154 | QMessageBox.about(self, "hi,兰神", '先load文件') 155 | else: 156 | x = int(x) if x != '' else x 157 | y = int(y) if y != '' else y 158 | r1 = int(r1) if r1 != '' else r1 159 | r2 = int(r2) if r2 != '' else r2 160 | 161 | if self.checkBox_book.isChecked(): 162 | print('book') 163 | key_idx = [x,y] 164 | rg = [r1,'last'] 165 | for wb_k in self.infos_bak.keys(): 166 | ws_keys = self.infos_bak[wb_k]['sheet_names'] 167 | for ws_k in ws_keys.keys(): 168 | self.infos_bak[wb_k]['sheet_names'][ws_k] = [key_idx,rg] 169 | elif self.checkBox_sheet.isChecked(): 170 | print('sheet') 171 | key_idx = [x,y] 172 | rg = [r1,'last'] 173 | ws_keys = self.infos_bak[wb]['sheet_names'] 174 | for ws_k in ws_keys.keys(): 175 | self.infos_bak[wb]['sheet_names'][ws_k] = [key_idx,rg] 176 | else: 177 | print('cell') 178 | key_idx = [x,y] 179 | rg = [r1,r2] 180 | self.infos_bak[wb]['sheet_names'][ws] = [key_idx,rg] 181 | self.flag_confirm = True 182 | 183 | 184 | def selectall(self): 185 | self.listWidget.selectAll() 186 | items = self.listWidget.selectedItems() 187 | if len(items) == 0: 188 | QMessageBox.about(self, "hi,兰神", '请先加载文件') 189 | 190 | def LoadProcess(self): 191 | self.clearcontext_all() 192 | if self.comboBoxfiletype.currentIndex() == 1: # xls 193 | QMessageBox.about(self, "hi,兰神", '不支持 xls 格式文件') 194 | elif self.comboBoxfiletype.currentIndex() == 0: # xlsx 195 | items = self.listWidget.selectedItems() 196 | if len(items) == 0: 197 | QMessageBox.about(self, "hi,兰神", '请先选择文件') 198 | else: 199 | self.infos = {} 200 | for i in list(items): 201 | file_path = str(i.text()) 202 | wb = load_workbook(filename=file_path) 203 | name = os.path.split(file_path)[-1] 204 | 205 | sheet_names = wb.sheetnames 206 | 207 | sheets_dict = {} 208 | for s in sheet_names: 209 | sheets_dict[s] = [] 210 | self.infos[name] = {'path':file_path,'sheet_names':sheets_dict} 211 | wb.close() 212 | for k in self.infos.keys(): 213 | self.comboBox_wb.addItem(k) 214 | k = self.comboBox_wb.itemText(0) 215 | sheets = list(self.infos[k]['sheet_names'].keys()) 216 | for s in sheets: 217 | self.comboBox_ws.addItem(s) 218 | self.activate_file[0] = self.infos[k]['path'] 219 | self.activate_file[1] = list(self.infos[k]['sheet_names'].keys())[0] 220 | 221 | self.show_excel() 222 | print('可以预览文件') 223 | def wbActivated(self,index): 224 | self.clearcontext_show() 225 | wb_k = self.comboBox_wb.itemText(index) 226 | sheets = list(self.infos[wb_k]['sheet_names'].keys()) 227 | self.comboBox_ws.clear() 228 | for s in sheets: 229 | self.comboBox_ws.addItem(s) 230 | self.activate_file[0] = self.infos[wb_k]['path'] 231 | self.activate_file[1] = list(self.infos[wb_k]['sheet_names'].keys())[0] 232 | self.show_excel() 233 | 234 | def wsActivated(self,index): 235 | ws_k = self.comboBox_ws.itemText(index) 236 | self.activate_file[1] = ws_k 237 | self.show_excel() 238 | 239 | def handleItemClick(self,item): 240 | cont = item.text() 241 | self.comboBox_x.clear() 242 | self.comboBox_y.clear() 243 | self.comboBox_r1.clear() 244 | row = item.row()+1 245 | column = item.column()+1 246 | #=======对合并的单元格取idx 247 | for p in self.merge_position: 248 | if row == p[0] and column == p[1]: 249 | row = row + (p[2]-p[0]) 250 | break 251 | #=======对合并的单元格取idx 252 | self.comboBox_x.addItem(str(row)) 253 | self.comboBox_y.addItem(str(column)) 254 | self.comboBox_r1.addItem(str(row+1)) 255 | 256 | 257 | def show_excel(self): 258 | self.merge_position = [] 259 | path = self.activate_file[0] 260 | sheetname = self.activate_file[1] 261 | wb = load_workbook(filename=path) 262 | ws = wb[sheetname] 263 | num_row = ws.max_row 264 | num_column = ws.max_column 265 | self.tableWidget.setColumnCount(num_column) 266 | self.tableWidget.setRowCount(num_row) 267 | 268 | #======合并单元格======= 269 | merge_idx = ws.merged_cells 270 | merge_idx = get_merge_cell_list(merge_idx) 271 | 272 | for i in range(len(merge_idx)): 273 | m_idx = merge_idx[i] 274 | self.tableWidget.setSpan(m_idx[0]-1, m_idx[1]-1, m_idx[2]-m_idx[0]+1, m_idx[3]-m_idx[1]+1) 275 | self.merge_position.append([m_idx[0],m_idx[1],m_idx[2]])#[x1,y1,range] 276 | #======合并单元格======= 277 | 278 | #======单元格大小======= 279 | for i in range(1,num_row+1): 280 | h = ws.row_dimensions[i].height 281 | if h is not None: 282 | self.tableWidget.setRowHeight(i-1,h) 283 | # for i in range(1,num_column+1): 284 | # w = ws.column_dimensions[get_column_letter(i)].width 285 | # if w is not None: 286 | # self.tableWidget.setColumnWidth(i-1,w) 287 | #======单元格大小======= 288 | 289 | self.comboBox_r2.clear() 290 | for i in range(1,num_row+1): 291 | self.comboBox_r2.addItem(str(num_row-i+1)) 292 | row_sizes = [] 293 | for j in range(1,num_column+1): 294 | cell = ws.cell(row=i, column=j) 295 | if cell.value is not None: 296 | item = QTableWidgetItem(str(cell.value)) 297 | assign_style_qt(item,cell) 298 | else: 299 | item = QTableWidgetItem() 300 | self.tableWidget.setItem(i-1, j-1, item) 301 | 302 | # self.tableWidget.resizeColumnsToContents() 303 | # self.tableWidget.resizeRowsToContents() 304 | wb.close() 305 | 306 | def SplitProcess(self): 307 | if not self.flag_confirm: 308 | QMessageBox.about(self, "hi,兰神", '请先选择文件并load文件,并选择拆分关键词') 309 | else: 310 | try: 311 | self.infos = self.assign_dict(self.infos_bak,self.infos) 312 | self.splitThread = splitThread(self.infos) 313 | self.splitThread.split_signal.connect(self.set_progressbar_value) 314 | self.splitThread.split_signal_lcd.connect(self.set_lcdnumber_value) 315 | self.splitThread.start() 316 | except: 317 | QMessageBox.about(self, "hi,兰神", '拆分{}出现错误'.format(base_name)) 318 | self.flag_confirm = False 319 | def mergeProcess(self): 320 | QMessageBox.about(self, "hi,兰神", '此功能为付费功能') 321 | def analyseProcess(self): 322 | QMessageBox.about(self, "hi,兰神", '此功能为付费功能') 323 | def makaloProcess(self): 324 | webbrowser.open('https://blog.csdn.net/guicai1647855685?spm=1010.2135.3001.5421') 325 | def linkProcess(self): 326 | webbrowser.open('https://github.com/makalo') 327 | def set_progressbar_value(self, value): 328 | self.progressBar.setValue(value) 329 | def set_lcdnumber_value(self,value): 330 | self.lcdNumber.display(value) 331 | 332 | 333 | 334 | app = QtWidgets.QApplication(sys.argv) 335 | window = anaxcelhandler() 336 | # setup stylesheet 337 | app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5()) 338 | window.show() 339 | sys.exit(app.exec_()) 340 | 341 | -------------------------------------------------------------------------------- /run_v5.0.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/makalo/pyqt5-excel/0e211057c628db13154017048553e778471de6a6/run_v5.0.exe -------------------------------------------------------------------------------- /show/show.mp4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/makalo/pyqt5-excel/0e211057c628db13154017048553e778471de6a6/show/show.mp4.gif -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | from openpyxl import Workbook, load_workbook,styles 2 | import os 3 | import copy 4 | import math 5 | from openpyxl.utils import get_column_letter, column_index_from_string 6 | from PyQt5.QtGui import QFont 7 | from PyQt5.QtCore import Qt 8 | def get_key(wb,valid_sheets,base_info): 9 | sheet_names = valid_sheets 10 | # print(sheet_names) 11 | dict_idx = {} 12 | keys = [] 13 | for name_s in sheet_names: 14 | ws = wb[name_s] 15 | idx,rg = base_info[name_s] 16 | _,y = idx 17 | num_row = ws.max_row 18 | num_column = ws.max_column 19 | 20 | merge_idx = ws.merged_cells 21 | merge_idx = get_merge_cell_list(merge_idx) 22 | 23 | for i in range(rg[0],num_row+1 if rg[1] == 'last' else rg[1]+1): 24 | k = ws.cell(row=i, column=y).value 25 | if k is None: 26 | for m_idx in merge_idx: 27 | if y == m_idx[1] and i >= m_idx[0] and i<= m_idx[2]: 28 | k = keys[-1] 29 | break 30 | else: 31 | continue 32 | if k is None: 33 | continue 34 | keys.append(k) 35 | if not k in dict_idx.keys(): 36 | dict_idx[k] = {name_s:[i]} 37 | elif not name_s in dict_idx[k].keys(): 38 | dict_idx[k][name_s] = [i] 39 | else: 40 | l = dict_idx[k][name_s] 41 | l.append(i) 42 | dict_idx[k][name_s] = l 43 | return dict_idx 44 | 45 | def get_merge_cell_list(merge_idx): 46 | merge_idx = list(merge_idx) 47 | merge_list = [] 48 | for i in range(len(merge_idx)): 49 | merge = merge_idx[i] 50 | row_min, row_max, col_min, col_max = merge.min_row, merge.max_row, merge.min_col, merge.max_col 51 | # merge_list.append([row_min, row_max, col_min, col_max]) 52 | merge_list.append([row_min, col_min, row_max, col_max]) 53 | return merge_list 54 | def get_merge_map(merge_idx,idx): 55 | row_min, col_min,row_max , col_max = merge_idx 56 | col_min = get_column_letter(col_min) 57 | col_max = get_column_letter(col_max) 58 | try: 59 | x1 = idx.index(row_min) + 1 60 | x2 = idx.index(row_max) + 1 61 | s = '{}{}:{}{}'.format(col_min,x1,col_max,x2) 62 | return s 63 | except: 64 | return None 65 | 66 | def idx2letter(idx): 67 | x,y = idx 68 | y = get_column_letter(y) 69 | s = '{}{}'.format(y,x) 70 | return s 71 | 72 | 73 | def set_style(ws): 74 | align = styles.Alignment(horizontal='center',vertical='center') 75 | font_18 = styles.Font(size=18) 76 | 77 | num_row = ws.max_row #最大行数 78 | num_column = ws.max_column #最大列数 79 | for i in range(1,num_row+1): 80 | for j in range(1,num_column+1): 81 | cell = ws.cell(row=i, column=j) 82 | cell.alignment = align 83 | cell.font = font_18 84 | def assign_style(target_cell,source_cell): 85 | target_cell.fill = copy.copy(source_cell.fill) 86 | if source_cell.has_style: 87 | target_cell._style = copy.copy(source_cell._style) 88 | target_cell.font = copy.copy(source_cell.font) 89 | target_cell.border = copy.copy(source_cell.border) 90 | target_cell.fill = copy.copy(source_cell.fill) 91 | target_cell.number_format = copy.copy(source_cell.number_format) 92 | target_cell.protection = copy.copy(source_cell.protection) 93 | target_cell.alignment = copy.copy(source_cell.alignment) 94 | 95 | def color(value): 96 | digit = list(map(str, range(10))) + list("ABCDEF") 97 | if isinstance(value, tuple): 98 | string = '#' 99 | for i in value: 100 | a1 = i // 16 101 | a2 = i % 16 102 | string += digit[a1] + digit[a2] 103 | return string 104 | elif isinstance(value, str): 105 | a1 = digit.index(value[1]) * 16 + digit.index(value[2]) 106 | a2 = digit.index(value[3]) * 16 + digit.index(value[4]) 107 | a3 = digit.index(value[5]) * 16 + digit.index(value[6]) 108 | return (a1, a2, a3) 109 | else: 110 | return (0, 0, 0) 111 | def assign_style_qt(target_cell,source_cell): 112 | #字体,大小,颜色,加粗 113 | font = QFont() #实例化字体对象 114 | font.setFamily(source_cell.font.name) #字体 115 | font.setBold(source_cell.font.bold) #加粗 116 | font.setPointSize(source_cell.font.size) #字体大小 117 | target_cell.setFont(font) 118 | #居中 119 | target_cell.setTextAlignment(Qt.AlignCenter | Qt.AlignCenter) 120 | #背景 121 | # print(source_cell.fill.bgColor.rgb) 122 | # target_cell.setBackground(QtGui.QColor(100,100,150)) 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | --------------------------------------------------------------------------------