pyxllib 0.0.43__py3-none-any.whl → 0.3.197__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyxllib/__init__.py +9 -2
- pyxllib/algo/__init__.py +8 -0
- pyxllib/algo/disjoint.py +54 -0
- pyxllib/algo/geo.py +541 -0
- pyxllib/{util/mathlib.py → algo/intervals.py} +172 -36
- pyxllib/algo/matcher.py +389 -0
- pyxllib/algo/newbie.py +166 -0
- pyxllib/algo/pupil.py +629 -0
- pyxllib/algo/shapelylib.py +67 -0
- pyxllib/algo/specialist.py +241 -0
- pyxllib/algo/stat.py +494 -0
- pyxllib/algo/treelib.py +149 -0
- pyxllib/algo/unitlib.py +66 -0
- pyxllib/autogui/__init__.py +5 -0
- pyxllib/autogui/activewin.py +246 -0
- pyxllib/autogui/all.py +9 -0
- pyxllib/autogui/autogui.py +852 -0
- pyxllib/autogui/uiautolib.py +362 -0
- pyxllib/autogui/virtualkey.py +102 -0
- pyxllib/autogui/wechat.py +827 -0
- pyxllib/autogui/wechat_msg.py +421 -0
- pyxllib/autogui/wxautolib.py +84 -0
- pyxllib/cv/__init__.py +1 -11
- pyxllib/cv/expert.py +267 -0
- pyxllib/cv/{imlib.py → imfile.py} +18 -83
- pyxllib/cv/imhash.py +39 -0
- pyxllib/cv/pupil.py +9 -0
- pyxllib/cv/rgbfmt.py +1525 -0
- pyxllib/cv/slidercaptcha.py +137 -0
- pyxllib/cv/trackbartools.py +163 -49
- pyxllib/cv/xlcvlib.py +1040 -0
- pyxllib/cv/xlpillib.py +423 -0
- pyxllib/data/__init__.py +0 -0
- pyxllib/data/echarts.py +240 -0
- pyxllib/data/jsonlib.py +89 -0
- pyxllib/{util/oss2_.py → data/oss.py} +11 -9
- pyxllib/data/pglib.py +1127 -0
- pyxllib/data/sqlite.py +568 -0
- pyxllib/{util → data}/sqllib.py +13 -31
- pyxllib/ext/JLineViewer.py +505 -0
- pyxllib/ext/__init__.py +6 -0
- pyxllib/{util → ext}/demolib.py +119 -35
- pyxllib/ext/drissionlib.py +277 -0
- pyxllib/ext/kq5034lib.py +12 -0
- pyxllib/{util/main.py → ext/old.py} +122 -284
- pyxllib/ext/qt.py +449 -0
- pyxllib/ext/robustprocfile.py +497 -0
- pyxllib/ext/seleniumlib.py +76 -0
- pyxllib/{util/tklib.py → ext/tk.py} +10 -11
- pyxllib/ext/unixlib.py +827 -0
- pyxllib/ext/utools.py +351 -0
- pyxllib/{util/webhooklib.py → ext/webhook.py} +45 -17
- pyxllib/ext/win32lib.py +40 -0
- pyxllib/ext/wjxlib.py +88 -0
- pyxllib/ext/wpsapi.py +124 -0
- pyxllib/ext/xlwork.py +9 -0
- pyxllib/ext/yuquelib.py +1105 -0
- pyxllib/file/__init__.py +17 -0
- pyxllib/file/docxlib.py +761 -0
- pyxllib/{util → file}/gitlib.py +40 -27
- pyxllib/file/libreoffice.py +165 -0
- pyxllib/file/movielib.py +148 -0
- pyxllib/file/newbie.py +10 -0
- pyxllib/file/onenotelib.py +1469 -0
- pyxllib/file/packlib/__init__.py +330 -0
- pyxllib/{util → file/packlib}/zipfile.py +598 -195
- pyxllib/file/pdflib.py +426 -0
- pyxllib/file/pupil.py +185 -0
- pyxllib/file/specialist/__init__.py +685 -0
- pyxllib/{basic/_5_dirlib.py → file/specialist/dirlib.py} +364 -93
- pyxllib/file/specialist/download.py +193 -0
- pyxllib/file/specialist/filelib.py +2829 -0
- pyxllib/file/xlsxlib.py +3131 -0
- pyxllib/file/xlsyncfile.py +341 -0
- pyxllib/prog/__init__.py +5 -0
- pyxllib/prog/cachetools.py +64 -0
- pyxllib/prog/deprecatedlib.py +233 -0
- pyxllib/prog/filelock.py +42 -0
- pyxllib/prog/ipyexec.py +253 -0
- pyxllib/prog/multiprogs.py +940 -0
- pyxllib/prog/newbie.py +451 -0
- pyxllib/prog/pupil.py +1197 -0
- pyxllib/{sitepackages.py → prog/sitepackages.py} +5 -3
- pyxllib/prog/specialist/__init__.py +391 -0
- pyxllib/prog/specialist/bc.py +203 -0
- pyxllib/prog/specialist/browser.py +497 -0
- pyxllib/prog/specialist/common.py +347 -0
- pyxllib/prog/specialist/datetime.py +199 -0
- pyxllib/prog/specialist/tictoc.py +240 -0
- pyxllib/prog/specialist/xllog.py +180 -0
- pyxllib/prog/xlosenv.py +108 -0
- pyxllib/stdlib/__init__.py +17 -0
- pyxllib/{util → stdlib}/tablepyxl/__init__.py +1 -3
- pyxllib/{util → stdlib}/tablepyxl/style.py +1 -1
- pyxllib/{util → stdlib}/tablepyxl/tablepyxl.py +2 -4
- pyxllib/text/__init__.py +8 -0
- pyxllib/text/ahocorasick.py +39 -0
- pyxllib/text/airscript.js +744 -0
- pyxllib/text/charclasslib.py +121 -0
- pyxllib/text/jiebalib.py +267 -0
- pyxllib/text/jinjalib.py +32 -0
- pyxllib/text/jsa_ai_prompt.md +271 -0
- pyxllib/text/jscode.py +922 -0
- pyxllib/text/latex/__init__.py +158 -0
- pyxllib/text/levenshtein.py +303 -0
- pyxllib/text/nestenv.py +1215 -0
- pyxllib/text/newbie.py +300 -0
- pyxllib/text/pupil/__init__.py +8 -0
- pyxllib/text/pupil/common.py +1121 -0
- pyxllib/text/pupil/xlalign.py +326 -0
- pyxllib/text/pycode.py +47 -0
- pyxllib/text/specialist/__init__.py +8 -0
- pyxllib/text/specialist/common.py +112 -0
- pyxllib/text/specialist/ptag.py +186 -0
- pyxllib/text/spellchecker.py +172 -0
- pyxllib/text/templates/echart_base.html +11 -0
- pyxllib/text/templates/highlight_code.html +17 -0
- pyxllib/text/templates/latex_editor.html +103 -0
- pyxllib/text/vbacode.py +17 -0
- pyxllib/text/xmllib.py +747 -0
- pyxllib/xl.py +39 -0
- pyxllib/xlcv.py +17 -0
- pyxllib-0.3.197.dist-info/METADATA +48 -0
- pyxllib-0.3.197.dist-info/RECORD +126 -0
- {pyxllib-0.0.43.dist-info → pyxllib-0.3.197.dist-info}/WHEEL +4 -5
- pyxllib/basic/_1_strlib.py +0 -945
- pyxllib/basic/_2_timelib.py +0 -488
- pyxllib/basic/_3_pathlib.py +0 -916
- pyxllib/basic/_4_loglib.py +0 -419
- pyxllib/basic/__init__.py +0 -54
- pyxllib/basic/arrow_.py +0 -250
- pyxllib/basic/chardet_.py +0 -66
- pyxllib/basic/dirlib.py +0 -529
- pyxllib/basic/dprint.py +0 -202
- pyxllib/basic/extension.py +0 -12
- pyxllib/basic/judge.py +0 -31
- pyxllib/basic/log.py +0 -204
- pyxllib/basic/pathlib_.py +0 -705
- pyxllib/basic/pytictoc.py +0 -102
- pyxllib/basic/qiniu_.py +0 -61
- pyxllib/basic/strlib.py +0 -761
- pyxllib/basic/timer.py +0 -132
- pyxllib/cv/cv.py +0 -834
- pyxllib/cv/cvlib/_1_geo.py +0 -543
- pyxllib/cv/cvlib/_2_cvprcs.py +0 -309
- pyxllib/cv/cvlib/_2_imgproc.py +0 -594
- pyxllib/cv/cvlib/_3_pilprcs.py +0 -80
- pyxllib/cv/cvlib/_4_cvimg.py +0 -211
- pyxllib/cv/cvlib/__init__.py +0 -10
- pyxllib/cv/debugtools.py +0 -82
- pyxllib/cv/fitz_.py +0 -300
- pyxllib/cv/installer.py +0 -42
- pyxllib/debug/_0_installer.py +0 -38
- pyxllib/debug/_1_typelib.py +0 -277
- pyxllib/debug/_2_chrome.py +0 -198
- pyxllib/debug/_3_showdir.py +0 -161
- pyxllib/debug/_4_bcompare.py +0 -140
- pyxllib/debug/__init__.py +0 -49
- pyxllib/debug/bcompare.py +0 -132
- pyxllib/debug/chrome.py +0 -198
- pyxllib/debug/installer.py +0 -38
- pyxllib/debug/showdir.py +0 -158
- pyxllib/debug/typelib.py +0 -278
- pyxllib/image/__init__.py +0 -12
- pyxllib/torch/__init__.py +0 -20
- pyxllib/torch/modellib.py +0 -37
- pyxllib/torch/trainlib.py +0 -344
- pyxllib/util/__init__.py +0 -20
- pyxllib/util/aip_.py +0 -141
- pyxllib/util/casiadb.py +0 -59
- pyxllib/util/excellib.py +0 -495
- pyxllib/util/filelib.py +0 -612
- pyxllib/util/jsondata.py +0 -27
- pyxllib/util/jsondata2.py +0 -92
- pyxllib/util/labelmelib.py +0 -139
- pyxllib/util/onepy/__init__.py +0 -29
- pyxllib/util/onepy/onepy.py +0 -574
- pyxllib/util/onepy/onmanager.py +0 -170
- pyxllib/util/pyautogui_.py +0 -219
- pyxllib/util/textlib.py +0 -1305
- pyxllib/util/unorder.py +0 -22
- pyxllib/util/xmllib.py +0 -639
- pyxllib-0.0.43.dist-info/METADATA +0 -39
- pyxllib-0.0.43.dist-info/RECORD +0 -80
- pyxllib-0.0.43.dist-info/top_level.txt +0 -1
- {pyxllib-0.0.43.dist-info → pyxllib-0.3.197.dist-info/licenses}/LICENSE +0 -0
pyxllib/util/excellib.py
DELETED
@@ -1,495 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
# -*- coding: utf-8 -*-
|
3
|
-
# @Author : 陈坤泽
|
4
|
-
# @Email : 877362867@qq.com
|
5
|
-
# @Data : 2020/06/02
|
6
|
-
|
7
|
-
|
8
|
-
"""
|
9
|
-
扩展了些自己的openpyxl工具
|
10
|
-
"""
|
11
|
-
|
12
|
-
import subprocess
|
13
|
-
|
14
|
-
try:
|
15
|
-
import openpyxl
|
16
|
-
except ModuleNotFoundError:
|
17
|
-
subprocess.run(['pip', 'install', 'openpyxl'])
|
18
|
-
import openpyxl
|
19
|
-
|
20
|
-
try:
|
21
|
-
import premailer
|
22
|
-
except ModuleNotFoundError:
|
23
|
-
subprocess.run(['pip', 'install', 'premailer'])
|
24
|
-
import premailer
|
25
|
-
|
26
|
-
try:
|
27
|
-
import xlrd2
|
28
|
-
except ModuleNotFoundError:
|
29
|
-
subprocess.run(['pip', 'install', 'xlrd2'])
|
30
|
-
import xlrd2
|
31
|
-
|
32
|
-
from openpyxl.styles import Font
|
33
|
-
|
34
|
-
from pyxllib.debug import *
|
35
|
-
|
36
|
-
|
37
|
-
class Openpyxl:
|
38
|
-
"""
|
39
|
-
对openpyxl库做一些基本的功能拓展
|
40
|
-
"""
|
41
|
-
|
42
|
-
@staticmethod
|
43
|
-
def address(n, m) -> str:
|
44
|
-
r"""数字索引转excel地址索引
|
45
|
-
:param n: 行号,可以输入字符串形式的数字
|
46
|
-
:param m: 列号,同上可以输入str的数字
|
47
|
-
:return:
|
48
|
-
|
49
|
-
>>> Openpyxl.address(2, 3)
|
50
|
-
'C2'
|
51
|
-
"""
|
52
|
-
from openpyxl.utils.cell import get_column_letter
|
53
|
-
return f'{get_column_letter(int(m))}{n}'
|
54
|
-
|
55
|
-
@staticmethod
|
56
|
-
def in_range(cell):
|
57
|
-
"""判断一个单元格所在的合并单元格
|
58
|
-
>> in_range(ws['C1'])
|
59
|
-
<openpyxl.worksheet.cell_range.CellRange> A1:D3
|
60
|
-
"""
|
61
|
-
ws = cell.parent
|
62
|
-
for rng in ws.merged_cells.ranges:
|
63
|
-
if cell.coordinate in rng:
|
64
|
-
break
|
65
|
-
else: # 如果找不到则返回原值
|
66
|
-
rng = cell
|
67
|
-
return rng
|
68
|
-
|
69
|
-
@staticmethod
|
70
|
-
def mcell(cell):
|
71
|
-
"""返回“有效单元格”,即如果输入的是一个合并单元格,会返回该合并单元格左上角的单元格
|
72
|
-
修改左上角单元格的值才是可行、有意义的
|
73
|
-
|
74
|
-
因为跟合并单元格有关,所以 以m前缀 merge
|
75
|
-
"""
|
76
|
-
from openpyxl.cell.cell import MergedCell
|
77
|
-
if isinstance(cell, MergedCell):
|
78
|
-
ws = cell.parent
|
79
|
-
xy = Openpyxl.in_range(cell).top[0]
|
80
|
-
return ws[Openpyxl.address(*xy)]
|
81
|
-
else:
|
82
|
-
return cell
|
83
|
-
|
84
|
-
@staticmethod
|
85
|
-
def celltype(cell):
|
86
|
-
"""
|
87
|
-
:param cell: 一个单元格
|
88
|
-
:return: 单元格类型
|
89
|
-
0:普通单元格
|
90
|
-
1:合并单元格其他衍生位置
|
91
|
-
2:合并单元格的左上角的位置
|
92
|
-
|
93
|
-
TODO 这个函数还是可以看看能不能有更好的实现、提速
|
94
|
-
"""
|
95
|
-
from openpyxl.cell.cell import MergedCell
|
96
|
-
if isinstance(cell, MergedCell):
|
97
|
-
return 1
|
98
|
-
elif isinstance(cell.offset(1, 0), MergedCell) or isinstance(cell.offset(0, 1), MergedCell):
|
99
|
-
# 这里只能判断可能是合并单元格,具体是不是合并单元格,还要
|
100
|
-
rng = Openpyxl.in_range(cell)
|
101
|
-
return 2 if hasattr(rng, 'size') else 0
|
102
|
-
else:
|
103
|
-
return 0
|
104
|
-
|
105
|
-
@staticmethod
|
106
|
-
def isnone(cell):
|
107
|
-
"""是普通单元格且值为None
|
108
|
-
注意合并单元格的衍生单元格不为None
|
109
|
-
"""
|
110
|
-
celltype = Openpyxl.celltype(cell)
|
111
|
-
return celltype == 0 and cell.value is None
|
112
|
-
|
113
|
-
@staticmethod
|
114
|
-
def copy_cell_format(cell, new_cell):
|
115
|
-
""" 单元格全格式复制,需要事先指定好新旧单元格的物理位置
|
116
|
-
参考:https://stackoverflow.com/questions/23332259/copy-cell-style-openpyxl
|
117
|
-
"""
|
118
|
-
from copy import copy
|
119
|
-
if cell.has_style:
|
120
|
-
new_cell.font = copy(cell.font) # 字体
|
121
|
-
new_cell.border = copy(cell.border) # 表格线
|
122
|
-
new_cell.fill = copy(cell.fill) # 填充色
|
123
|
-
new_cell.number_format = copy(cell.number_format) # 数字格式
|
124
|
-
new_cell.protection = copy(cell.protection) # 保护?
|
125
|
-
new_cell.alignment = copy(cell.alignment) # 对齐格式
|
126
|
-
# new_cell.style = cell.style
|
127
|
-
# if cell.comment:
|
128
|
-
# 这个会引发AttributeError。。。
|
129
|
-
# vml = fromstring(self.workbook.vba_archive.read(ws.legacy_drawing))
|
130
|
-
# AttributeError: 'NoneType' object has no attribute 'read'
|
131
|
-
# new_cell.comment = copy(cell.comment)
|
132
|
-
# 就算开了keep_vba可以强制写入了,打开的时候文件可能还是会错
|
133
|
-
|
134
|
-
@staticmethod
|
135
|
-
def copy_cell(cell, new_cell):
|
136
|
-
""" 单元格全格式、包括值的整体复制
|
137
|
-
"""
|
138
|
-
new_cell.value = cell.value
|
139
|
-
Openpyxl.copy_cell_format(cell, new_cell)
|
140
|
-
|
141
|
-
@classmethod
|
142
|
-
def down(cls, cell):
|
143
|
-
"""输入一个单元格,向下移动一格
|
144
|
-
注意其跟offset的区别,如果cell是合并单元格,会跳过自身的衍生单元格
|
145
|
-
"""
|
146
|
-
if cls.celltype(cell): # 合并单元格
|
147
|
-
rng = cls.in_range(cell)
|
148
|
-
return cell.parent.cell(rng.max_row + 1, cell.column)
|
149
|
-
else:
|
150
|
-
return cell.offset(1, 0)
|
151
|
-
|
152
|
-
@classmethod
|
153
|
-
def right(cls, cell):
|
154
|
-
if cls.celltype(cell):
|
155
|
-
rng = cls.in_range(cell)
|
156
|
-
return cell.parent.cell(cell.row, rng.max_row + 1)
|
157
|
-
else:
|
158
|
-
return cell.offset(0, 1)
|
159
|
-
|
160
|
-
@classmethod
|
161
|
-
def up(cls, cell):
|
162
|
-
if cls.celltype(cell):
|
163
|
-
rng = cls.in_range(cell)
|
164
|
-
return cell.parent.cell(rng.min_row - 1, cell.column)
|
165
|
-
else:
|
166
|
-
return cell.offset(-1, 0)
|
167
|
-
|
168
|
-
@classmethod
|
169
|
-
def left(cls, cell):
|
170
|
-
if cls.celltype(cell):
|
171
|
-
rng = cls.in_range(cell)
|
172
|
-
return cell.parent.cell(cell.row, rng.min_row - 1)
|
173
|
-
else:
|
174
|
-
return cell.offset(0, -1)
|
175
|
-
|
176
|
-
@staticmethod
|
177
|
-
def copy_worksheet(origin_ws, target_ws):
|
178
|
-
"""跨工作薄时复制表格内容的功能
|
179
|
-
openpyxl自带的Workbook.copy_worksheet没法跨工作薄复制,很坑
|
180
|
-
"""
|
181
|
-
# 1 取每个单元格的值
|
182
|
-
for row in origin_ws:
|
183
|
-
for cell in row:
|
184
|
-
try:
|
185
|
-
Openpyxl.copy_cell(cell, target_ws[cell.coordinate])
|
186
|
-
except AttributeError:
|
187
|
-
pass
|
188
|
-
# 2 合并单元格的处理
|
189
|
-
for rng in origin_ws.merged_cells.ranges:
|
190
|
-
target_ws.merge_cells(rng.ref)
|
191
|
-
# 3 其他表格属性的复制
|
192
|
-
# 这个从excel读取过来的时候,是不准的,例如D3可能因为关闭时停留窗口的原因误跑到D103
|
193
|
-
# dprint(origin_ws.freeze_panes)
|
194
|
-
# target_ws.freeze_panes = origin_ws.freeze_panes
|
195
|
-
|
196
|
-
|
197
|
-
def product(*iterables, order=None, repeat=1):
|
198
|
-
""" 对 itertools 的product扩展orders参数的更高级的product迭代器
|
199
|
-
:param order: 假设iterables有n=3个迭代器,则默认 orders=[1, 2, 3] (起始编号1)
|
200
|
-
即标准的product,是按顺序对每个迭代器进行重置、遍历的
|
201
|
-
但是我扩展的这个接口,允许调整每个维度的更新顺序
|
202
|
-
例如设置为 [-2, 1, 3],表示先对第2维降序,然后按第1、3维的方式排序获得各个坐标点
|
203
|
-
注:可以只输入[-2],默认会自动补充维[1, 3]
|
204
|
-
|
205
|
-
for x in product('ab', 'cd', 'ef', order=[3, -2, 1]):
|
206
|
-
print(x)
|
207
|
-
|
208
|
-
['a', 'd', 'e']
|
209
|
-
['b', 'd', 'e']
|
210
|
-
['a', 'c', 'e']
|
211
|
-
['b', 'c', 'e']
|
212
|
-
['a', 'd', 'f']
|
213
|
-
['b', 'd', 'f']
|
214
|
-
['a', 'c', 'f']
|
215
|
-
['b', 'c', 'f']
|
216
|
-
|
217
|
-
TODO 我在想numpy这么牛逼,会不会有等价的功能接口可以实现,我不用重复造轮子?
|
218
|
-
"""
|
219
|
-
import itertools, numpy
|
220
|
-
|
221
|
-
# 一、标准调用方式
|
222
|
-
if order is None:
|
223
|
-
for x in itertools.product(*iterables, repeat=repeat):
|
224
|
-
yield x
|
225
|
-
return
|
226
|
-
|
227
|
-
# 二、输入orders参数的调用方式
|
228
|
-
# 1 补全orders参数长度
|
229
|
-
n = len(iterables)
|
230
|
-
for i in range(1, n + 1):
|
231
|
-
if not (i in order or -i in order):
|
232
|
-
order.append(i)
|
233
|
-
if len(order) != n: return ValueError(f'orders参数值有问题 {order}')
|
234
|
-
|
235
|
-
# 2 生成新的迭代器组
|
236
|
-
new_iterables = [(iterables[i - 1] if i > 0 else reversed(iterables[-i - 1])) for i in order]
|
237
|
-
idx = numpy.argsort([abs(i) - 1 for i in order])
|
238
|
-
for y in itertools.product(*new_iterables, repeat=repeat):
|
239
|
-
yield [y[i] for i in idx]
|
240
|
-
|
241
|
-
|
242
|
-
class Worksheet(openpyxl.worksheet.worksheet.Worksheet):
|
243
|
-
""" 扩展标准的Workshhet功能
|
244
|
-
>> wb = openpyxl.load_workbook(filename='高中数学知识树匹配终稿.xlsx', data_only=True)
|
245
|
-
>> ws1 = Worksheet(wb['main'])
|
246
|
-
>> ws2 = Worksheet(wb['导出'])
|
247
|
-
"""
|
248
|
-
|
249
|
-
def __init__(self, ws):
|
250
|
-
self.__dict__ = ws.__dict__
|
251
|
-
|
252
|
-
def _cells_by_row(self, min_col, min_row, max_col, max_row, values_only=False):
|
253
|
-
"""openpyxl的这个迭代器,遇到合并单元格会有bug
|
254
|
-
所以我把它重新设计一下~~
|
255
|
-
"""
|
256
|
-
for row in range(min_row, max_row + 1):
|
257
|
-
cells = (self.cell(row=row, column=column) for column in range(min_col, max_col + 1))
|
258
|
-
if values_only:
|
259
|
-
# yield tuple(cell.value for cell in cells) # 原代码
|
260
|
-
yield tuple(getattr(cell, 'value', None) for cell in cells)
|
261
|
-
else:
|
262
|
-
yield tuple(cells)
|
263
|
-
|
264
|
-
def search(self, pattern, min_row=None, max_row=None, min_col=None, max_col=None, order=None, direction=0):
|
265
|
-
"""查找满足pattern正则表达式的单元格
|
266
|
-
|
267
|
-
:param pattern: 正则匹配式,可以输入re.complier对象
|
268
|
-
会将每个单元格的值转成str,然后进行字符串规则search匹配
|
269
|
-
支持多层嵌套 ['模块一', '属性1']
|
270
|
-
:param direction: 只有在 pattern 为数组的时候有用
|
271
|
-
pattern有多组时,会嵌套找单元格
|
272
|
-
每计算出一个条件后,默认取该单元格下方的子区间 axis=0
|
273
|
-
如果不是下方,而是右方,可以设置为1
|
274
|
-
还有奇葩的上方、左方,可以分别设置为2、3
|
275
|
-
:param order: 默认None,也就是 [1, 2] 的效果,规律详见product接口
|
276
|
-
|
277
|
-
>> wb = openpyxl.load_workbook(filename='2020寒假教材各地区数量统计最新2020.1.1.xlsx')
|
278
|
-
>> ws = Worksheet(wb['预算总表'])
|
279
|
-
>> ws.search('年段')
|
280
|
-
<Cell '预算总表'.B2>
|
281
|
-
"""
|
282
|
-
# 1 定界
|
283
|
-
x1, x2 = max(min_row or 1, 1), min(max_row or self.max_row, self.max_row)
|
284
|
-
y1, y2 = max(min_col or 1, 1), min(max_col or self.max_column, self.max_column)
|
285
|
-
|
286
|
-
# 2 遍历
|
287
|
-
if isinstance(pattern, (list, tuple)):
|
288
|
-
cel = None
|
289
|
-
for p in pattern:
|
290
|
-
cel = self.search(p, x1, x2, y1, y2, order)
|
291
|
-
if cel:
|
292
|
-
# up, down, left, right 找到的单元格四边界
|
293
|
-
l, u, r, d = getattr(Openpyxl.in_range(cel), 'bounds', (cel.column, cel.row, cel.column, cel.row))
|
294
|
-
if direction == 0:
|
295
|
-
x1, y1, y2 = max(x1, d), max(y1, l), min(y2, r)
|
296
|
-
elif direction == 1:
|
297
|
-
x1, x2, y1 = max(x1, u), min(x2, d), max(y1, r)
|
298
|
-
elif direction == 2:
|
299
|
-
x2, y1, y2 = min(x2, d), max(y1, l), min(y2, r)
|
300
|
-
elif direction == 3:
|
301
|
-
x1, x2, y2 = max(x1, u), min(x2, d), min(y2, l)
|
302
|
-
else:
|
303
|
-
raise ValueError(f'direction参数值错误{direction}')
|
304
|
-
else:
|
305
|
-
return None
|
306
|
-
return cel
|
307
|
-
else:
|
308
|
-
if isinstance(pattern, str): pattern = re.compile(pattern)
|
309
|
-
for x, y in product(range(x1, x2 + 1), range(y1, y2 + 1), order=order):
|
310
|
-
cell = self.cell(x, y)
|
311
|
-
if Openpyxl.celltype(cell) == 1: continue # 过滤掉合并单元格位置
|
312
|
-
if pattern.search(str(cell.value)): return cell # 返回满足条件的第一个值
|
313
|
-
|
314
|
-
findcel = search
|
315
|
-
|
316
|
-
def findrow(self, pattern, *args, **kwargs):
|
317
|
-
cel = self.findcel(pattern, *args, **kwargs)
|
318
|
-
return cel.row if cel else 0
|
319
|
-
|
320
|
-
def findcol(self, pattern, *args, **kwargs):
|
321
|
-
cel = self.findcel(pattern, *args, **kwargs)
|
322
|
-
return cel.column if cel else 0
|
323
|
-
|
324
|
-
def chrome(self):
|
325
|
-
"""注意,这里会去除掉合并单元格"""
|
326
|
-
chrome(pd.DataFrame(self.values))
|
327
|
-
|
328
|
-
def select_columns(self, columns, column_name='searchkey'):
|
329
|
-
r"""获取表中columns属性列的值,返回dataframe数据类型
|
330
|
-
|
331
|
-
:param columns: 搜索列名使用正则re.search字符串匹配查找
|
332
|
-
可以单列:'attr1',找到列头后,会一直往后取到最后一个非空值
|
333
|
-
也可以多列: ['attr1', 'attr2', 'attr3']
|
334
|
-
会结合多个列标题定位,数据从最大的起始行号开始取,
|
335
|
-
(TODO 截止到最末非空值所在行 未实现,先用openpyxl自带的max_row判断,不过这个有时会判断过大)
|
336
|
-
遇到合并单元格,会寻找其母单元格的值填充
|
337
|
-
:param column_name: 返回的df。列名
|
338
|
-
origin,原始的列名
|
339
|
-
searchkey,搜索时用的查找名
|
340
|
-
"""
|
341
|
-
if not isinstance(columns, (list, tuple)):
|
342
|
-
columns = [columns]
|
343
|
-
|
344
|
-
# 1 找到所有标题位置,定位起始行
|
345
|
-
cels, names, start_line = [], [], -1
|
346
|
-
for search_name in columns:
|
347
|
-
cel = self.findcel(search_name)
|
348
|
-
if cel:
|
349
|
-
cels.append(cel)
|
350
|
-
if column_name == 'searchkey':
|
351
|
-
names.append(str(search_name))
|
352
|
-
elif column_name == 'origin':
|
353
|
-
if isinstance(search_name, (list, tuple)) and len(search_name) > 1:
|
354
|
-
names.append('/'.join(list(search_name[:-1]) + [str(cel.value)]))
|
355
|
-
else:
|
356
|
-
names.append(str(cel.value))
|
357
|
-
else:
|
358
|
-
raise ValueError(f'{column_name}')
|
359
|
-
start_line = max(start_line, Openpyxl.down(cel).row)
|
360
|
-
else:
|
361
|
-
dprint(search_name) # 找不到指定列
|
362
|
-
|
363
|
-
# 2 获得每列的数据
|
364
|
-
datas = {}
|
365
|
-
for k, cel in enumerate(cels):
|
366
|
-
if cel:
|
367
|
-
col = cel.column
|
368
|
-
li = []
|
369
|
-
for i in range(start_line, self.max_row + 1):
|
370
|
-
v = Openpyxl.mcell(self.cell(i, col)).value # 注意合并单元格的取值
|
371
|
-
li.append(v)
|
372
|
-
datas[names[k]] = li
|
373
|
-
else:
|
374
|
-
# 如果没找到列,设一个空列
|
375
|
-
datas[names[k]] = [None] * (self.max_row + 1 - start_line)
|
376
|
-
df = pd.DataFrame(datas)
|
377
|
-
|
378
|
-
# 3 去除所有空行数据
|
379
|
-
df.dropna(how='all', inplace=True)
|
380
|
-
|
381
|
-
return df
|
382
|
-
|
383
|
-
def copy_range(self, cell_range, rows=0, cols=0):
|
384
|
-
""" 同表格内的 range 复制操作
|
385
|
-
Copy a cell range by the number of rows and/or columns:
|
386
|
-
down if rows > 0 and up if rows < 0
|
387
|
-
right if cols > 0 and left if cols < 0
|
388
|
-
Existing cells will be overwritten.
|
389
|
-
Formulae and references will not be updated.
|
390
|
-
"""
|
391
|
-
from openpyxl.worksheet.cell_range import CellRange
|
392
|
-
from itertools import product
|
393
|
-
# 1 预处理
|
394
|
-
if isinstance(cell_range, str):
|
395
|
-
cell_range = CellRange(cell_range)
|
396
|
-
if not isinstance(cell_range, CellRange):
|
397
|
-
raise ValueError("Only CellRange objects can be copied")
|
398
|
-
if not rows and not cols:
|
399
|
-
return
|
400
|
-
min_col, min_row, max_col, max_row = cell_range.bounds
|
401
|
-
# 2 注意拷贝顺序跟移动方向是有关系的,要防止被误覆盖,复制了新的值,而非原始值
|
402
|
-
r = sorted(range(min_row, max_row + 1), reverse=rows > 0)
|
403
|
-
c = sorted(range(min_col, max_col + 1), reverse=cols > 0)
|
404
|
-
for row, column in product(r, c):
|
405
|
-
Openpyxl.copy_cell(self.cell(row, column), self.cell(row + rows, column + cols))
|
406
|
-
|
407
|
-
def reindex_columns(self, orders):
|
408
|
-
""" 重新排列表格的列顺序
|
409
|
-
>> ws.reindex_columns('I,J,A,,,G,B,C,D,F,E,H,,,K'.split(','))
|
410
|
-
|
411
|
-
TODO 支持含合并单元格的整体移动?
|
412
|
-
"""
|
413
|
-
from openpyxl.utils.cell import column_index_from_string
|
414
|
-
max_row, max_column = self.max_row, self.max_column
|
415
|
-
for j, col in enumerate(orders, 1):
|
416
|
-
if not col: continue
|
417
|
-
self.copy_range(f'{col}1:{col}{max_row}', cols=max_column + j - column_index_from_string(col))
|
418
|
-
self.delete_cols(1, max_column)
|
419
|
-
|
420
|
-
|
421
|
-
def adjust_sheets(wb, new_sheetnames):
|
422
|
-
""" 按照 new_sheetnames 的清单重新调整sheets
|
423
|
-
在清单里的按顺序罗列
|
424
|
-
不在清单里的表格删除
|
425
|
-
不能出现wb原本没有的表格名
|
426
|
-
"""
|
427
|
-
for name in set(wb.sheetnames) - set(new_sheetnames):
|
428
|
-
# 最好调用标准的remove接口删除sheet
|
429
|
-
# 不然虽然能表面上看也能删除sheet,但会有命名空间的一些冗余信息留下
|
430
|
-
wb.remove(wb[name])
|
431
|
-
wb._sheets = [wb[name] for name in new_sheetnames]
|
432
|
-
return wb
|
433
|
-
|
434
|
-
|
435
|
-
def demo_openpyxl():
|
436
|
-
# 一、新建一个工作薄
|
437
|
-
from openpyxl import Workbook
|
438
|
-
wb = Workbook()
|
439
|
-
|
440
|
-
# 取一个工作表
|
441
|
-
ws = wb.active # wb['Sheet'],取已知名称、下标的表格,excel不区分大小写,这里索引区分大小写
|
442
|
-
|
443
|
-
# 1 索引单元格的两种方法,及可以用.value获取值
|
444
|
-
ws['A2'] = '123'
|
445
|
-
dprint(ws.cell(2, 1).value) # 123
|
446
|
-
|
447
|
-
# 2 合并单元格
|
448
|
-
ws.merge_cells('A1:C2')
|
449
|
-
dprint(ws['A1'].value) # None,会把原来A2的内容清除
|
450
|
-
|
451
|
-
# print(ws['A2'].value) # AttributeError: 'MergedCell' object has no attribute 'value'
|
452
|
-
|
453
|
-
# ws.unmerge_cells('A1:A3') # ValueError: list.remove(x): x not in list,必须标记完整的合并单元格区域,否则会报错
|
454
|
-
ws['A1'].value = '模块一'
|
455
|
-
ws['A3'].value = '属性1'
|
456
|
-
ws['B3'].value = '属性2'
|
457
|
-
ws['C3'].value = '属性3'
|
458
|
-
|
459
|
-
ws.merge_cells('D1:E2')
|
460
|
-
ws['D1'].value = '模块二'
|
461
|
-
ws['D3'].value = '属性1'
|
462
|
-
ws['E3'].value = '属性2'
|
463
|
-
|
464
|
-
dprint(ws['A1'].offset(1, 0).coordinate) # A2
|
465
|
-
dprint(Openpyxl.down(ws['A1']).coordinate) # A3
|
466
|
-
|
467
|
-
# 3 设置单元格样式、格式
|
468
|
-
from openpyxl.comments import Comment
|
469
|
-
cell = ws['A3']
|
470
|
-
cell.font = Font(name='Courier', size=36)
|
471
|
-
cell.comment = Comment(text="A comment", author="Author's Name")
|
472
|
-
from openpyxl.styles.colors import RED
|
473
|
-
|
474
|
-
styles = [['Number formats', 'Comma', 'Comma [0]', 'Currency', 'Currency [0]', 'Percent'],
|
475
|
-
['Informative', 'Calculation', 'Total', 'Note', 'Warning Text', 'Explanatory Text'],
|
476
|
-
['Text styles', 'Title', 'Headline 1', 'Headline 2', 'Headline 3', 'Headline 4', 'Hyperlink',
|
477
|
-
'Followed Hyperlink', 'Linked Cell'],
|
478
|
-
['Comparisons', 'Input', 'Output', 'Check Cell', 'Good', 'Bad', 'Neutral'],
|
479
|
-
['Highlights', 'Accent1', '20 % - Accent1', '40 % - Accent1', '60 % - Accent1', 'Accent2', 'Accent3',
|
480
|
-
'Accent4', 'Accent5', 'Accent6', 'Pandas']]
|
481
|
-
for i, name in enumerate(styles, start=4):
|
482
|
-
ws.cell(i, 1, name[0])
|
483
|
-
for j, v in enumerate(name[1:], start=2):
|
484
|
-
ws.cell(i, j, v)
|
485
|
-
ws.cell(i, j).style = v
|
486
|
-
|
487
|
-
# 二、测试一些功能
|
488
|
-
ws = Worksheet(ws)
|
489
|
-
|
490
|
-
dprint(ws.search('模块二').coordinate) # D1
|
491
|
-
dprint(ws.search(['模块二', '属性1']).coordinate) # D3
|
492
|
-
|
493
|
-
dprint(ws.findcol(['模块一', '属性1'], direction=1)) # 0
|
494
|
-
|
495
|
-
wb.save("demo_openpyxl.xlsx")
|