tdrpa.tdworker 1.2.13.2__py312-none-win_amd64.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.
- tdrpa/_tdxlwings/__init__.py +193 -0
- tdrpa/_tdxlwings/__pycache__/__init__.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/__init__.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/_win32patch.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/_win32patch.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/_xlwindows.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/_xlwindows.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/apps.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/apps.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/base_classes.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/base_classes.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/com_server.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/com_server.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/constants.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/constants.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/expansion.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/expansion.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/main.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/main.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/udfs.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/udfs.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/utils.cpython-311.pyc +0 -0
- tdrpa/_tdxlwings/__pycache__/utils.cpython-38.pyc +0 -0
- tdrpa/_tdxlwings/_win32patch.py +90 -0
- tdrpa/_tdxlwings/_xlmac.py +2240 -0
- tdrpa/_tdxlwings/_xlwindows.py +2518 -0
- tdrpa/_tdxlwings/addin/Dictionary.cls +474 -0
- tdrpa/_tdxlwings/addin/IWebAuthenticator.cls +71 -0
- tdrpa/_tdxlwings/addin/WebClient.cls +772 -0
- tdrpa/_tdxlwings/addin/WebHelpers.bas +3203 -0
- tdrpa/_tdxlwings/addin/WebRequest.cls +875 -0
- tdrpa/_tdxlwings/addin/WebResponse.cls +453 -0
- tdrpa/_tdxlwings/addin/xlwings.xlam +0 -0
- tdrpa/_tdxlwings/apps.py +35 -0
- tdrpa/_tdxlwings/base_classes.py +1092 -0
- tdrpa/_tdxlwings/cli.py +1306 -0
- tdrpa/_tdxlwings/com_server.py +385 -0
- tdrpa/_tdxlwings/constants.py +3080 -0
- tdrpa/_tdxlwings/conversion/__init__.py +103 -0
- tdrpa/_tdxlwings/conversion/framework.py +147 -0
- tdrpa/_tdxlwings/conversion/numpy_conv.py +34 -0
- tdrpa/_tdxlwings/conversion/pandas_conv.py +184 -0
- tdrpa/_tdxlwings/conversion/standard.py +321 -0
- tdrpa/_tdxlwings/expansion.py +83 -0
- tdrpa/_tdxlwings/ext/__init__.py +3 -0
- tdrpa/_tdxlwings/ext/sql.py +73 -0
- tdrpa/_tdxlwings/html/xlwings-alert.html +71 -0
- tdrpa/_tdxlwings/js/xlwings.js +577 -0
- tdrpa/_tdxlwings/js/xlwings.ts +729 -0
- tdrpa/_tdxlwings/mac_dict.py +6399 -0
- tdrpa/_tdxlwings/main.py +5205 -0
- tdrpa/_tdxlwings/mistune/__init__.py +63 -0
- tdrpa/_tdxlwings/mistune/block_parser.py +366 -0
- tdrpa/_tdxlwings/mistune/inline_parser.py +216 -0
- tdrpa/_tdxlwings/mistune/markdown.py +84 -0
- tdrpa/_tdxlwings/mistune/renderers.py +220 -0
- tdrpa/_tdxlwings/mistune/scanner.py +121 -0
- tdrpa/_tdxlwings/mistune/util.py +41 -0
- tdrpa/_tdxlwings/pro/__init__.py +40 -0
- tdrpa/_tdxlwings/pro/_xlcalamine.py +536 -0
- tdrpa/_tdxlwings/pro/_xlofficejs.py +146 -0
- tdrpa/_tdxlwings/pro/_xlremote.py +1293 -0
- tdrpa/_tdxlwings/pro/custom_functions_code.js +150 -0
- tdrpa/_tdxlwings/pro/embedded_code.py +60 -0
- tdrpa/_tdxlwings/pro/udfs_officejs.py +549 -0
- tdrpa/_tdxlwings/pro/utils.py +199 -0
- tdrpa/_tdxlwings/quickstart.xlsm +0 -0
- tdrpa/_tdxlwings/quickstart_addin.xlam +0 -0
- tdrpa/_tdxlwings/quickstart_addin_ribbon.xlam +0 -0
- tdrpa/_tdxlwings/quickstart_fastapi/main.py +47 -0
- tdrpa/_tdxlwings/quickstart_fastapi/requirements.txt +3 -0
- tdrpa/_tdxlwings/quickstart_standalone.xlsm +0 -0
- tdrpa/_tdxlwings/reports.py +12 -0
- tdrpa/_tdxlwings/rest/__init__.py +1 -0
- tdrpa/_tdxlwings/rest/api.py +368 -0
- tdrpa/_tdxlwings/rest/serializers.py +103 -0
- tdrpa/_tdxlwings/server.py +14 -0
- tdrpa/_tdxlwings/udfs.py +775 -0
- tdrpa/_tdxlwings/utils.py +777 -0
- tdrpa/_tdxlwings/xlwings-0.31.6.applescript +30 -0
- tdrpa/_tdxlwings/xlwings.bas +2061 -0
- tdrpa/_tdxlwings/xlwings_custom_addin.bas +2042 -0
- tdrpa/_tdxlwings/xlwingslib.cp38-win_amd64.pyd +0 -0
- tdrpa/tdworker/__init__.pyi +12 -0
- tdrpa/tdworker/_clip.pyi +50 -0
- tdrpa/tdworker/_excel.pyi +743 -0
- tdrpa/tdworker/_file.pyi +77 -0
- tdrpa/tdworker/_img.pyi +226 -0
- tdrpa/tdworker/_network.pyi +94 -0
- tdrpa/tdworker/_os.pyi +47 -0
- tdrpa/tdworker/_sp.pyi +21 -0
- tdrpa/tdworker/_w.pyi +129 -0
- tdrpa/tdworker/_web.pyi +995 -0
- tdrpa/tdworker/_winE.pyi +228 -0
- tdrpa/tdworker/_winK.pyi +74 -0
- tdrpa/tdworker/_winM.pyi +117 -0
- tdrpa/tdworker.cp312-win_amd64.pyd +0 -0
- tdrpa_tdworker-1.2.13.2.dist-info/METADATA +38 -0
- tdrpa_tdworker-1.2.13.2.dist-info/RECORD +101 -0
- tdrpa_tdworker-1.2.13.2.dist-info/WHEEL +5 -0
- tdrpa_tdworker-1.2.13.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,2240 @@
|
|
1
|
+
import atexit
|
2
|
+
import datetime as dt
|
3
|
+
import os
|
4
|
+
import re
|
5
|
+
import shutil
|
6
|
+
import struct
|
7
|
+
import subprocess
|
8
|
+
from pathlib import Path
|
9
|
+
|
10
|
+
import aem
|
11
|
+
import appscript
|
12
|
+
import osax
|
13
|
+
import psutil
|
14
|
+
from appscript import its, k as kw, mactypes
|
15
|
+
from appscript.reference import CommandError
|
16
|
+
|
17
|
+
import tdrpa._tdxlwings as xlwings
|
18
|
+
|
19
|
+
from . import base_classes, mac_dict, utils
|
20
|
+
from .constants import ColorIndex
|
21
|
+
from .utils import (
|
22
|
+
VersionNumber,
|
23
|
+
col_name,
|
24
|
+
fullname_url_to_local_path,
|
25
|
+
int_to_rgb,
|
26
|
+
np_datetime_to_datetime,
|
27
|
+
read_config_sheet,
|
28
|
+
)
|
29
|
+
|
30
|
+
try:
|
31
|
+
import pandas as pd
|
32
|
+
except ImportError:
|
33
|
+
pd = None
|
34
|
+
try:
|
35
|
+
import numpy as np
|
36
|
+
except ImportError:
|
37
|
+
np = None
|
38
|
+
try:
|
39
|
+
from PIL import ImageGrab
|
40
|
+
except ImportError:
|
41
|
+
PIL = None
|
42
|
+
|
43
|
+
|
44
|
+
# Time types
|
45
|
+
time_types = (dt.date, dt.datetime)
|
46
|
+
if np:
|
47
|
+
time_types = time_types + (np.datetime64,)
|
48
|
+
|
49
|
+
cell_errors = (
|
50
|
+
"#DIV/0!",
|
51
|
+
"#N/A",
|
52
|
+
"#NAME?",
|
53
|
+
"#NULL!",
|
54
|
+
"#NUM!",
|
55
|
+
"#REF!",
|
56
|
+
"#VALUE!",
|
57
|
+
)
|
58
|
+
|
59
|
+
|
60
|
+
def _clean_value_data_element(
|
61
|
+
value, datetime_builder, empty_as, number_builder, err_to_str
|
62
|
+
):
|
63
|
+
if value == "" or value == kw.missing_value:
|
64
|
+
return empty_as
|
65
|
+
if isinstance(value, dt.datetime) and datetime_builder is not dt.datetime:
|
66
|
+
value = datetime_builder(
|
67
|
+
month=value.month,
|
68
|
+
day=value.day,
|
69
|
+
year=value.year,
|
70
|
+
hour=value.hour,
|
71
|
+
minute=value.minute,
|
72
|
+
second=value.second,
|
73
|
+
microsecond=value.microsecond,
|
74
|
+
tzinfo=None,
|
75
|
+
)
|
76
|
+
elif number_builder is not None and isinstance(value, float):
|
77
|
+
value = number_builder(value)
|
78
|
+
return value
|
79
|
+
|
80
|
+
|
81
|
+
class Engine:
|
82
|
+
@property
|
83
|
+
def apps(self):
|
84
|
+
return Apps()
|
85
|
+
|
86
|
+
@property
|
87
|
+
def name(self):
|
88
|
+
return "excel"
|
89
|
+
|
90
|
+
@property
|
91
|
+
def type(self):
|
92
|
+
return "desktop"
|
93
|
+
|
94
|
+
@staticmethod
|
95
|
+
def prepare_xl_data_element(x, options):
|
96
|
+
if x is None:
|
97
|
+
return ""
|
98
|
+
elif pd and pd.isna(x):
|
99
|
+
return ""
|
100
|
+
elif np and isinstance(x, (np.floating, float)) and np.isnan(x):
|
101
|
+
return ""
|
102
|
+
elif np and isinstance(x, np.datetime64):
|
103
|
+
# handle numpy.datetime64
|
104
|
+
return np_datetime_to_datetime(x).replace(tzinfo=None)
|
105
|
+
elif np and isinstance(x, np.number):
|
106
|
+
return float(x)
|
107
|
+
elif pd and isinstance(x, pd.Timestamp):
|
108
|
+
# This transformation seems to be only needed on Python 2.6 (?)
|
109
|
+
return x.to_pydatetime().replace(tzinfo=None)
|
110
|
+
elif pd and isinstance(x, type(pd.NaT)):
|
111
|
+
return None
|
112
|
+
elif isinstance(x, dt.datetime):
|
113
|
+
# Make datetime timezone naive
|
114
|
+
return x.replace(tzinfo=None)
|
115
|
+
elif isinstance(x, bool):
|
116
|
+
# Must be tested before int!
|
117
|
+
return x
|
118
|
+
elif isinstance(x, int):
|
119
|
+
# appscript packs integers larger than SInt32 but smaller than SInt64 as
|
120
|
+
# typeSInt64, and integers larger than SInt64 as typeIEEE64BitFloatingPoint.
|
121
|
+
# Excel silently ignores typeSInt64. (GH 227)
|
122
|
+
return float(x)
|
123
|
+
return x
|
124
|
+
|
125
|
+
@staticmethod
|
126
|
+
def clean_value_data(data, datetime_builder, empty_as, number_builder, err_to_str):
|
127
|
+
return [
|
128
|
+
[
|
129
|
+
_clean_value_data_element(
|
130
|
+
c, datetime_builder, empty_as, number_builder, err_to_str
|
131
|
+
)
|
132
|
+
for c in row
|
133
|
+
]
|
134
|
+
for row in data
|
135
|
+
]
|
136
|
+
|
137
|
+
|
138
|
+
engine = Engine()
|
139
|
+
|
140
|
+
|
141
|
+
class Apps(base_classes.Apps):
|
142
|
+
def _iter_excel_instances(self):
|
143
|
+
asn = subprocess.check_output(
|
144
|
+
["lsappinfo", "visibleprocesslist", "-includehidden"]
|
145
|
+
).decode("utf-8")
|
146
|
+
for asn in asn.split(" "):
|
147
|
+
if "Microsoft_Excel" in asn:
|
148
|
+
pid_info = subprocess.check_output(
|
149
|
+
["lsappinfo", "info", "-only", "pid", asn]
|
150
|
+
).decode("utf-8")
|
151
|
+
if pid_info != '"pid"=[ NULL ] \n':
|
152
|
+
yield int(pid_info.split("=")[1])
|
153
|
+
|
154
|
+
def keys(self):
|
155
|
+
return list(self._iter_excel_instances())
|
156
|
+
|
157
|
+
def add(self, spec=None, add_book=None, visible=None):
|
158
|
+
return App(spec=spec, add_book=add_book, visible=visible)
|
159
|
+
|
160
|
+
def __iter__(self):
|
161
|
+
for pid in self._iter_excel_instances():
|
162
|
+
yield App(xl=pid)
|
163
|
+
|
164
|
+
def __len__(self):
|
165
|
+
return len(list(self._iter_excel_instances()))
|
166
|
+
|
167
|
+
def __getitem__(self, pid):
|
168
|
+
if pid not in self.keys():
|
169
|
+
raise KeyError("Could not find an Excel instance with this PID.")
|
170
|
+
return App(xl=pid)
|
171
|
+
|
172
|
+
|
173
|
+
class App(base_classes.App):
|
174
|
+
def __init__(self, spec=None, add_book=None, xl=None, visible=True):
|
175
|
+
if xl is None:
|
176
|
+
self._xl = appscript.app(
|
177
|
+
name=spec or "Microsoft Excel",
|
178
|
+
newinstance=True,
|
179
|
+
terms=mac_dict,
|
180
|
+
hide=not visible,
|
181
|
+
)
|
182
|
+
if visible:
|
183
|
+
self.activate() # Makes it behave like on Windows
|
184
|
+
elif isinstance(xl, int):
|
185
|
+
self._xl = appscript.app(pid=xl, terms=mac_dict)
|
186
|
+
else:
|
187
|
+
self._xl = xl
|
188
|
+
|
189
|
+
@property
|
190
|
+
def xl(self):
|
191
|
+
return self._xl
|
192
|
+
|
193
|
+
@xl.setter
|
194
|
+
def xl(self, value):
|
195
|
+
self._xl = value
|
196
|
+
|
197
|
+
@property
|
198
|
+
def api(self):
|
199
|
+
return self.xl
|
200
|
+
|
201
|
+
@property
|
202
|
+
def engine(self):
|
203
|
+
return engine
|
204
|
+
|
205
|
+
@property
|
206
|
+
def path(self):
|
207
|
+
return hfs_to_posix_path(self.xl.path.get())
|
208
|
+
|
209
|
+
@property
|
210
|
+
def pid(self):
|
211
|
+
data = (
|
212
|
+
self.xl.AS_appdata.target()
|
213
|
+
.addressdesc.coerce(aem.kae.typeKernelProcessID)
|
214
|
+
.data
|
215
|
+
)
|
216
|
+
(pid,) = struct.unpack("i", data)
|
217
|
+
return pid
|
218
|
+
|
219
|
+
@property
|
220
|
+
def version(self):
|
221
|
+
return self.xl.version.get()
|
222
|
+
|
223
|
+
@property
|
224
|
+
def selection(self):
|
225
|
+
sheet = self.books.active.sheets.active
|
226
|
+
try:
|
227
|
+
# fails if e.g. chart is selected
|
228
|
+
return Range(sheet, self.xl.selection.get_address())
|
229
|
+
except CommandError:
|
230
|
+
return None
|
231
|
+
|
232
|
+
def activate(self, steal_focus=False):
|
233
|
+
asn = subprocess.check_output(
|
234
|
+
["lsappinfo", "visibleprocesslist", "-includehidden"]
|
235
|
+
).decode("utf-8")
|
236
|
+
frontmost_asn = asn.split(" ")[0]
|
237
|
+
pid_info_frontmost = subprocess.check_output(
|
238
|
+
["lsappinfo", "info", "-only", "pid", frontmost_asn]
|
239
|
+
).decode("utf-8")
|
240
|
+
pid_frontmost = int(pid_info_frontmost.split("=")[1])
|
241
|
+
try:
|
242
|
+
appscript.app("System Events").processes[
|
243
|
+
its.unix_id == self.pid
|
244
|
+
].frontmost.set(True)
|
245
|
+
if not steal_focus:
|
246
|
+
appscript.app("System Events").processes[
|
247
|
+
its.unix_id == pid_frontmost
|
248
|
+
].frontmost.set(True)
|
249
|
+
except CommandError:
|
250
|
+
pass # may require root privileges (GH 1966)
|
251
|
+
|
252
|
+
@property
|
253
|
+
def visible(self):
|
254
|
+
try:
|
255
|
+
return (
|
256
|
+
appscript.app("System Events")
|
257
|
+
.processes[its.unix_id == self.pid]
|
258
|
+
.visible.get()[0]
|
259
|
+
)
|
260
|
+
except CommandError:
|
261
|
+
return None # may require root privileges (GH 1966)
|
262
|
+
|
263
|
+
@visible.setter
|
264
|
+
def visible(self, visible):
|
265
|
+
try:
|
266
|
+
appscript.app("System Events").processes[
|
267
|
+
its.unix_id == self.pid
|
268
|
+
].visible.set(visible)
|
269
|
+
except CommandError:
|
270
|
+
pass # may require root privileges (GH 1966)
|
271
|
+
|
272
|
+
def quit(self):
|
273
|
+
self.xl.quit(saving=kw.no)
|
274
|
+
|
275
|
+
def kill(self):
|
276
|
+
psutil.Process(self.pid).kill()
|
277
|
+
|
278
|
+
@property
|
279
|
+
def screen_updating(self):
|
280
|
+
return self.xl.screen_updating.get()
|
281
|
+
|
282
|
+
@screen_updating.setter
|
283
|
+
def screen_updating(self, value):
|
284
|
+
self.xl.screen_updating.set(value)
|
285
|
+
|
286
|
+
@property
|
287
|
+
def display_alerts(self):
|
288
|
+
return self.xl.display_alerts.get()
|
289
|
+
|
290
|
+
@display_alerts.setter
|
291
|
+
def display_alerts(self, value):
|
292
|
+
self.xl.display_alerts.set(value)
|
293
|
+
|
294
|
+
@property
|
295
|
+
def enable_events(self):
|
296
|
+
return self.xl.enable_events.get()
|
297
|
+
|
298
|
+
@enable_events.setter
|
299
|
+
def enable_events(self, value):
|
300
|
+
self.xl.enable_events.set(value)
|
301
|
+
|
302
|
+
@property
|
303
|
+
def interactive(self):
|
304
|
+
# TODO: replace with specific error when Exceptions are refactored
|
305
|
+
raise xlwings.XlwingsError(
|
306
|
+
"Getting or setting 'app.interactive' isn't supported on macOS."
|
307
|
+
)
|
308
|
+
|
309
|
+
@interactive.setter
|
310
|
+
def interactive(self, value):
|
311
|
+
# TODO: replace with specific error when Exceptions are refactored
|
312
|
+
raise xlwings.XlwingsError(
|
313
|
+
"Getting or setting 'app.interactive' isn't supported on macOS."
|
314
|
+
)
|
315
|
+
|
316
|
+
@property
|
317
|
+
def startup_path(self):
|
318
|
+
return hfs_to_posix_path(self.xl.startup_path.get())
|
319
|
+
|
320
|
+
@property
|
321
|
+
def calculation(self):
|
322
|
+
return calculation_k2s[self.xl.calculation.get()]
|
323
|
+
|
324
|
+
@calculation.setter
|
325
|
+
def calculation(self, value):
|
326
|
+
self.xl.calculation.set(calculation_s2k[value])
|
327
|
+
|
328
|
+
def calculate(self):
|
329
|
+
self.xl.calculate()
|
330
|
+
|
331
|
+
@property
|
332
|
+
def books(self):
|
333
|
+
return Books(self)
|
334
|
+
|
335
|
+
@property
|
336
|
+
def hwnd(self):
|
337
|
+
return None
|
338
|
+
|
339
|
+
def run(self, macro, args):
|
340
|
+
kwargs = {"arg{0}".format(i): n for i, n in enumerate(args, 1)}
|
341
|
+
return self.xl.run_VB_macro(macro, **kwargs)
|
342
|
+
|
343
|
+
@property
|
344
|
+
def status_bar(self):
|
345
|
+
return self.xl.status_bar.get()
|
346
|
+
|
347
|
+
@status_bar.setter
|
348
|
+
def status_bar(self, value):
|
349
|
+
self.xl.status_bar.set(value)
|
350
|
+
|
351
|
+
@property
|
352
|
+
def cut_copy_mode(self):
|
353
|
+
modes = {kw.cut_mode: "cut", kw.copy_mode: "copy"}
|
354
|
+
return modes.get(self.xl.cut_copy_mode.get())
|
355
|
+
|
356
|
+
@cut_copy_mode.setter
|
357
|
+
def cut_copy_mode(self, value):
|
358
|
+
self.xl.cut_copy_mode.set(value)
|
359
|
+
|
360
|
+
def alert(self, prompt, title, buttons, mode, callback):
|
361
|
+
# OSAX (Open Scripting Architecture Extension) instance for StandardAdditions
|
362
|
+
# See /System/Library/ScriptingAdditions/StandardAdditions.osax
|
363
|
+
sa = osax.OSAX(pid=self.pid)
|
364
|
+
sa.activate() # Activate app so dialog box will be visible
|
365
|
+
modes = {
|
366
|
+
None: kw.informational,
|
367
|
+
"info": kw.informational,
|
368
|
+
"critical": kw.critical,
|
369
|
+
}
|
370
|
+
buttons_dict = {
|
371
|
+
None: "OK",
|
372
|
+
"ok": "OK",
|
373
|
+
"ok_cancel": ["Cancel", "OK"],
|
374
|
+
"yes_no": ["No", "Yes"],
|
375
|
+
"yes_no_cancel": ["Cancel", "No", "Yes"],
|
376
|
+
}
|
377
|
+
rv = sa.display_alert(
|
378
|
+
"" if title is None else title,
|
379
|
+
message="" if prompt is None else prompt,
|
380
|
+
buttons=buttons_dict[buttons],
|
381
|
+
as_=modes[mode],
|
382
|
+
)
|
383
|
+
return rv[kw.button_returned].lower()
|
384
|
+
|
385
|
+
|
386
|
+
class Books(base_classes.Books):
|
387
|
+
def __init__(self, app):
|
388
|
+
self.app = app
|
389
|
+
|
390
|
+
@property
|
391
|
+
def api(self):
|
392
|
+
return None
|
393
|
+
|
394
|
+
@property
|
395
|
+
def active(self):
|
396
|
+
return Book(self.app, self.app.xl.active_workbook.name.get())
|
397
|
+
|
398
|
+
def __call__(self, name_or_index):
|
399
|
+
b = Book(self.app, name_or_index)
|
400
|
+
if not b.xl.exists():
|
401
|
+
raise KeyError(name_or_index)
|
402
|
+
return b
|
403
|
+
|
404
|
+
def __contains__(self, key):
|
405
|
+
return Book(self.app, key).xl.exists()
|
406
|
+
|
407
|
+
def __len__(self):
|
408
|
+
return self.app.xl.count(each=kw.workbook)
|
409
|
+
|
410
|
+
def add(self):
|
411
|
+
if self.app.visible:
|
412
|
+
self.app.activate()
|
413
|
+
xl = self.app.xl.make(new=kw.workbook)
|
414
|
+
wb = Book(self.app, xl.name.get())
|
415
|
+
return wb
|
416
|
+
|
417
|
+
def open(
|
418
|
+
self,
|
419
|
+
fullname,
|
420
|
+
update_links=None,
|
421
|
+
read_only=None,
|
422
|
+
format=None,
|
423
|
+
password=None,
|
424
|
+
write_res_password=None,
|
425
|
+
ignore_read_only_recommended=None,
|
426
|
+
origin=None,
|
427
|
+
delimiter=None,
|
428
|
+
editable=None,
|
429
|
+
notify=None,
|
430
|
+
converter=None,
|
431
|
+
add_to_mru=None,
|
432
|
+
local=None,
|
433
|
+
corrupt_load=None,
|
434
|
+
):
|
435
|
+
# TODO: format and origin currently require a native appscript keyword,
|
436
|
+
# read_only doesn't seem to work
|
437
|
+
# Unsupported params
|
438
|
+
if local is not None:
|
439
|
+
# TODO: replace with specific error when Exceptions are refactored
|
440
|
+
raise xlwings.XlwingsError("local is not supported on macOS")
|
441
|
+
if corrupt_load is not None:
|
442
|
+
# TODO: replace with specific error when Exceptions are refactored
|
443
|
+
raise xlwings.XlwingsError("corrupt_load is not supported on macOS")
|
444
|
+
# update_links: on Windows only constants 0 and 3 seem to be supported in
|
445
|
+
# this context
|
446
|
+
if update_links:
|
447
|
+
update_links = kw.update_remote_and_external_links
|
448
|
+
else:
|
449
|
+
update_links = kw.do_not_update_links
|
450
|
+
if self.app.visible:
|
451
|
+
self.app.activate()
|
452
|
+
filename = os.path.basename(fullname)
|
453
|
+
self.app.xl.open_workbook(
|
454
|
+
workbook_file_name=fullname,
|
455
|
+
update_links=update_links,
|
456
|
+
read_only=read_only,
|
457
|
+
format=format,
|
458
|
+
password=password,
|
459
|
+
write_reserved_password=write_res_password,
|
460
|
+
ignore_read_only_recommended=ignore_read_only_recommended,
|
461
|
+
origin=origin,
|
462
|
+
delimiter=delimiter,
|
463
|
+
editable=editable,
|
464
|
+
notify=notify,
|
465
|
+
converter=converter,
|
466
|
+
add_to_mru=add_to_mru,
|
467
|
+
timeout=-1,
|
468
|
+
)
|
469
|
+
wb = Book(self.app, filename)
|
470
|
+
return wb
|
471
|
+
|
472
|
+
def __iter__(self):
|
473
|
+
n = len(self)
|
474
|
+
for i in range(n):
|
475
|
+
yield Book(self.app, i + 1)
|
476
|
+
|
477
|
+
|
478
|
+
class Book(base_classes.Book):
|
479
|
+
def __init__(self, app, name_or_index):
|
480
|
+
self._app = app
|
481
|
+
self.xl = app.xl.workbooks[name_or_index]
|
482
|
+
|
483
|
+
@property
|
484
|
+
def app(self):
|
485
|
+
return self._app
|
486
|
+
|
487
|
+
@property
|
488
|
+
def api(self):
|
489
|
+
return self.xl
|
490
|
+
|
491
|
+
def json(self):
|
492
|
+
raise NotImplementedError()
|
493
|
+
|
494
|
+
@property
|
495
|
+
def name(self):
|
496
|
+
return self.xl.name.get()
|
497
|
+
|
498
|
+
@property
|
499
|
+
def sheets(self):
|
500
|
+
return Sheets(self)
|
501
|
+
|
502
|
+
def close(self):
|
503
|
+
self.xl.close(saving=kw.no)
|
504
|
+
|
505
|
+
def save(self, path, password):
|
506
|
+
saved_path = self.xl.properties().get(kw.path)
|
507
|
+
source_ext = os.path.splitext(self.name)[1] if saved_path else None
|
508
|
+
target_ext = os.path.splitext(path)[1] if path else ".xlsx"
|
509
|
+
if saved_path and source_ext == target_ext:
|
510
|
+
file_format = self.xl.properties().get(kw.file_format)
|
511
|
+
else:
|
512
|
+
ext_to_file_format = {
|
513
|
+
".xlsx": kw.Excel_XML_file_format,
|
514
|
+
".xlsm": kw.macro_enabled_XML_file_format,
|
515
|
+
".xlsb": kw.Excel_binary_file_format,
|
516
|
+
".xltm": kw.macro_enabled_template_file_format,
|
517
|
+
".xltx": kw.template_file_format,
|
518
|
+
".xlam": kw.add_in_file_format,
|
519
|
+
".xls": kw.Excel98to2004_file_format,
|
520
|
+
".xlt": kw.Excel98to2004_template_file_format,
|
521
|
+
".xla": kw.Excel98to2004_add_in_file_format,
|
522
|
+
}
|
523
|
+
|
524
|
+
file_format = ext_to_file_format[target_ext]
|
525
|
+
if (saved_path != "") and (path is None):
|
526
|
+
# Previously saved: Save under existing name
|
527
|
+
self.xl.save(timeout=-1)
|
528
|
+
elif (
|
529
|
+
(saved_path != "") and (path is not None) and (os.path.split(path)[0] == "")
|
530
|
+
):
|
531
|
+
# Save existing book under new name in cwd if no path has been provided
|
532
|
+
save_as_name = path
|
533
|
+
path = os.path.join(os.getcwd(), path)
|
534
|
+
hfs_path = posix_to_hfs_path(os.path.realpath(path))
|
535
|
+
self.xl.save_workbook_as(
|
536
|
+
filename=hfs_path,
|
537
|
+
overwrite=True,
|
538
|
+
file_format=file_format,
|
539
|
+
timeout=-1,
|
540
|
+
password=password,
|
541
|
+
)
|
542
|
+
self.xl = self.app.xl.workbooks[save_as_name]
|
543
|
+
elif (saved_path == "") and (path is None):
|
544
|
+
# Previously unsaved: Save under current name in current working directory
|
545
|
+
save_as_name = self.xl.name.get() + ".xlsx"
|
546
|
+
path = os.path.join(os.getcwd(), save_as_name)
|
547
|
+
hfs_path = posix_to_hfs_path(os.path.realpath(path))
|
548
|
+
self.xl.save_workbook_as(
|
549
|
+
filename=hfs_path,
|
550
|
+
overwrite=True,
|
551
|
+
file_format=file_format,
|
552
|
+
timeout=-1,
|
553
|
+
password=password,
|
554
|
+
)
|
555
|
+
self.xl = self.app.xl.workbooks[save_as_name]
|
556
|
+
elif path:
|
557
|
+
# Save under new name/location
|
558
|
+
hfs_path = posix_to_hfs_path(os.path.realpath(path))
|
559
|
+
self.xl.save_workbook_as(
|
560
|
+
filename=hfs_path,
|
561
|
+
overwrite=True,
|
562
|
+
file_format=file_format,
|
563
|
+
timeout=-1,
|
564
|
+
password=password,
|
565
|
+
)
|
566
|
+
self.xl = self.app.xl.workbooks[os.path.basename(path)]
|
567
|
+
|
568
|
+
@property
|
569
|
+
def fullname(self):
|
570
|
+
display_alerts = self.app.display_alerts
|
571
|
+
self.app.display_alerts = False
|
572
|
+
# This causes a pop-up if there's a pw protected sheet, see #1377
|
573
|
+
path = self.xl.properties().get(kw.full_name)
|
574
|
+
if "://" in path:
|
575
|
+
config = read_config_sheet(xlwings.Book(impl=self))
|
576
|
+
self.app.display_alerts = display_alerts
|
577
|
+
return fullname_url_to_local_path(
|
578
|
+
url=path,
|
579
|
+
sheet_onedrive_consumer_config=config.get("ONEDRIVE_CONSUMER_MAC"),
|
580
|
+
sheet_onedrive_commercial_config=config.get("ONEDRIVE_COMMERCIAL_MAC"),
|
581
|
+
sheet_sharepoint_config=config.get("SHAREPOINT_MAC"),
|
582
|
+
)
|
583
|
+
else:
|
584
|
+
self.app.display_alerts = display_alerts
|
585
|
+
return path
|
586
|
+
|
587
|
+
@property
|
588
|
+
def names(self):
|
589
|
+
return Names(parent=self, xl=self.xl.named_items)
|
590
|
+
|
591
|
+
def activate(self):
|
592
|
+
self.xl.activate_object()
|
593
|
+
|
594
|
+
def to_pdf(self, path, quality=None):
|
595
|
+
# quality parameter for compatibility
|
596
|
+
hfs_path = posix_to_hfs_path(path)
|
597
|
+
display_alerts = self.app.display_alerts
|
598
|
+
self.app.display_alerts = False
|
599
|
+
if Path(path).exists():
|
600
|
+
# Errors out with Parameter error (OSERROR: -50) otherwise
|
601
|
+
os.unlink(path)
|
602
|
+
self.xl.save(in_=hfs_path, as_=kw.PDF_file_format)
|
603
|
+
self.app.display_alerts = display_alerts
|
604
|
+
|
605
|
+
|
606
|
+
class Sheets(base_classes.Sheets):
|
607
|
+
def __init__(self, workbook):
|
608
|
+
self.workbook = workbook
|
609
|
+
|
610
|
+
@property
|
611
|
+
def api(self):
|
612
|
+
return None
|
613
|
+
|
614
|
+
@property
|
615
|
+
def active(self):
|
616
|
+
return Sheet(self.workbook, self.workbook.xl.active_sheet.name.get())
|
617
|
+
|
618
|
+
def __call__(self, name_or_index):
|
619
|
+
return Sheet(self.workbook, name_or_index)
|
620
|
+
|
621
|
+
def __len__(self):
|
622
|
+
return self.workbook.xl.count(each=kw.worksheet)
|
623
|
+
|
624
|
+
def __iter__(self):
|
625
|
+
for i in range(len(self)):
|
626
|
+
yield self(i + 1)
|
627
|
+
|
628
|
+
def add(self, before=None, after=None, name=None):
|
629
|
+
if before is None and after is None:
|
630
|
+
before = self.workbook.app.books.active.sheets.active
|
631
|
+
if before:
|
632
|
+
position = before.xl.before
|
633
|
+
else:
|
634
|
+
position = after.xl.after
|
635
|
+
xl = self.workbook.xl.make(new=kw.worksheet, at=position)
|
636
|
+
if name is not None:
|
637
|
+
xl.name.set(name)
|
638
|
+
xl = self.workbook.xl.worksheets[name]
|
639
|
+
return Sheet(self.workbook, xl.name.get())
|
640
|
+
|
641
|
+
|
642
|
+
class Sheet(base_classes.Sheet):
|
643
|
+
def __init__(self, workbook, name_or_index):
|
644
|
+
self.workbook = workbook
|
645
|
+
self.xl = workbook.xl.worksheets[name_or_index]
|
646
|
+
|
647
|
+
@property
|
648
|
+
def api(self):
|
649
|
+
return self.xl
|
650
|
+
|
651
|
+
@property
|
652
|
+
def name(self):
|
653
|
+
return self.xl.name.get()
|
654
|
+
|
655
|
+
@name.setter
|
656
|
+
def name(self, value):
|
657
|
+
self.xl.name.set(value)
|
658
|
+
self.xl = self.workbook.xl.worksheets[value]
|
659
|
+
|
660
|
+
@property
|
661
|
+
def names(self):
|
662
|
+
return Names(parent=self, xl=self.xl.named_items)
|
663
|
+
|
664
|
+
@property
|
665
|
+
def book(self):
|
666
|
+
return self.workbook
|
667
|
+
|
668
|
+
@property
|
669
|
+
def index(self):
|
670
|
+
return self.xl.entry_index.get()
|
671
|
+
|
672
|
+
def range(self, arg1, arg2=None):
|
673
|
+
if isinstance(arg1, tuple):
|
674
|
+
if len(arg1) == 2:
|
675
|
+
if 0 in arg1:
|
676
|
+
raise IndexError(
|
677
|
+
"Attempted to access 0-based Range. "
|
678
|
+
"xlwings/Excel Ranges are 1-based."
|
679
|
+
)
|
680
|
+
row1 = arg1[0]
|
681
|
+
col1 = arg1[1]
|
682
|
+
address1 = self.xl.rows[row1].columns[col1].get_address()
|
683
|
+
elif len(arg1) == 4:
|
684
|
+
return Range(self, arg1)
|
685
|
+
else:
|
686
|
+
raise ValueError("Invalid parameters")
|
687
|
+
elif isinstance(arg1, Range):
|
688
|
+
row1 = min(arg1.row, arg2.row)
|
689
|
+
col1 = min(arg1.column, arg2.column)
|
690
|
+
address1 = self.xl.rows[row1].columns[col1].get_address()
|
691
|
+
elif isinstance(arg1, str):
|
692
|
+
address1 = arg1.split(":")[0]
|
693
|
+
else:
|
694
|
+
raise ValueError("Invalid parameters")
|
695
|
+
|
696
|
+
if isinstance(arg2, tuple):
|
697
|
+
if 0 in arg2:
|
698
|
+
raise IndexError(
|
699
|
+
"Attempted to access 0-based Range. "
|
700
|
+
"xlwings/Excel Ranges are 1-based."
|
701
|
+
)
|
702
|
+
row2 = arg2[0]
|
703
|
+
col2 = arg2[1]
|
704
|
+
address2 = self.xl.rows[row2].columns[col2].get_address()
|
705
|
+
elif isinstance(arg2, Range):
|
706
|
+
row2 = max(arg1.row + arg1.shape[0] - 1, arg2.row + arg2.shape[0] - 1)
|
707
|
+
col2 = max(arg1.column + arg1.shape[1] - 1, arg2.column + arg2.shape[1] - 1)
|
708
|
+
address2 = self.xl.rows[row2].columns[col2].get_address()
|
709
|
+
elif isinstance(arg2, str):
|
710
|
+
address2 = arg2
|
711
|
+
elif arg2 is None:
|
712
|
+
if isinstance(arg1, str) and len(arg1.split(":")) == 2:
|
713
|
+
address2 = arg1.split(":")[1]
|
714
|
+
else:
|
715
|
+
return Range(self, "{0}".format(address1))
|
716
|
+
else:
|
717
|
+
raise ValueError("Invalid parameters")
|
718
|
+
|
719
|
+
return Range(self, "{0}:{1}".format(address1, address2))
|
720
|
+
|
721
|
+
@property
|
722
|
+
def cells(self):
|
723
|
+
return self.range(
|
724
|
+
(1, 1), (self.xl.count(each=kw.row), self.xl.count(each=kw.column))
|
725
|
+
)
|
726
|
+
|
727
|
+
def activate(self):
|
728
|
+
self.xl.activate_object()
|
729
|
+
|
730
|
+
def select(self):
|
731
|
+
self.xl.select()
|
732
|
+
|
733
|
+
def clear_contents(self):
|
734
|
+
self.xl.used_range.clear_contents()
|
735
|
+
|
736
|
+
def clear_formats(self):
|
737
|
+
self.xl.used_range.clear_formats()
|
738
|
+
|
739
|
+
def clear(self):
|
740
|
+
self.xl.used_range.clear_range()
|
741
|
+
|
742
|
+
def autofit(self, axis=None):
|
743
|
+
num_columns = self.xl.count(each=kw.column)
|
744
|
+
num_rows = self.xl.count(each=kw.row)
|
745
|
+
address = self.range((1, 1), (num_rows, num_columns)).address
|
746
|
+
alerts_state = self.book.app.screen_updating
|
747
|
+
self.book.app.screen_updating = False
|
748
|
+
if axis == "rows" or axis == "r":
|
749
|
+
self.xl.rows[address].autofit()
|
750
|
+
elif axis == "columns" or axis == "c":
|
751
|
+
self.xl.columns[address].autofit()
|
752
|
+
elif axis is None:
|
753
|
+
self.xl.rows[address].autofit()
|
754
|
+
self.xl.columns[address].autofit()
|
755
|
+
self.book.app.screen_updating = alerts_state
|
756
|
+
|
757
|
+
def delete(self):
|
758
|
+
alerts_state = self.book.app.xl.display_alerts.get()
|
759
|
+
self.book.app.xl.display_alerts.set(False)
|
760
|
+
self.xl.delete()
|
761
|
+
self.book.app.xl.display_alerts.set(alerts_state)
|
762
|
+
|
763
|
+
def copy(self, before, after):
|
764
|
+
if before:
|
765
|
+
before = before.xl
|
766
|
+
if after:
|
767
|
+
after = after.xl
|
768
|
+
self.xl.copy_worksheet(before_=before, after_=after)
|
769
|
+
|
770
|
+
@property
|
771
|
+
def charts(self):
|
772
|
+
return Charts(self)
|
773
|
+
|
774
|
+
@property
|
775
|
+
def shapes(self):
|
776
|
+
return Shapes(self)
|
777
|
+
|
778
|
+
@property
|
779
|
+
def tables(self):
|
780
|
+
return Tables(self)
|
781
|
+
|
782
|
+
@property
|
783
|
+
def pictures(self):
|
784
|
+
return Pictures(self)
|
785
|
+
|
786
|
+
@property
|
787
|
+
def used_range(self):
|
788
|
+
return Range(self, self.xl.used_range.get_address())
|
789
|
+
|
790
|
+
@property
|
791
|
+
def visible(self):
|
792
|
+
return True if self.xl.visible.get() == kw.sheet_visible else False
|
793
|
+
|
794
|
+
@visible.setter
|
795
|
+
def visible(self, value):
|
796
|
+
self.xl.visible.set(value)
|
797
|
+
|
798
|
+
@property
|
799
|
+
def page_setup(self):
|
800
|
+
return PageSetup(self, self.xl.page_setup_object)
|
801
|
+
|
802
|
+
|
803
|
+
class Range(base_classes.Range):
|
804
|
+
def __init__(self, sheet, address):
|
805
|
+
self.sheet = sheet
|
806
|
+
self.options = None # Assigned by main.Range to keep API of sheet.range clean
|
807
|
+
if isinstance(address, tuple):
|
808
|
+
self._coords = address
|
809
|
+
row, col, nrows, ncols = address
|
810
|
+
if nrows and ncols:
|
811
|
+
self.xl = sheet.xl.cells[
|
812
|
+
"%s:%s"
|
813
|
+
% (
|
814
|
+
sheet.xl.rows[row].columns[col].get_address(),
|
815
|
+
sheet.xl.rows[row + nrows - 1]
|
816
|
+
.columns[col + ncols - 1]
|
817
|
+
.get_address(),
|
818
|
+
)
|
819
|
+
]
|
820
|
+
else:
|
821
|
+
self.xl = None
|
822
|
+
else:
|
823
|
+
self.xl = sheet.xl.cells[address]
|
824
|
+
self._coords = None
|
825
|
+
|
826
|
+
@property
|
827
|
+
def coords(self):
|
828
|
+
if self._coords is None:
|
829
|
+
self._coords = (
|
830
|
+
self.xl.first_row_index.get(),
|
831
|
+
self.xl.first_column_index.get(),
|
832
|
+
self.xl.count(each=kw.row),
|
833
|
+
self.xl.count(each=kw.column),
|
834
|
+
)
|
835
|
+
return self._coords
|
836
|
+
|
837
|
+
@property
|
838
|
+
def api(self):
|
839
|
+
return self.xl
|
840
|
+
|
841
|
+
def __len__(self):
|
842
|
+
return self.coords[2] * self.coords[3]
|
843
|
+
|
844
|
+
@property
|
845
|
+
def row(self):
|
846
|
+
return self.coords[0]
|
847
|
+
|
848
|
+
@property
|
849
|
+
def column(self):
|
850
|
+
return self.coords[1]
|
851
|
+
|
852
|
+
@property
|
853
|
+
def shape(self):
|
854
|
+
return self.coords[2], self.coords[3]
|
855
|
+
|
856
|
+
@property
|
857
|
+
def raw_value(self):
|
858
|
+
def ensure_2d(values):
|
859
|
+
# Usually done in converter, but macOS doesn't deliver any info about
|
860
|
+
# errors with values
|
861
|
+
if not isinstance(values, list):
|
862
|
+
return [[values]]
|
863
|
+
elif not isinstance(values[0], list):
|
864
|
+
return [values]
|
865
|
+
|
866
|
+
if self.xl is not None:
|
867
|
+
values = self.xl.value.get()
|
868
|
+
if self.options.get("err_to_str", False):
|
869
|
+
string_values = self.xl.string_value.get()
|
870
|
+
values = ensure_2d(values)
|
871
|
+
string_values = ensure_2d(string_values)
|
872
|
+
for row_ix, row in enumerate(string_values):
|
873
|
+
for col_ix, c in enumerate(row):
|
874
|
+
if c in cell_errors:
|
875
|
+
values[row_ix][col_ix] = c
|
876
|
+
return values
|
877
|
+
|
878
|
+
@raw_value.setter
|
879
|
+
def raw_value(self, value):
|
880
|
+
if self.xl is not None:
|
881
|
+
self.xl.value.set(value)
|
882
|
+
|
883
|
+
def clear_contents(self):
|
884
|
+
if self.xl is not None:
|
885
|
+
alerts_state = self.sheet.book.app.screen_updating
|
886
|
+
self.sheet.book.app.screen_updating = False
|
887
|
+
self.xl.clear_contents()
|
888
|
+
self.sheet.book.app.screen_updating = alerts_state
|
889
|
+
|
890
|
+
def clear_formats(self):
|
891
|
+
if self.xl is not None:
|
892
|
+
alerts_state = self.sheet.book.app.screen_updating
|
893
|
+
self.sheet.book.app.screen_updating = False
|
894
|
+
self.xl.clear_formats()
|
895
|
+
self.sheet.book.app.screen_updating = alerts_state
|
896
|
+
|
897
|
+
def clear(self):
|
898
|
+
if self.xl is not None:
|
899
|
+
alerts_state = self.sheet.book.app.screen_updating
|
900
|
+
self.sheet.book.app.screen_updating = False
|
901
|
+
self.xl.clear_range()
|
902
|
+
self.sheet.book.app.screen_updating = alerts_state
|
903
|
+
|
904
|
+
def end(self, direction):
|
905
|
+
direction = directions_s2k.get(direction, direction)
|
906
|
+
return Range(self.sheet, self.xl.get_end(direction=direction).get_address())
|
907
|
+
|
908
|
+
@property
|
909
|
+
def formula(self):
|
910
|
+
if self.xl is not None:
|
911
|
+
return self.xl.formula.get()
|
912
|
+
|
913
|
+
@formula.setter
|
914
|
+
def formula(self, value):
|
915
|
+
if self.xl is not None:
|
916
|
+
self.xl.formula.set(value)
|
917
|
+
|
918
|
+
@property
|
919
|
+
def formula2(self):
|
920
|
+
if self.xl is not None:
|
921
|
+
return self.xl.formula2.get()
|
922
|
+
|
923
|
+
@formula2.setter
|
924
|
+
def formula2(self, value):
|
925
|
+
if self.xl is not None:
|
926
|
+
self.xl.formula2.set(value)
|
927
|
+
|
928
|
+
@property
|
929
|
+
def formula_array(self):
|
930
|
+
if self.xl is not None:
|
931
|
+
rv = self.xl.formula_array.get()
|
932
|
+
return None if rv == kw.missing_value else rv
|
933
|
+
|
934
|
+
@formula_array.setter
|
935
|
+
def formula_array(self, value):
|
936
|
+
if self.xl is not None:
|
937
|
+
self.xl.formula_array.set(value)
|
938
|
+
|
939
|
+
@property
|
940
|
+
def font(self):
|
941
|
+
return Font(self, self.xl.font_object)
|
942
|
+
|
943
|
+
@property
|
944
|
+
def column_width(self):
|
945
|
+
if self.xl is not None:
|
946
|
+
rv = self.xl.column_width.get()
|
947
|
+
return None if rv == kw.missing_value else rv
|
948
|
+
else:
|
949
|
+
return 0
|
950
|
+
|
951
|
+
@column_width.setter
|
952
|
+
def column_width(self, value):
|
953
|
+
if self.xl is not None:
|
954
|
+
self.xl.column_width.set(value)
|
955
|
+
|
956
|
+
@property
|
957
|
+
def row_height(self):
|
958
|
+
if self.xl is not None:
|
959
|
+
rv = self.xl.row_height.get()
|
960
|
+
return None if rv == kw.missing_value else rv
|
961
|
+
else:
|
962
|
+
return 0
|
963
|
+
|
964
|
+
@row_height.setter
|
965
|
+
def row_height(self, value):
|
966
|
+
if self.xl is not None:
|
967
|
+
self.xl.row_height.set(value)
|
968
|
+
|
969
|
+
@property
|
970
|
+
def width(self):
|
971
|
+
if self.xl is not None:
|
972
|
+
return self.xl.width.get()
|
973
|
+
else:
|
974
|
+
return 0
|
975
|
+
|
976
|
+
@property
|
977
|
+
def height(self):
|
978
|
+
if self.xl is not None:
|
979
|
+
return self.xl.height.get()
|
980
|
+
else:
|
981
|
+
return 0
|
982
|
+
|
983
|
+
@property
|
984
|
+
def left(self):
|
985
|
+
return self.xl.properties().get(kw.left_position)
|
986
|
+
|
987
|
+
@property
|
988
|
+
def top(self):
|
989
|
+
return self.xl.properties().get(kw.top)
|
990
|
+
|
991
|
+
@property
|
992
|
+
def has_array(self):
|
993
|
+
if self.xl is not None:
|
994
|
+
return self.xl.has_array.get()
|
995
|
+
|
996
|
+
@property
|
997
|
+
def number_format(self):
|
998
|
+
if self.xl is not None:
|
999
|
+
rv = self.xl.number_format.get()
|
1000
|
+
return None if rv == kw.missing_value else rv
|
1001
|
+
|
1002
|
+
@number_format.setter
|
1003
|
+
def number_format(self, value):
|
1004
|
+
if self.xl is not None:
|
1005
|
+
alerts_state = self.sheet.book.app.screen_updating
|
1006
|
+
self.sheet.book.app.screen_updating = False
|
1007
|
+
self.xl.number_format.set(value)
|
1008
|
+
self.sheet.book.app.screen_updating = alerts_state
|
1009
|
+
|
1010
|
+
def get_address(self, row_absolute, col_absolute, external):
|
1011
|
+
if self.xl is not None:
|
1012
|
+
return self.xl.get_address(
|
1013
|
+
row_absolute=row_absolute,
|
1014
|
+
column_absolute=col_absolute,
|
1015
|
+
external=external,
|
1016
|
+
)
|
1017
|
+
|
1018
|
+
@property
|
1019
|
+
def address(self):
|
1020
|
+
if self.xl is not None:
|
1021
|
+
return self.xl.get_address()
|
1022
|
+
else:
|
1023
|
+
row, col, nrows, ncols = self.coords
|
1024
|
+
return "$%s$%s{%sx%s}" % (col_name(col), row, nrows, ncols)
|
1025
|
+
|
1026
|
+
@property
|
1027
|
+
def current_region(self):
|
1028
|
+
return Range(self.sheet, self.xl.current_region.get_address())
|
1029
|
+
|
1030
|
+
def autofit(self, axis=None):
|
1031
|
+
if self.xl is not None:
|
1032
|
+
address = self.address
|
1033
|
+
alerts_state = self.sheet.book.app.screen_updating
|
1034
|
+
self.sheet.book.app.screen_updating = False
|
1035
|
+
if axis == "rows" or axis == "r":
|
1036
|
+
self.sheet.xl.rows[address].autofit()
|
1037
|
+
elif axis == "columns" or axis == "c":
|
1038
|
+
self.sheet.xl.columns[address].autofit()
|
1039
|
+
elif axis is None:
|
1040
|
+
self.sheet.xl.rows[address].autofit()
|
1041
|
+
self.sheet.xl.columns[address].autofit()
|
1042
|
+
self.sheet.book.app.screen_updating = alerts_state
|
1043
|
+
|
1044
|
+
def insert(self, shift=None, copy_origin=None):
|
1045
|
+
# copy_origin is not supported on mac
|
1046
|
+
shifts = {"down": kw.shift_down, "right": kw.shift_to_right, None: None}
|
1047
|
+
self.xl.insert_into_range(shift=shifts[shift])
|
1048
|
+
|
1049
|
+
def delete(self, shift=None):
|
1050
|
+
shifts = {"up": kw.shift_up, "left": kw.shift_to_left, None: None}
|
1051
|
+
self.xl.delete_range(shift=shifts[shift])
|
1052
|
+
|
1053
|
+
def copy(self, destination=None):
|
1054
|
+
self.xl.copy_range(destination=destination.api if destination else None)
|
1055
|
+
|
1056
|
+
def paste(self, paste=None, operation=None, skip_blanks=False, transpose=False):
|
1057
|
+
pastes = {
|
1058
|
+
# all_merging_conditional_formats unsupported on mac
|
1059
|
+
"all": kw.paste_all,
|
1060
|
+
"all_except_borders": kw.paste_all_except_borders,
|
1061
|
+
"all_using_source_theme": kw.paste_all_using_source_theme,
|
1062
|
+
"column_widths": kw.paste_column_widths,
|
1063
|
+
"comments": kw.paste_comments,
|
1064
|
+
"formats": kw.paste_formats,
|
1065
|
+
"formulas": kw.paste_formulas,
|
1066
|
+
"formulas_and_number_formats": kw.paste_formulas_and_number_formats,
|
1067
|
+
"validation": kw.paste_validation,
|
1068
|
+
"values": kw.paste_values,
|
1069
|
+
"values_and_number_formats": kw.paste_values_and_number_formats,
|
1070
|
+
None: None,
|
1071
|
+
}
|
1072
|
+
|
1073
|
+
operations = {
|
1074
|
+
"add": kw.paste_special_operation_add,
|
1075
|
+
"divide": kw.paste_special_operation_divide,
|
1076
|
+
"multiply": kw.paste_special_operation_multiply,
|
1077
|
+
"subtract": kw.paste_special_operation_subtract,
|
1078
|
+
None: None,
|
1079
|
+
}
|
1080
|
+
|
1081
|
+
self.xl.paste_special(
|
1082
|
+
what=pastes[paste],
|
1083
|
+
operation=operations[operation],
|
1084
|
+
skip_blanks=skip_blanks,
|
1085
|
+
transpose=transpose,
|
1086
|
+
)
|
1087
|
+
|
1088
|
+
@property
|
1089
|
+
def hyperlink(self):
|
1090
|
+
try:
|
1091
|
+
return self.xl.hyperlinks[1].address.get()
|
1092
|
+
except CommandError:
|
1093
|
+
raise Exception("The cell doesn't seem to contain a hyperlink!")
|
1094
|
+
|
1095
|
+
def add_hyperlink(self, address, text_to_display=None, screen_tip=None):
|
1096
|
+
if self.xl is not None:
|
1097
|
+
self.xl.make(
|
1098
|
+
at=self.xl,
|
1099
|
+
new=kw.hyperlink,
|
1100
|
+
with_properties={
|
1101
|
+
kw.address: address,
|
1102
|
+
kw.text_to_display: text_to_display,
|
1103
|
+
kw.screen_tip: screen_tip,
|
1104
|
+
},
|
1105
|
+
)
|
1106
|
+
|
1107
|
+
@property
|
1108
|
+
def color(self):
|
1109
|
+
if (
|
1110
|
+
not self.xl
|
1111
|
+
or self.xl.interior_object.color_index.get() == kw.color_index_none
|
1112
|
+
):
|
1113
|
+
return None
|
1114
|
+
else:
|
1115
|
+
return tuple(self.xl.interior_object.color.get())
|
1116
|
+
|
1117
|
+
@color.setter
|
1118
|
+
def color(self, color_or_rgb):
|
1119
|
+
if isinstance(color_or_rgb, str):
|
1120
|
+
color_or_rgb = utils.hex_to_rgb(color_or_rgb)
|
1121
|
+
if self.xl is not None:
|
1122
|
+
if color_or_rgb is None:
|
1123
|
+
self.xl.interior_object.color_index.set(ColorIndex.xlColorIndexNone)
|
1124
|
+
elif isinstance(color_or_rgb, int):
|
1125
|
+
self.xl.interior_object.color.set(int_to_rgb(color_or_rgb))
|
1126
|
+
else:
|
1127
|
+
self.xl.interior_object.color.set(color_or_rgb)
|
1128
|
+
|
1129
|
+
@property
|
1130
|
+
def name(self):
|
1131
|
+
if not self.xl:
|
1132
|
+
return None
|
1133
|
+
xl = self.xl.named_item
|
1134
|
+
if xl.get() == kw.missing_value:
|
1135
|
+
return None
|
1136
|
+
else:
|
1137
|
+
return Name(self.sheet.book, xl=xl)
|
1138
|
+
|
1139
|
+
@name.setter
|
1140
|
+
def name(self, value):
|
1141
|
+
if self.xl is not None:
|
1142
|
+
self.xl.name.set(value)
|
1143
|
+
|
1144
|
+
def __call__(self, arg1, arg2=None):
|
1145
|
+
if arg2 is None:
|
1146
|
+
col = (arg1 - 1) % self.shape[1]
|
1147
|
+
row = int((arg1 - 1 - col) / self.shape[1])
|
1148
|
+
return self(1 + row, 1 + col)
|
1149
|
+
else:
|
1150
|
+
return Range(
|
1151
|
+
self.sheet,
|
1152
|
+
self.sheet.xl.rows[self.row + arg1 - 1]
|
1153
|
+
.columns[self.column + arg2 - 1]
|
1154
|
+
.get_address(),
|
1155
|
+
)
|
1156
|
+
|
1157
|
+
@property
|
1158
|
+
def rows(self):
|
1159
|
+
row = self.row
|
1160
|
+
col1 = self.column
|
1161
|
+
col2 = col1 + self.shape[1] - 1
|
1162
|
+
return [
|
1163
|
+
self.sheet.range((row + i, col1), (row + i, col2))
|
1164
|
+
for i in range(self.shape[0])
|
1165
|
+
]
|
1166
|
+
|
1167
|
+
@property
|
1168
|
+
def columns(self):
|
1169
|
+
col = self.column
|
1170
|
+
row1 = self.row
|
1171
|
+
row2 = row1 + self.shape[0] - 1
|
1172
|
+
sht = self.sheet
|
1173
|
+
return [
|
1174
|
+
sht.range((row1, col + i), (row2, col + i)) for i in range(self.shape[1])
|
1175
|
+
]
|
1176
|
+
|
1177
|
+
def select(self):
|
1178
|
+
if self.xl is not None:
|
1179
|
+
return self.xl.select()
|
1180
|
+
|
1181
|
+
@property
|
1182
|
+
def merge_area(self):
|
1183
|
+
return Range(self.sheet, self.xl.merge_area.get_address())
|
1184
|
+
|
1185
|
+
@property
|
1186
|
+
def merge_cells(self):
|
1187
|
+
return self.xl.merge_cells.get()
|
1188
|
+
|
1189
|
+
def merge(self, across):
|
1190
|
+
self.xl.merge(across=across)
|
1191
|
+
|
1192
|
+
def unmerge(self):
|
1193
|
+
self.xl.unmerge()
|
1194
|
+
|
1195
|
+
@property
|
1196
|
+
def table(self):
|
1197
|
+
if self.xl.list_object.name.get() == kw.missing_value:
|
1198
|
+
return None
|
1199
|
+
else:
|
1200
|
+
return Table(self.sheet, self.xl.list_object.name.get())
|
1201
|
+
|
1202
|
+
@property
|
1203
|
+
def characters(self):
|
1204
|
+
# This is broken with AppleScript/Excel 2016
|
1205
|
+
return Characters(parent=self, xl=self.xl.characters)
|
1206
|
+
|
1207
|
+
@property
|
1208
|
+
def wrap_text(self):
|
1209
|
+
return self.xl.wrap_text.get()
|
1210
|
+
|
1211
|
+
@wrap_text.setter
|
1212
|
+
def wrap_text(self, value):
|
1213
|
+
self.xl.wrap_text.set(value)
|
1214
|
+
|
1215
|
+
@property
|
1216
|
+
def note(self):
|
1217
|
+
try:
|
1218
|
+
# No easy way to check whether there's a comment like on Windows
|
1219
|
+
return (
|
1220
|
+
Note(parent=self, xl=self.xl.Excel_comment)
|
1221
|
+
if self.xl.Excel_comment.Excel_comment_text()
|
1222
|
+
else None
|
1223
|
+
)
|
1224
|
+
except appscript.reference.CommandError:
|
1225
|
+
return None
|
1226
|
+
|
1227
|
+
def copy_picture(self, appearance, format):
|
1228
|
+
_appearance = {"screen": kw.screen, "printer": kw.printer}
|
1229
|
+
_format = {"picture": kw.picture, "bitmap": kw.bitmap}
|
1230
|
+
self.xl.copy_picture(appearance=_appearance[appearance], format=_format[format])
|
1231
|
+
|
1232
|
+
def to_png(self, path):
|
1233
|
+
self.copy_picture(appearance="screen", format="bitmap")
|
1234
|
+
im = ImageGrab.grabclipboard()
|
1235
|
+
im.save(path)
|
1236
|
+
|
1237
|
+
def to_pdf(self, path, quality=None):
|
1238
|
+
raise xlwings.XlwingsError("Range.to_pdf() isn't supported on macOS.")
|
1239
|
+
|
1240
|
+
def autofill(self, destination, type_):
|
1241
|
+
types = {
|
1242
|
+
"fill_copy": kw.fill_copy,
|
1243
|
+
"fill_days": kw.fill_days,
|
1244
|
+
"fill_default": kw.fill_default,
|
1245
|
+
"fill_formats": kw.fill_formats,
|
1246
|
+
"fill_months": kw.fill_months,
|
1247
|
+
"fill_series": kw.fill_series,
|
1248
|
+
"fill_values": kw.fill_values,
|
1249
|
+
"fill_weekdays": kw.fill_weekdays,
|
1250
|
+
"fill_years": kw.fill_years,
|
1251
|
+
"growth_trend": kw.growth_trend,
|
1252
|
+
"linear_trend": kw.linear_trend,
|
1253
|
+
"flash_fill": kw.flashfill,
|
1254
|
+
}
|
1255
|
+
self.xl.autofill(destination=destination.api, type=types[type_])
|
1256
|
+
|
1257
|
+
|
1258
|
+
class Shape(base_classes.Shape):
|
1259
|
+
def __init__(self, parent, key):
|
1260
|
+
self._parent = parent
|
1261
|
+
self.xl = parent.xl.shapes[key]
|
1262
|
+
|
1263
|
+
@property
|
1264
|
+
def parent(self):
|
1265
|
+
return self._parent
|
1266
|
+
|
1267
|
+
@property
|
1268
|
+
def api(self):
|
1269
|
+
return self.xl
|
1270
|
+
|
1271
|
+
@property
|
1272
|
+
def name(self):
|
1273
|
+
return self.xl.name.get()
|
1274
|
+
|
1275
|
+
@name.setter
|
1276
|
+
def name(self, value):
|
1277
|
+
self.xl.name.set(value)
|
1278
|
+
|
1279
|
+
@property
|
1280
|
+
def type(self):
|
1281
|
+
return shape_types_k2s[self.xl.shape_type.get()]
|
1282
|
+
|
1283
|
+
@property
|
1284
|
+
def left(self):
|
1285
|
+
return self.xl.left_position.get()
|
1286
|
+
|
1287
|
+
@left.setter
|
1288
|
+
def left(self, value):
|
1289
|
+
self.xl.left_position.set(value)
|
1290
|
+
|
1291
|
+
@property
|
1292
|
+
def top(self):
|
1293
|
+
return self.xl.top.get()
|
1294
|
+
|
1295
|
+
@top.setter
|
1296
|
+
def top(self, value):
|
1297
|
+
self.xl.top.set(value)
|
1298
|
+
|
1299
|
+
@property
|
1300
|
+
def width(self):
|
1301
|
+
return self.xl.width.get()
|
1302
|
+
|
1303
|
+
@width.setter
|
1304
|
+
def width(self, value):
|
1305
|
+
self.xl.width.set(value)
|
1306
|
+
|
1307
|
+
@property
|
1308
|
+
def height(self):
|
1309
|
+
return self.xl.height.get()
|
1310
|
+
|
1311
|
+
@height.setter
|
1312
|
+
def height(self, value):
|
1313
|
+
self.xl.height.set(value)
|
1314
|
+
|
1315
|
+
def delete(self):
|
1316
|
+
self.xl.delete()
|
1317
|
+
|
1318
|
+
@property
|
1319
|
+
def index(self):
|
1320
|
+
return self.xl.entry_index.get()
|
1321
|
+
|
1322
|
+
def activate(self):
|
1323
|
+
# self.xl.activate_object() # doesn't work?
|
1324
|
+
self.xl.select()
|
1325
|
+
|
1326
|
+
def scale_height(self, factor, relative_to_original_size, scale):
|
1327
|
+
self.xl.scale_height(
|
1328
|
+
scale=scaling[scale],
|
1329
|
+
relative_to_original_size=relative_to_original_size,
|
1330
|
+
factor=factor,
|
1331
|
+
)
|
1332
|
+
|
1333
|
+
def scale_width(self, factor, relative_to_original_size, scale):
|
1334
|
+
self.xl.scale_width(
|
1335
|
+
scale=scaling[scale],
|
1336
|
+
relative_to_original_size=relative_to_original_size,
|
1337
|
+
factor=factor,
|
1338
|
+
)
|
1339
|
+
|
1340
|
+
@property
|
1341
|
+
def text(self):
|
1342
|
+
if self.xl.shape_text_frame.has_text.get():
|
1343
|
+
return self.xl.shape_text_frame.text_range.content.get()
|
1344
|
+
|
1345
|
+
@text.setter
|
1346
|
+
def text(self, value):
|
1347
|
+
self.xl.shape_text_frame.text_range.content.set(value)
|
1348
|
+
|
1349
|
+
@property
|
1350
|
+
def font(self):
|
1351
|
+
return Font(self, self.xl.shape_text_frame.text_range.font)
|
1352
|
+
|
1353
|
+
@property
|
1354
|
+
def characters(self):
|
1355
|
+
raise AttributeError("Characters isn't supported on macOS with shapes.")
|
1356
|
+
|
1357
|
+
|
1358
|
+
class Font(base_classes.Font):
|
1359
|
+
def __init__(self, parent, xl):
|
1360
|
+
# xl can be font or font_object
|
1361
|
+
self.parent = parent
|
1362
|
+
self.xl = xl
|
1363
|
+
|
1364
|
+
@property
|
1365
|
+
def api(self):
|
1366
|
+
return self.xl
|
1367
|
+
|
1368
|
+
@property
|
1369
|
+
def bold(self):
|
1370
|
+
return self.xl.bold.get()
|
1371
|
+
|
1372
|
+
@bold.setter
|
1373
|
+
def bold(self, value):
|
1374
|
+
self.xl.bold.set(value)
|
1375
|
+
|
1376
|
+
@property
|
1377
|
+
def italic(self):
|
1378
|
+
return self.xl.italic.get()
|
1379
|
+
|
1380
|
+
@italic.setter
|
1381
|
+
def italic(self, value):
|
1382
|
+
self.xl.italic.set(value)
|
1383
|
+
|
1384
|
+
@property
|
1385
|
+
def size(self):
|
1386
|
+
return self.xl.font_size.get()
|
1387
|
+
|
1388
|
+
@size.setter
|
1389
|
+
def size(self, value):
|
1390
|
+
self.xl.font_size.set(value)
|
1391
|
+
|
1392
|
+
@property
|
1393
|
+
def color(self):
|
1394
|
+
if isinstance(self.parent, Range):
|
1395
|
+
return tuple(self.xl.color.get())
|
1396
|
+
elif isinstance(self.parent, Shape):
|
1397
|
+
return tuple(self.xl.font_color.get())
|
1398
|
+
|
1399
|
+
@color.setter
|
1400
|
+
def color(self, color_or_rgb):
|
1401
|
+
if self.xl is not None:
|
1402
|
+
if isinstance(self.parent, (Range, Characters)):
|
1403
|
+
obj = self.xl.color
|
1404
|
+
elif isinstance(self.parent, Shape):
|
1405
|
+
obj = self.xl.font_color
|
1406
|
+
|
1407
|
+
if isinstance(color_or_rgb, int):
|
1408
|
+
obj.set(int_to_rgb(color_or_rgb))
|
1409
|
+
else:
|
1410
|
+
obj.set(color_or_rgb)
|
1411
|
+
|
1412
|
+
@property
|
1413
|
+
def name(self):
|
1414
|
+
if isinstance(self.parent, Range):
|
1415
|
+
return self.xl.name.get()
|
1416
|
+
elif isinstance(self.parent, Shape):
|
1417
|
+
return self.xl.font_name.get()
|
1418
|
+
|
1419
|
+
@name.setter
|
1420
|
+
def name(self, value):
|
1421
|
+
if isinstance(self.parent, Range):
|
1422
|
+
self.xl.name.set(value)
|
1423
|
+
elif isinstance(self.parent, Shape):
|
1424
|
+
self.xl.font_name.set(value)
|
1425
|
+
|
1426
|
+
|
1427
|
+
class Characters(base_classes.Characters):
|
1428
|
+
def __init__(self, parent, xl):
|
1429
|
+
self.parent = parent
|
1430
|
+
self.xl = xl
|
1431
|
+
|
1432
|
+
@property
|
1433
|
+
def api(self):
|
1434
|
+
return self.xl
|
1435
|
+
|
1436
|
+
@property
|
1437
|
+
def text(self):
|
1438
|
+
return self.xl.content.get()
|
1439
|
+
|
1440
|
+
@property
|
1441
|
+
def font(self):
|
1442
|
+
return Font(self, self.xl.font_object)
|
1443
|
+
|
1444
|
+
def __getitem__(self, item):
|
1445
|
+
# TODO: This is broken with AppleScript and Excel 2016:
|
1446
|
+
# set bold of font object of (characters 5 thru 9 of range "A1") to true
|
1447
|
+
# https://answers.microsoft.com/en-us/msoffice/forum/
|
1448
|
+
# msoffice_excel-mso_mac-msoversion_other/applescript-and-excel-problem/
|
1449
|
+
# 6e5a50b1-6209-4fbf-91f4-6d6674f1e488
|
1450
|
+
if isinstance(item, slice):
|
1451
|
+
return Characters(
|
1452
|
+
parent=self.parent,
|
1453
|
+
xl=self.xl[
|
1454
|
+
item.start + 1
|
1455
|
+
if item.start
|
1456
|
+
else None : item.stop
|
1457
|
+
if item.stop
|
1458
|
+
else len(self.text)
|
1459
|
+
],
|
1460
|
+
)
|
1461
|
+
else:
|
1462
|
+
return Characters(parent=self.parent, xl=self.xl[item + 1 : item + 1])
|
1463
|
+
|
1464
|
+
|
1465
|
+
class PageSetup(base_classes.PageSetup):
|
1466
|
+
def __init__(self, parent, xl):
|
1467
|
+
self.parent = parent
|
1468
|
+
self.xl = xl
|
1469
|
+
|
1470
|
+
@property
|
1471
|
+
def api(self):
|
1472
|
+
return self.xl
|
1473
|
+
|
1474
|
+
@property
|
1475
|
+
def print_area(self):
|
1476
|
+
value = self.xl.print_area.get()
|
1477
|
+
if value == kw.missing_value:
|
1478
|
+
return None
|
1479
|
+
else:
|
1480
|
+
return self.xl.print_area.get()
|
1481
|
+
|
1482
|
+
@print_area.setter
|
1483
|
+
def print_area(self, value):
|
1484
|
+
self.xl.print_area.set("" if value is None else value)
|
1485
|
+
|
1486
|
+
|
1487
|
+
class Note(base_classes.Note):
|
1488
|
+
def __init__(self, parent, xl):
|
1489
|
+
self.parent = parent
|
1490
|
+
self.xl = xl
|
1491
|
+
|
1492
|
+
def api(self):
|
1493
|
+
return self.xl
|
1494
|
+
|
1495
|
+
@property
|
1496
|
+
def text(self):
|
1497
|
+
return self.xl.Excel_comment_text()
|
1498
|
+
|
1499
|
+
@text.setter
|
1500
|
+
def text(self, value):
|
1501
|
+
self.xl.Excel_comment_text(text=value)
|
1502
|
+
|
1503
|
+
def delete(self):
|
1504
|
+
self.parent.xl.clear_Excel_comments()
|
1505
|
+
|
1506
|
+
|
1507
|
+
class Collection(base_classes.Collection):
|
1508
|
+
def __init__(self, parent):
|
1509
|
+
self._parent = parent
|
1510
|
+
self.xl = getattr(self.parent.xl, self._attr)
|
1511
|
+
|
1512
|
+
@property
|
1513
|
+
def parent(self):
|
1514
|
+
return self._parent
|
1515
|
+
|
1516
|
+
@property
|
1517
|
+
def api(self):
|
1518
|
+
return self.xl
|
1519
|
+
|
1520
|
+
def __call__(self, key):
|
1521
|
+
if not self.xl[key].exists():
|
1522
|
+
raise KeyError(key)
|
1523
|
+
return self._wrap(self.parent, key)
|
1524
|
+
|
1525
|
+
def __len__(self):
|
1526
|
+
return self.parent.xl.count(each=self._kw)
|
1527
|
+
|
1528
|
+
def __iter__(self):
|
1529
|
+
for i in range(len(self)):
|
1530
|
+
yield self(i + 1)
|
1531
|
+
|
1532
|
+
def __contains__(self, key):
|
1533
|
+
return self.xl[key].exists()
|
1534
|
+
|
1535
|
+
|
1536
|
+
class Table(base_classes.Table):
|
1537
|
+
def __init__(self, parent, key):
|
1538
|
+
self._parent = parent
|
1539
|
+
self.xl = parent.xl.list_objects[key]
|
1540
|
+
|
1541
|
+
@property
|
1542
|
+
def parent(self):
|
1543
|
+
return self._parent
|
1544
|
+
|
1545
|
+
@property
|
1546
|
+
def api(self):
|
1547
|
+
return self.xl
|
1548
|
+
|
1549
|
+
@property
|
1550
|
+
def name(self):
|
1551
|
+
return self.xl.name.get()
|
1552
|
+
|
1553
|
+
@name.setter
|
1554
|
+
def name(self, value):
|
1555
|
+
self.xl.name.set(value)
|
1556
|
+
self.xl = self.parent.xl.list_objects[value]
|
1557
|
+
|
1558
|
+
@property
|
1559
|
+
def data_body_range(self):
|
1560
|
+
if self.xl.cell_table.get() == kw.missing_value:
|
1561
|
+
return
|
1562
|
+
else:
|
1563
|
+
return Range(self.parent, self.xl.cell_table.get_address())
|
1564
|
+
|
1565
|
+
@property
|
1566
|
+
def display_name(self):
|
1567
|
+
return self.xl.display_name.get()
|
1568
|
+
|
1569
|
+
@display_name.setter
|
1570
|
+
def display_name(self, value):
|
1571
|
+
# Changing the display_name also changes the name
|
1572
|
+
self.xl.display_name.set(value)
|
1573
|
+
self.xl = self.parent.xl.list_objects[value]
|
1574
|
+
|
1575
|
+
@property
|
1576
|
+
def header_row_range(self):
|
1577
|
+
if self.xl.header_row.get() == kw.missing_value:
|
1578
|
+
return
|
1579
|
+
else:
|
1580
|
+
return Range(self.parent, self.xl.header_row.get_address())
|
1581
|
+
|
1582
|
+
@property
|
1583
|
+
def insert_row_range(self):
|
1584
|
+
if self.xl.insert_row.get() == kw.missing_value:
|
1585
|
+
return
|
1586
|
+
else:
|
1587
|
+
return Range(self.parent, self.xl.insert_row.get_address())
|
1588
|
+
|
1589
|
+
@property
|
1590
|
+
def range(self):
|
1591
|
+
return Range(self.parent, self.xl.range_object.get_address())
|
1592
|
+
|
1593
|
+
@property
|
1594
|
+
def show_autofilter(self):
|
1595
|
+
return self.xl.show_autofilter.get()
|
1596
|
+
|
1597
|
+
@show_autofilter.setter
|
1598
|
+
def show_autofilter(self, value):
|
1599
|
+
self.xl.show_autofilter.set(value)
|
1600
|
+
|
1601
|
+
@property
|
1602
|
+
def show_headers(self):
|
1603
|
+
return self.xl.show_headers.get()
|
1604
|
+
|
1605
|
+
@show_headers.setter
|
1606
|
+
def show_headers(self, value):
|
1607
|
+
self.xl.show_headers.set(value)
|
1608
|
+
|
1609
|
+
@property
|
1610
|
+
def show_table_style_column_stripes(self):
|
1611
|
+
return self.xl.show_table_style_column_stripes.get()
|
1612
|
+
|
1613
|
+
@show_table_style_column_stripes.setter
|
1614
|
+
def show_table_style_column_stripes(self, value):
|
1615
|
+
self.xl.show_table_style_column_stripes.set(value)
|
1616
|
+
|
1617
|
+
@property
|
1618
|
+
def show_table_style_first_column(self):
|
1619
|
+
return self.xl.show_table_style_first_column.get()
|
1620
|
+
|
1621
|
+
@show_table_style_first_column.setter
|
1622
|
+
def show_table_style_first_column(self, value):
|
1623
|
+
self.xl.show_table_style_first_column.set(value)
|
1624
|
+
|
1625
|
+
@property
|
1626
|
+
def show_table_style_last_column(self):
|
1627
|
+
return self.xl.show_table_style_last_column.get()
|
1628
|
+
|
1629
|
+
@show_table_style_last_column.setter
|
1630
|
+
def show_table_style_last_column(self, value):
|
1631
|
+
self.xl.show_table_style_last_column.set(value)
|
1632
|
+
|
1633
|
+
@property
|
1634
|
+
def show_table_style_row_stripes(self):
|
1635
|
+
return self.xl.show_table_style_row_stripes.get()
|
1636
|
+
|
1637
|
+
@show_table_style_row_stripes.setter
|
1638
|
+
def show_table_style_row_stripes(self, value):
|
1639
|
+
self.xl.show_table_style_row_stripes.set(value)
|
1640
|
+
|
1641
|
+
@property
|
1642
|
+
def show_totals(self):
|
1643
|
+
return self.xl.total.get()
|
1644
|
+
|
1645
|
+
@show_totals.setter
|
1646
|
+
def show_totals(self, value):
|
1647
|
+
self.xl.total.set(value)
|
1648
|
+
|
1649
|
+
@property
|
1650
|
+
def table_style(self):
|
1651
|
+
return self.xl.table_style.properties().get(kw.name)
|
1652
|
+
|
1653
|
+
@table_style.setter
|
1654
|
+
def table_style(self, value):
|
1655
|
+
self.xl.table_style.set(value)
|
1656
|
+
|
1657
|
+
@property
|
1658
|
+
def totals_row_range(self):
|
1659
|
+
if self.xl.total_row.get() == kw.missing_value:
|
1660
|
+
return
|
1661
|
+
else:
|
1662
|
+
return Range(self.parent, self.xl.total_row.get_address())
|
1663
|
+
|
1664
|
+
def resize(self, range):
|
1665
|
+
self.xl.resize(range=range.api)
|
1666
|
+
|
1667
|
+
|
1668
|
+
class Tables(Collection, base_classes.Tables):
|
1669
|
+
_attr = "list_objects"
|
1670
|
+
_kw = kw.list_object
|
1671
|
+
_wrap = Table
|
1672
|
+
|
1673
|
+
def add(
|
1674
|
+
self,
|
1675
|
+
source_type=None,
|
1676
|
+
source=None,
|
1677
|
+
link_source=None,
|
1678
|
+
has_headers=None,
|
1679
|
+
destination=None,
|
1680
|
+
table_style_name=None,
|
1681
|
+
name=None,
|
1682
|
+
):
|
1683
|
+
header_row = {
|
1684
|
+
True: kw.header_yes,
|
1685
|
+
False: kw.header_no,
|
1686
|
+
"guess": kw.header_guess,
|
1687
|
+
}
|
1688
|
+
sheet_index = self.parent.xl.entry_index.get()
|
1689
|
+
table = Table(
|
1690
|
+
self.parent,
|
1691
|
+
self.parent.xl.make(
|
1692
|
+
at=self.parent.book.xl.sheets[sheet_index],
|
1693
|
+
new=kw.list_object,
|
1694
|
+
with_properties={
|
1695
|
+
kw.source_type: kw.src_range,
|
1696
|
+
kw.range_object: source.api,
|
1697
|
+
kw.header_row: header_row[has_headers],
|
1698
|
+
kw.table_style: table_style_name,
|
1699
|
+
},
|
1700
|
+
).name.get(),
|
1701
|
+
)
|
1702
|
+
if name is not None:
|
1703
|
+
table.name = name
|
1704
|
+
return table
|
1705
|
+
|
1706
|
+
|
1707
|
+
class Chart(base_classes.Chart):
|
1708
|
+
def __init__(self, parent, key):
|
1709
|
+
self._parent = parent
|
1710
|
+
if isinstance(parent, Sheet):
|
1711
|
+
self.xl_obj = parent.xl.chart_objects[key]
|
1712
|
+
self.xl = self.xl_obj.chart
|
1713
|
+
else:
|
1714
|
+
self.xl_obj = None
|
1715
|
+
self.xl = self.charts[key]
|
1716
|
+
|
1717
|
+
@property
|
1718
|
+
def parent(self):
|
1719
|
+
return self._parent
|
1720
|
+
|
1721
|
+
@property
|
1722
|
+
def api(self):
|
1723
|
+
return self.xl_obj, self.xl
|
1724
|
+
|
1725
|
+
def set_source_data(self, rng):
|
1726
|
+
self.xl.set_source_data(source=rng.xl)
|
1727
|
+
|
1728
|
+
@property
|
1729
|
+
def name(self):
|
1730
|
+
if self.xl_obj is not None:
|
1731
|
+
return self.xl_obj.name.get()
|
1732
|
+
else:
|
1733
|
+
return self.xl.name.get()
|
1734
|
+
|
1735
|
+
@name.setter
|
1736
|
+
def name(self, value):
|
1737
|
+
if self.xl_obj is not None:
|
1738
|
+
self.xl_obj.name.set(value)
|
1739
|
+
else:
|
1740
|
+
self.xl.name.get(value)
|
1741
|
+
|
1742
|
+
@property
|
1743
|
+
def chart_type(self):
|
1744
|
+
return chart_types_k2s[self.xl.chart_type.get()]
|
1745
|
+
|
1746
|
+
@chart_type.setter
|
1747
|
+
def chart_type(self, value):
|
1748
|
+
self.xl.chart_type.set(chart_types_s2k[value])
|
1749
|
+
|
1750
|
+
@property
|
1751
|
+
def left(self):
|
1752
|
+
if self.xl_obj is None:
|
1753
|
+
raise Exception("This chart is not embedded.")
|
1754
|
+
return self.xl_obj.left_position.get()
|
1755
|
+
|
1756
|
+
@left.setter
|
1757
|
+
def left(self, value):
|
1758
|
+
if self.xl_obj is None:
|
1759
|
+
raise Exception("This chart is not embedded.")
|
1760
|
+
self.xl_obj.left_position.set(value)
|
1761
|
+
|
1762
|
+
@property
|
1763
|
+
def top(self):
|
1764
|
+
if self.xl_obj is None:
|
1765
|
+
raise Exception("This chart is not embedded.")
|
1766
|
+
return self.xl_obj.top.get()
|
1767
|
+
|
1768
|
+
@top.setter
|
1769
|
+
def top(self, value):
|
1770
|
+
if self.xl_obj is None:
|
1771
|
+
raise Exception("This chart is not embedded.")
|
1772
|
+
self.xl_obj.top.set(value)
|
1773
|
+
|
1774
|
+
@property
|
1775
|
+
def width(self):
|
1776
|
+
if self.xl_obj is None:
|
1777
|
+
raise Exception("This chart is not embedded.")
|
1778
|
+
return self.xl_obj.width.get()
|
1779
|
+
|
1780
|
+
@width.setter
|
1781
|
+
def width(self, value):
|
1782
|
+
if self.xl_obj is None:
|
1783
|
+
raise Exception("This chart is not embedded.")
|
1784
|
+
self.xl_obj.width.set(value)
|
1785
|
+
|
1786
|
+
@property
|
1787
|
+
def height(self):
|
1788
|
+
if self.xl_obj is None:
|
1789
|
+
raise Exception("This chart is not embedded.")
|
1790
|
+
return self.xl_obj.height.get()
|
1791
|
+
|
1792
|
+
@height.setter
|
1793
|
+
def height(self, value):
|
1794
|
+
if self.xl_obj is None:
|
1795
|
+
raise Exception("This chart is not embedded.")
|
1796
|
+
self.xl_obj.height.set(value)
|
1797
|
+
|
1798
|
+
def delete(self):
|
1799
|
+
self.xl_obj.delete()
|
1800
|
+
|
1801
|
+
def to_png(self, path):
|
1802
|
+
raise xlwings.XlwingsError("Chart.to_png() isn't supported on macOS.")
|
1803
|
+
# Both versions should work, but seem to be broken with Excel 2016
|
1804
|
+
#
|
1805
|
+
# Version 1
|
1806
|
+
# import uuid
|
1807
|
+
# temp_path = posix_to_hfs_path(os.path.expanduser("~")
|
1808
|
+
# + f"/Library/Containers/com.microsoft.Excel/"
|
1809
|
+
# f"Data/{uuid.uuid4()}.png")
|
1810
|
+
# self.xl.save_as(filename=temp_path)
|
1811
|
+
# shutil.copy2(temp_path, path)
|
1812
|
+
# try:
|
1813
|
+
# os.unlink(temp_path)
|
1814
|
+
# except:
|
1815
|
+
# pass
|
1816
|
+
#
|
1817
|
+
# Version 2
|
1818
|
+
# self.xl_obj.save_as_picture(file_name=posix_to_hfs_path('...'),
|
1819
|
+
# picture_type=kw.save_as_PNG_file)
|
1820
|
+
|
1821
|
+
def to_pdf(self, path, quality=None):
|
1822
|
+
raise xlwings.XlwingsError("Chart.to_pdf() isn't supported on macOS.")
|
1823
|
+
|
1824
|
+
|
1825
|
+
class Charts(Collection, base_classes.Charts):
|
1826
|
+
_attr = "chart_objects"
|
1827
|
+
_kw = kw.chart_object
|
1828
|
+
_wrap = Chart
|
1829
|
+
|
1830
|
+
def add(self, left, top, width, height):
|
1831
|
+
sheet_index = self.parent.xl.entry_index.get()
|
1832
|
+
return Chart(
|
1833
|
+
self.parent,
|
1834
|
+
self.parent.xl.make(
|
1835
|
+
at=self.parent.book.xl.sheets[sheet_index],
|
1836
|
+
new=kw.chart_object,
|
1837
|
+
with_properties={
|
1838
|
+
kw.width: width,
|
1839
|
+
kw.top: top,
|
1840
|
+
kw.left_position: left,
|
1841
|
+
kw.height: height,
|
1842
|
+
},
|
1843
|
+
).name.get(),
|
1844
|
+
)
|
1845
|
+
|
1846
|
+
|
1847
|
+
class Picture(base_classes.Picture):
|
1848
|
+
def __init__(self, parent, key):
|
1849
|
+
self._parent = parent
|
1850
|
+
self.xl = parent.xl.pictures[key]
|
1851
|
+
|
1852
|
+
@property
|
1853
|
+
def parent(self):
|
1854
|
+
return self._parent
|
1855
|
+
|
1856
|
+
@property
|
1857
|
+
def api(self):
|
1858
|
+
return self.xl
|
1859
|
+
|
1860
|
+
@property
|
1861
|
+
def name(self):
|
1862
|
+
return self.xl.name.get()
|
1863
|
+
|
1864
|
+
@name.setter
|
1865
|
+
def name(self, value):
|
1866
|
+
self.xl.name.set(value)
|
1867
|
+
|
1868
|
+
@property
|
1869
|
+
def left(self):
|
1870
|
+
return self.xl.left_position.get()
|
1871
|
+
|
1872
|
+
@left.setter
|
1873
|
+
def left(self, value):
|
1874
|
+
self.xl.left_position.set(value)
|
1875
|
+
|
1876
|
+
@property
|
1877
|
+
def top(self):
|
1878
|
+
return self.xl.top.get()
|
1879
|
+
|
1880
|
+
@top.setter
|
1881
|
+
def top(self, value):
|
1882
|
+
self.xl.top.set(value)
|
1883
|
+
|
1884
|
+
@property
|
1885
|
+
def width(self):
|
1886
|
+
return self.xl.width.get()
|
1887
|
+
|
1888
|
+
@width.setter
|
1889
|
+
def width(self, value):
|
1890
|
+
self.xl.width.set(value)
|
1891
|
+
|
1892
|
+
@property
|
1893
|
+
def height(self):
|
1894
|
+
return self.xl.height.get()
|
1895
|
+
|
1896
|
+
@height.setter
|
1897
|
+
def height(self, value):
|
1898
|
+
self.xl.height.set(value)
|
1899
|
+
|
1900
|
+
def delete(self):
|
1901
|
+
self.xl.delete()
|
1902
|
+
|
1903
|
+
@property
|
1904
|
+
def lock_aspect_ratio(self):
|
1905
|
+
return self.xl.lock_aspect_ratio.get()
|
1906
|
+
|
1907
|
+
@lock_aspect_ratio.setter
|
1908
|
+
def lock_aspect_ratio(self, value):
|
1909
|
+
self.xl.lock_aspect_ratio.set(value)
|
1910
|
+
|
1911
|
+
def update(self, filename):
|
1912
|
+
return utils.excel_update_picture(self, filename)
|
1913
|
+
|
1914
|
+
|
1915
|
+
class Pictures(Collection, base_classes.Pictures):
|
1916
|
+
_attr = "pictures"
|
1917
|
+
_kw = kw.picture
|
1918
|
+
_wrap = Picture
|
1919
|
+
|
1920
|
+
def add(
|
1921
|
+
self,
|
1922
|
+
filename,
|
1923
|
+
link_to_file,
|
1924
|
+
save_with_document,
|
1925
|
+
left,
|
1926
|
+
top,
|
1927
|
+
width,
|
1928
|
+
height,
|
1929
|
+
anchor,
|
1930
|
+
):
|
1931
|
+
if anchor:
|
1932
|
+
top, left = anchor.top, anchor.left
|
1933
|
+
|
1934
|
+
version = VersionNumber(self.parent.book.app.version)
|
1935
|
+
|
1936
|
+
if not link_to_file and version >= 15:
|
1937
|
+
# Office 2016 for Mac is sandboxed. This path seems to work without the
|
1938
|
+
# need of granting access explicitly.
|
1939
|
+
xlwings_picture = (
|
1940
|
+
os.path.expanduser("~")
|
1941
|
+
+ "/Library/Containers/com.microsoft.Excel/Data/xlwings_picture.png"
|
1942
|
+
)
|
1943
|
+
shutil.copy2(filename, xlwings_picture)
|
1944
|
+
filename = xlwings_picture
|
1945
|
+
|
1946
|
+
sheet_index = self.parent.xl.entry_index.get()
|
1947
|
+
picture = Picture(
|
1948
|
+
self.parent,
|
1949
|
+
self.parent.xl.make(
|
1950
|
+
at=self.parent.book.xl.sheets[sheet_index],
|
1951
|
+
new=kw.picture,
|
1952
|
+
with_properties={
|
1953
|
+
kw.file_name: posix_to_hfs_path(filename),
|
1954
|
+
kw.link_to_file: link_to_file,
|
1955
|
+
kw.save_with_document: save_with_document,
|
1956
|
+
kw.width: width,
|
1957
|
+
kw.height: height,
|
1958
|
+
# Top and left: see below
|
1959
|
+
kw.top: 0,
|
1960
|
+
kw.left_position: 0,
|
1961
|
+
},
|
1962
|
+
).name.get(),
|
1963
|
+
)
|
1964
|
+
|
1965
|
+
# Top and left cause an issue in the make command above
|
1966
|
+
# if they are not set to 0 when width & height are -1
|
1967
|
+
picture.top = top if top else 0
|
1968
|
+
picture.left = left if left else 0
|
1969
|
+
|
1970
|
+
if not link_to_file and version >= 15:
|
1971
|
+
os.remove(filename)
|
1972
|
+
|
1973
|
+
return picture
|
1974
|
+
|
1975
|
+
|
1976
|
+
class Names(base_classes.Names):
|
1977
|
+
def __init__(self, parent, xl):
|
1978
|
+
self.parent = parent
|
1979
|
+
self.xl = xl
|
1980
|
+
|
1981
|
+
def __call__(self, name_or_index):
|
1982
|
+
return Name(self.parent, xl=self.xl[name_or_index])
|
1983
|
+
|
1984
|
+
def contains(self, name_or_index):
|
1985
|
+
try:
|
1986
|
+
self.xl[name_or_index].get()
|
1987
|
+
except appscript.reference.CommandError:
|
1988
|
+
# TODO: make more specific
|
1989
|
+
return False
|
1990
|
+
return True
|
1991
|
+
|
1992
|
+
def __len__(self):
|
1993
|
+
named_items = self.xl.get()
|
1994
|
+
if named_items == kw.missing_value:
|
1995
|
+
return 0
|
1996
|
+
else:
|
1997
|
+
return len(named_items)
|
1998
|
+
|
1999
|
+
def add(self, name, refers_to):
|
2000
|
+
return Name(
|
2001
|
+
self.parent,
|
2002
|
+
self.parent.xl.make(
|
2003
|
+
at=self.parent.xl,
|
2004
|
+
new=kw.named_item,
|
2005
|
+
with_properties={kw.references: refers_to, kw.name: name},
|
2006
|
+
),
|
2007
|
+
)
|
2008
|
+
|
2009
|
+
|
2010
|
+
class Name(base_classes.Name):
|
2011
|
+
def __init__(self, parent, xl):
|
2012
|
+
self.parent = parent
|
2013
|
+
self.xl = xl
|
2014
|
+
|
2015
|
+
def delete(self):
|
2016
|
+
self.xl.delete()
|
2017
|
+
|
2018
|
+
@property
|
2019
|
+
def name(self):
|
2020
|
+
return self.xl.name.get()
|
2021
|
+
|
2022
|
+
@name.setter
|
2023
|
+
def name(self, value):
|
2024
|
+
self.xl.name.set(value)
|
2025
|
+
|
2026
|
+
@property
|
2027
|
+
def refers_to(self):
|
2028
|
+
return self.xl.properties().get(kw.references)
|
2029
|
+
|
2030
|
+
@refers_to.setter
|
2031
|
+
def refers_to(self, value):
|
2032
|
+
self.xl.properties(kw.references).set(value)
|
2033
|
+
|
2034
|
+
@property
|
2035
|
+
def refers_to_range(self):
|
2036
|
+
book = self.parent if isinstance(self.parent, Book) else self.parent.book
|
2037
|
+
external_address = self.xl.reference_range.get_address(external=True)
|
2038
|
+
match = re.search(r"\](.*?)'?!(.*)", external_address)
|
2039
|
+
return Range(Sheet(book, match.group(1)), match.group(2))
|
2040
|
+
|
2041
|
+
|
2042
|
+
class Shapes(Collection):
|
2043
|
+
_attr = "shapes"
|
2044
|
+
_kw = kw.shape
|
2045
|
+
_wrap = Shape
|
2046
|
+
|
2047
|
+
|
2048
|
+
@atexit.register
|
2049
|
+
def cleanup():
|
2050
|
+
"""
|
2051
|
+
Since AppleScript cannot access Excel while a Macro is running, we have to run the
|
2052
|
+
Python call in a background process which makes the call return immediately: we
|
2053
|
+
rely on the StatusBar to give the user feedback.
|
2054
|
+
This function is triggered when the interpreter exits and runs the CleanUp Macro in
|
2055
|
+
VBA to show any errors and to reset the StatusBar.
|
2056
|
+
"""
|
2057
|
+
if is_excel_running():
|
2058
|
+
# Prevents Excel from reopening
|
2059
|
+
# if it has been closed manually or never been opened
|
2060
|
+
for app in Apps():
|
2061
|
+
try:
|
2062
|
+
app.xl.run_VB_macro("CleanUp")
|
2063
|
+
except (CommandError, AttributeError, aem.aemsend.EventError):
|
2064
|
+
# Excel files initiated from Python don't have the xlwings VBA module
|
2065
|
+
pass
|
2066
|
+
|
2067
|
+
|
2068
|
+
def posix_to_hfs_path(posix_path):
|
2069
|
+
"""
|
2070
|
+
Turns a posix path (/Path/file.ext) into an HFS path (Macintosh HD:Path:file.ext)
|
2071
|
+
"""
|
2072
|
+
dir_name, file_name = os.path.split(posix_path)
|
2073
|
+
dir_name_hfs = mactypes.Alias(dir_name).hfspath
|
2074
|
+
return dir_name_hfs + ":" + file_name
|
2075
|
+
|
2076
|
+
|
2077
|
+
def hfs_to_posix_path(hfs_path):
|
2078
|
+
"""
|
2079
|
+
Turns an HFS path (Macintosh HD:Path:file.ext) into a posix path (/Path/file.ext)
|
2080
|
+
"""
|
2081
|
+
url = mactypes.convertpathtourl(hfs_path, 1) # kCFURLHFSPathStyle = 1
|
2082
|
+
return mactypes.converturltopath(url, 0) # kCFURLPOSIXPathStyle = 0
|
2083
|
+
|
2084
|
+
|
2085
|
+
def is_excel_running():
|
2086
|
+
for proc in psutil.process_iter():
|
2087
|
+
try:
|
2088
|
+
if proc.name() == "Microsoft Excel":
|
2089
|
+
return True
|
2090
|
+
except psutil.NoSuchProcess:
|
2091
|
+
pass
|
2092
|
+
return False
|
2093
|
+
|
2094
|
+
|
2095
|
+
# --- constants ---
|
2096
|
+
|
2097
|
+
chart_types_k2s = {
|
2098
|
+
kw.ThreeD_area: "3d_area",
|
2099
|
+
kw.ThreeD_area_stacked: "3d_area_stacked",
|
2100
|
+
kw.ThreeD_area_stacked_100: "3d_area_stacked_100",
|
2101
|
+
kw.ThreeD_bar_clustered: "3d_bar_clustered",
|
2102
|
+
kw.ThreeD_bar_stacked: "3d_bar_stacked",
|
2103
|
+
kw.ThreeD_bar_stacked_100: "3d_bar_stacked_100",
|
2104
|
+
kw.ThreeD_column: "3d_column",
|
2105
|
+
kw.ThreeD_column_clustered: "3d_column_clustered",
|
2106
|
+
kw.ThreeD_column_stacked: "3d_column_stacked",
|
2107
|
+
kw.ThreeD_column_stacked_100: "3d_column_stacked_100",
|
2108
|
+
kw.ThreeD_line: "3d_line",
|
2109
|
+
kw.ThreeD_pie: "3d_pie",
|
2110
|
+
kw.ThreeD_pie_exploded: "3d_pie_exploded",
|
2111
|
+
kw.area_chart: "area",
|
2112
|
+
kw.area_stacked: "area_stacked",
|
2113
|
+
kw.area_stacked_100: "area_stacked_100",
|
2114
|
+
kw.bar_clustered: "bar_clustered",
|
2115
|
+
kw.bar_of_pie: "bar_of_pie",
|
2116
|
+
kw.bar_stacked: "bar_stacked",
|
2117
|
+
kw.bar_stacked_100: "bar_stacked_100",
|
2118
|
+
kw.bubble: "bubble",
|
2119
|
+
kw.bubble_ThreeD_effect: "bubble_3d_effect",
|
2120
|
+
kw.column_clustered: "column_clustered",
|
2121
|
+
kw.column_stacked: "column_stacked",
|
2122
|
+
kw.column_stacked_100: "column_stacked_100",
|
2123
|
+
kw.combination_chart: "combination",
|
2124
|
+
kw.cone_bar_clustered: "cone_bar_clustered",
|
2125
|
+
kw.cone_bar_stacked: "cone_bar_stacked",
|
2126
|
+
kw.cone_bar_stacked_100: "cone_bar_stacked_100",
|
2127
|
+
kw.cone_col: "cone_col",
|
2128
|
+
kw.cone_column_clustered: "cone_col_clustered",
|
2129
|
+
kw.cone_column_stacked: "cone_col_stacked",
|
2130
|
+
kw.cone_column_stacked_100: "cone_col_stacked_100",
|
2131
|
+
kw.cylinder_bar_clustered: "cylinder_bar_clustered",
|
2132
|
+
kw.cylinder_bar_stacked: "cylinder_bar_stacked",
|
2133
|
+
kw.cylinder_bar_stacked_100: "cylinder_bar_stacked_100",
|
2134
|
+
kw.cylinder_column: "cylinder_col",
|
2135
|
+
kw.cylinder_column_clustered: "cylinder_col_clustered",
|
2136
|
+
kw.cylinder_column_stacked: "cylinder_col_stacked",
|
2137
|
+
kw.cylinder_column_stacked_100: "cylinder_col_stacked_100",
|
2138
|
+
kw.doughnut: "doughnut",
|
2139
|
+
kw.doughnut_exploded: "doughnut_exploded",
|
2140
|
+
kw.line_chart: "line",
|
2141
|
+
kw.line_markers: "line_markers",
|
2142
|
+
kw.line_markers_stacked: "line_markers_stacked",
|
2143
|
+
kw.line_markers_stacked_100: "line_markers_stacked_100",
|
2144
|
+
kw.line_stacked: "line_stacked",
|
2145
|
+
kw.line_stacked_100: "line_stacked_100",
|
2146
|
+
kw.pie_chart: "pie",
|
2147
|
+
kw.pie_exploded: "pie_exploded",
|
2148
|
+
kw.pie_of_pie: "pie_of_pie",
|
2149
|
+
kw.pyramid_bar_clustered: "pyramid_bar_clustered",
|
2150
|
+
kw.pyramid_bar_stacked: "pyramid_bar_stacked",
|
2151
|
+
kw.pyramid_bar_stacked_100: "pyramid_bar_stacked_100",
|
2152
|
+
kw.pyramid_column: "pyramid_col",
|
2153
|
+
kw.pyramid_column_clustered: "pyramid_col_clustered",
|
2154
|
+
kw.pyramid_column_stacked: "pyramid_col_stacked",
|
2155
|
+
kw.pyramid_column_stacked_100: "pyramid_col_stacked_100",
|
2156
|
+
kw.radar: "radar",
|
2157
|
+
kw.radar_filled: "radar_filled",
|
2158
|
+
kw.radar_markers: "radar_markers",
|
2159
|
+
kw.stock_HLC: "stock_hlc",
|
2160
|
+
kw.stock_OHLC: "stock_ohlc",
|
2161
|
+
kw.stock_VHLC: "stock_vhlc",
|
2162
|
+
kw.stock_VOHLC: "stock_vohlc",
|
2163
|
+
kw.surface: "surface",
|
2164
|
+
kw.surface_top_view: "surface_top_view",
|
2165
|
+
kw.surface_top_view_wireframe: "surface_top_view_wireframe",
|
2166
|
+
kw.surface_wireframe: "surface_wireframe",
|
2167
|
+
kw.xy_scatter_lines: "xy_scatter_lines",
|
2168
|
+
kw.xy_scatter_lines_no_markers: "xy_scatter_lines_no_markers",
|
2169
|
+
kw.xy_scatter_smooth: "xy_scatter_smooth",
|
2170
|
+
kw.xy_scatter_smooth_no_markers: "xy_scatter_smooth_no_markers",
|
2171
|
+
kw.xyscatter: "xy_scatter",
|
2172
|
+
}
|
2173
|
+
|
2174
|
+
chart_types_s2k = {v: k for k, v in chart_types_k2s.items()}
|
2175
|
+
|
2176
|
+
directions_s2k = {
|
2177
|
+
"d": kw.toward_the_bottom,
|
2178
|
+
"down": kw.toward_the_bottom,
|
2179
|
+
"l": kw.toward_the_left,
|
2180
|
+
"left": kw.toward_the_left,
|
2181
|
+
"r": kw.toward_the_right,
|
2182
|
+
"right": kw.toward_the_right,
|
2183
|
+
"u": kw.toward_the_top,
|
2184
|
+
"up": kw.toward_the_top,
|
2185
|
+
}
|
2186
|
+
|
2187
|
+
directions_k2s = {
|
2188
|
+
kw.toward_the_bottom: "down",
|
2189
|
+
kw.toward_the_left: "left",
|
2190
|
+
kw.toward_the_right: "right",
|
2191
|
+
kw.toward_the_top: "up",
|
2192
|
+
}
|
2193
|
+
|
2194
|
+
calculation_k2s = {
|
2195
|
+
kw.calculation_automatic: "automatic",
|
2196
|
+
kw.calculation_manual: "manual",
|
2197
|
+
kw.calculation_semiautomatic: "semiautomatic",
|
2198
|
+
}
|
2199
|
+
|
2200
|
+
calculation_s2k = {v: k for k, v in calculation_k2s.items()}
|
2201
|
+
|
2202
|
+
shape_types_k2s = {
|
2203
|
+
kw.shape_type_3d_model: "3d_model",
|
2204
|
+
kw.shape_type_auto: "auto_shape",
|
2205
|
+
kw.shape_type_callout: "callout",
|
2206
|
+
kw.shape_type_canvas: "canvas",
|
2207
|
+
kw.shape_type_chart: "chart",
|
2208
|
+
kw.shape_type_comment: "comment",
|
2209
|
+
kw.shape_type_content_application: "content_app",
|
2210
|
+
kw.shape_type_diagram: "diagram",
|
2211
|
+
kw.shape_type_free_form: "free_form",
|
2212
|
+
kw.shape_type_graphic: "graphic",
|
2213
|
+
kw.shape_type_group: "group",
|
2214
|
+
kw.shape_type_embedded_OLE_control: "embedded_ole_object",
|
2215
|
+
kw.shape_type_form_control: "form_control",
|
2216
|
+
kw.shape_type_line: "line",
|
2217
|
+
kw.shape_type_linked_3d_model: "linked_3d_model",
|
2218
|
+
kw.shape_type_linked_graphic: "linked_graphic",
|
2219
|
+
kw.shape_type_linked_OLE_object: "linked_ole_object",
|
2220
|
+
kw.shape_type_linked_picture: "linked_picture",
|
2221
|
+
kw.shape_type_OLE_control: "ole_control_object",
|
2222
|
+
kw.shape_type_picture: "picture",
|
2223
|
+
kw.shape_type_place_holder: "placeholder",
|
2224
|
+
kw.shape_type_web_video: "web_video",
|
2225
|
+
kw.shape_type_media: "media",
|
2226
|
+
kw.shape_type_text_box: "text_box",
|
2227
|
+
kw.shape_type_table: "table",
|
2228
|
+
kw.shape_type_ink: "ink",
|
2229
|
+
kw.shape_type_ink_comment: "ink_comment",
|
2230
|
+
kw.shape_type_unset: "unset",
|
2231
|
+
kw.shape_type_slicer: "slicer",
|
2232
|
+
}
|
2233
|
+
|
2234
|
+
scaling = {
|
2235
|
+
"scale_from_top_left": kw.scale_from_top_left,
|
2236
|
+
"scale_from_bottom_right": kw.scale_from_bottom_right,
|
2237
|
+
"scale_from_middle": kw.scale_from_middle,
|
2238
|
+
}
|
2239
|
+
|
2240
|
+
shape_types_s2k = {v: k for k, v in shape_types_k2s.items()}
|