numbers-parser 4.8.0__py3-none-any.whl → 4.9.0__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.
- numbers_parser/cell.py +110 -13
- numbers_parser/cell_storage.py +48 -12
- numbers_parser/constants.py +109 -6
- numbers_parser/containers.py +2 -2
- numbers_parser/document.py +303 -136
- numbers_parser/file.py +9 -4
- numbers_parser/model.py +104 -7
- {numbers_parser-4.8.0.dist-info → numbers_parser-4.9.0.dist-info}/METADATA +96 -74
- {numbers_parser-4.8.0.dist-info → numbers_parser-4.9.0.dist-info}/RECORD +12 -12
- {numbers_parser-4.8.0.dist-info → numbers_parser-4.9.0.dist-info}/LICENSE.rst +0 -0
- {numbers_parser-4.8.0.dist-info → numbers_parser-4.9.0.dist-info}/WHEEL +0 -0
- {numbers_parser-4.8.0.dist-info → numbers_parser-4.9.0.dist-info}/entry_points.txt +0 -0
numbers_parser/document.py
CHANGED
|
@@ -4,24 +4,28 @@ from warnings import warn
|
|
|
4
4
|
from pendulum import DateTime, Duration
|
|
5
5
|
|
|
6
6
|
from numbers_parser.cell import (
|
|
7
|
+
BackgroundImage,
|
|
7
8
|
Border,
|
|
8
9
|
Cell,
|
|
10
|
+
ControlFormattingType,
|
|
9
11
|
CustomFormatting,
|
|
10
12
|
CustomFormattingType,
|
|
11
|
-
DateCell,
|
|
12
13
|
Formatting,
|
|
13
14
|
FormattingType,
|
|
14
15
|
MergedCell,
|
|
15
|
-
NumberCell,
|
|
16
16
|
Style,
|
|
17
17
|
TextCell,
|
|
18
18
|
UnsupportedWarning,
|
|
19
19
|
xl_cell_to_rowcol,
|
|
20
|
+
xl_range,
|
|
20
21
|
)
|
|
21
22
|
from numbers_parser.cell_storage import CellStorage
|
|
22
23
|
from numbers_parser.constants import (
|
|
24
|
+
CUSTOM_FORMATTING_ALLOWED_CELLS,
|
|
23
25
|
DEFAULT_COLUMN_COUNT,
|
|
24
26
|
DEFAULT_ROW_COUNT,
|
|
27
|
+
FORMATTING_ACTION_CELLS,
|
|
28
|
+
FORMATTING_ALLOWED_CELLS,
|
|
25
29
|
MAX_COL_COUNT,
|
|
26
30
|
MAX_HEADER_COUNT,
|
|
27
31
|
MAX_ROW_COUNT,
|
|
@@ -29,7 +33,7 @@ from numbers_parser.constants import (
|
|
|
29
33
|
from numbers_parser.containers import ItemsList
|
|
30
34
|
from numbers_parser.file import write_numbers_file
|
|
31
35
|
from numbers_parser.model import _NumbersModel
|
|
32
|
-
from numbers_parser.numbers_cache import Cacheable
|
|
36
|
+
from numbers_parser.numbers_cache import Cacheable
|
|
33
37
|
|
|
34
38
|
__all__ = ["Document", "Sheet", "Table"]
|
|
35
39
|
|
|
@@ -197,20 +201,6 @@ class Document:
|
|
|
197
201
|
If no style name is provided, the next available numbered style
|
|
198
202
|
will be generated in the series ``Custom Style 1``, ``Custom Style 2``, etc.
|
|
199
203
|
|
|
200
|
-
Parameters
|
|
201
|
-
----------
|
|
202
|
-
kwargs: dict, optional
|
|
203
|
-
Key-value pairs to pass to the :class:`Style` constructor
|
|
204
|
-
|
|
205
|
-
Raises
|
|
206
|
-
------
|
|
207
|
-
TypeError:
|
|
208
|
-
If ``font_size`` is not a ``float``, ``font_name`` is not a ``str``,
|
|
209
|
-
or if any of the ``bool`` parameters are invalid.
|
|
210
|
-
|
|
211
|
-
Example
|
|
212
|
-
-------
|
|
213
|
-
|
|
214
204
|
.. code-block:: python
|
|
215
205
|
|
|
216
206
|
red_text = doc.add_style(
|
|
@@ -224,9 +214,27 @@ class Document:
|
|
|
224
214
|
)
|
|
225
215
|
table.write("B2", "Red", style=red_text)
|
|
226
216
|
table.set_cell_style("C2", red_text)
|
|
217
|
+
|
|
218
|
+
Parameters
|
|
219
|
+
----------
|
|
220
|
+
kwargs: dict, optional
|
|
221
|
+
Key-value pairs to pass to the :py:class:`~numbers_parser.Style` constructor.
|
|
222
|
+
|
|
223
|
+
Raises
|
|
224
|
+
------
|
|
225
|
+
TypeError:
|
|
226
|
+
If ``font_size`` is not a ``float``, ``font_name`` is not a ``str``,
|
|
227
|
+
``bg_image`` is not a :py:class:`~numbers_parser.BackgroundImage`,
|
|
228
|
+
or if any of the ``bool`` parameters are invalid.
|
|
227
229
|
""" # noqa: E501
|
|
228
230
|
if "name" in kwargs and kwargs["name"] is not None and kwargs["name"] in self._model.styles:
|
|
229
231
|
raise IndexError(f"style '{kwargs['name']}' already exists")
|
|
232
|
+
|
|
233
|
+
if "bg_image" in kwargs and kwargs["bg_image"] is not None:
|
|
234
|
+
if not isinstance(kwargs["bg_image"], BackgroundImage):
|
|
235
|
+
raise TypeError("bg_image must be a BackgroundImage object")
|
|
236
|
+
self._model.store_image((kwargs["bg_image"].data), kwargs["bg_image"].filename)
|
|
237
|
+
|
|
230
238
|
style = Style(**kwargs)
|
|
231
239
|
if style.name is None:
|
|
232
240
|
style.name = self._model.custom_style_name()
|
|
@@ -238,20 +246,28 @@ class Document:
|
|
|
238
246
|
r"""
|
|
239
247
|
Add a new custom format to the current document.
|
|
240
248
|
|
|
249
|
+
.. code-block:: python
|
|
250
|
+
|
|
251
|
+
long_date = doc.add_custom_format(
|
|
252
|
+
name="Long Date",
|
|
253
|
+
type="date",
|
|
254
|
+
date_time_format="EEEE, d MMMM yyyy"
|
|
255
|
+
)
|
|
256
|
+
table.set_cell_formatting("C1", "custom", format=long_date)
|
|
257
|
+
|
|
241
258
|
All custom formatting styles share a name and a type, described in the **Common**
|
|
242
259
|
parameters in the following table. Additional key-value pairs configure the format
|
|
243
|
-
depending upon the value of ``kwargs["type"]``.
|
|
244
|
-
``kwargs["type"]`` are:
|
|
245
|
-
|
|
246
|
-
* ``"datetime"``: A date and time value with custom formatting.
|
|
247
|
-
* ``"number"``: A decimal number.
|
|
248
|
-
* ``"text"``: A simple text string.
|
|
260
|
+
depending upon the value of ``kwargs["type"]``.
|
|
249
261
|
|
|
250
|
-
:Common
|
|
262
|
+
:Common Args:
|
|
251
263
|
* **name** (``str``) – The name of the custom format. If no name is provided,
|
|
252
264
|
one is generated using the scheme ``Custom Format``, ``Custom Format 1``, ``Custom Format 2``, etc.
|
|
253
265
|
* **type** (``str``, *optional*, default: ``number``) – The type of format to
|
|
254
|
-
create
|
|
266
|
+
create:
|
|
267
|
+
|
|
268
|
+
* ``"datetime"``: A date and time value with custom formatting.
|
|
269
|
+
* ``"number"``: A decimal number.
|
|
270
|
+
* ``"text"``: A simple text string.
|
|
255
271
|
|
|
256
272
|
:``"number"``:
|
|
257
273
|
* **integer_format** (``PaddingType``, *optional*, default: ``PaddingType.NONE``) – How
|
|
@@ -273,17 +289,6 @@ class Document:
|
|
|
273
289
|
* **format** (``str``, *optional*, default: ``"%s"``) – Text format.
|
|
274
290
|
The cell value is inserted in place of %s. Only one substitution is allowed by
|
|
275
291
|
Numbers, and multiple %s formatting references raise a TypeError exception
|
|
276
|
-
|
|
277
|
-
Example
|
|
278
|
-
-------
|
|
279
|
-
|
|
280
|
-
.. code-block:: python
|
|
281
|
-
|
|
282
|
-
long_date = doc.add_custom_format(
|
|
283
|
-
name="Long Date",
|
|
284
|
-
type="date",
|
|
285
|
-
date_time_format="EEEE, d MMMM yyyy")
|
|
286
|
-
table.set_cell_formatting("C1", "custom", format=long_date)
|
|
287
292
|
""" # noqa: E501
|
|
288
293
|
if (
|
|
289
294
|
"name" in kwargs
|
|
@@ -312,11 +317,6 @@ class Document:
|
|
|
312
317
|
|
|
313
318
|
|
|
314
319
|
class Sheet:
|
|
315
|
-
"""
|
|
316
|
-
.. NOTE::
|
|
317
|
-
Do not instantiate directly. Sheets are created by :py:class:`~numbers_parser.Document`.
|
|
318
|
-
"""
|
|
319
|
-
|
|
320
320
|
def __init__(self, model, sheet_id):
|
|
321
321
|
self._sheet_id = sheet_id
|
|
322
322
|
self._model = model
|
|
@@ -408,11 +408,6 @@ class Sheet:
|
|
|
408
408
|
|
|
409
409
|
|
|
410
410
|
class Table(Cacheable): # noqa: F811
|
|
411
|
-
"""
|
|
412
|
-
.. NOTE::
|
|
413
|
-
Do not instantiate directly. Tables are created by :py:class:`~numbers_parser.Document`.
|
|
414
|
-
"""
|
|
415
|
-
|
|
416
411
|
def __init__(self, model, table_id):
|
|
417
412
|
super().__init__()
|
|
418
413
|
self._model = model
|
|
@@ -461,6 +456,14 @@ class Table(Cacheable): # noqa: F811
|
|
|
461
456
|
"""
|
|
462
457
|
int: The number of header rows.
|
|
463
458
|
|
|
459
|
+
Example
|
|
460
|
+
-------
|
|
461
|
+
|
|
462
|
+
.. code-block:: python
|
|
463
|
+
|
|
464
|
+
# Add an extra header row
|
|
465
|
+
table.num_header_rows += 1
|
|
466
|
+
|
|
464
467
|
Raises
|
|
465
468
|
------
|
|
466
469
|
ValueError:
|
|
@@ -484,6 +487,14 @@ class Table(Cacheable): # noqa: F811
|
|
|
484
487
|
"""
|
|
485
488
|
int: The number of header columns.
|
|
486
489
|
|
|
490
|
+
Example
|
|
491
|
+
-------
|
|
492
|
+
|
|
493
|
+
.. code-block:: python
|
|
494
|
+
|
|
495
|
+
# Add an extra header column
|
|
496
|
+
table.num_header_cols += 1
|
|
497
|
+
|
|
487
498
|
Raises
|
|
488
499
|
------
|
|
489
500
|
ValueError:
|
|
@@ -516,6 +527,11 @@ class Table(Cacheable): # noqa: F811
|
|
|
516
527
|
"""
|
|
517
528
|
The height of a table row in points.
|
|
518
529
|
|
|
530
|
+
.. code-block:: python
|
|
531
|
+
|
|
532
|
+
# Double the row's height
|
|
533
|
+
_ = table.row_height(4, table.row_height(4) * 2)
|
|
534
|
+
|
|
519
535
|
Parameters
|
|
520
536
|
----------
|
|
521
537
|
row: int
|
|
@@ -571,7 +587,6 @@ class Table(Cacheable): # noqa: F811
|
|
|
571
587
|
return self._data
|
|
572
588
|
|
|
573
589
|
@property
|
|
574
|
-
@cache(num_args=0)
|
|
575
590
|
def merge_ranges(self) -> List[str]:
|
|
576
591
|
"""List[str]: The merge ranges of cells in A1 notation.
|
|
577
592
|
|
|
@@ -587,8 +602,13 @@ class Table(Cacheable): # noqa: F811
|
|
|
587
602
|
>>> table.cell("A5")
|
|
588
603
|
<numbers_parser.cell.MergedCell object at 0x1035f5310>
|
|
589
604
|
"""
|
|
590
|
-
merge_cells =
|
|
591
|
-
|
|
605
|
+
merge_cells = set()
|
|
606
|
+
for row, cells in enumerate(self._data):
|
|
607
|
+
for col, cell in enumerate(cells):
|
|
608
|
+
if cell.is_merged:
|
|
609
|
+
size = cell.size
|
|
610
|
+
merge_cells.add(xl_range(row, col, row + size[0] - 1, col + size[1] - 1))
|
|
611
|
+
return sorted(list(merge_cells))
|
|
592
612
|
|
|
593
613
|
def cell(self, *args) -> Union[Cell, MergedCell]:
|
|
594
614
|
"""
|
|
@@ -804,20 +824,28 @@ class Table(Cacheable): # noqa: F811
|
|
|
804
824
|
The ``write()`` method supports two forms of notation to designate the position
|
|
805
825
|
of cells: **Row-column** notation and **A1** notation:
|
|
806
826
|
|
|
807
|
-
.. code
|
|
827
|
+
.. code:: python
|
|
808
828
|
|
|
809
|
-
|
|
810
|
-
|
|
829
|
+
doc = Document("write.numbers")
|
|
830
|
+
sheets = doc.sheets
|
|
831
|
+
tables = sheets[0].tables
|
|
832
|
+
table = tables[0]
|
|
833
|
+
table.write(1, 1, "This is new text")
|
|
834
|
+
table.write("B7", datetime(2020, 12, 25))
|
|
835
|
+
doc.save("new-sheet.numbers")
|
|
811
836
|
|
|
812
837
|
Parameters
|
|
813
838
|
----------
|
|
814
839
|
|
|
815
|
-
|
|
840
|
+
row: int
|
|
816
841
|
The row number (zero indexed)
|
|
817
|
-
|
|
842
|
+
col: int
|
|
818
843
|
The column number (zero indexed)
|
|
819
|
-
|
|
820
|
-
The value to write to the cell. The generated cell type
|
|
844
|
+
value: str | int | float | bool | DateTime | Duration
|
|
845
|
+
The value to write to the cell. The generated cell type is automatically
|
|
846
|
+
created based on the type of ``value``.
|
|
847
|
+
style: Style | str | None
|
|
848
|
+
The name of a document custom style or a :py:class:`~numbers_parser.cell.Style` object.
|
|
821
849
|
|
|
822
850
|
Warns
|
|
823
851
|
-----
|
|
@@ -833,19 +861,6 @@ class Table(Cacheable): # noqa: F811
|
|
|
833
861
|
If the style parameter is an invalid type.
|
|
834
862
|
ValueError:
|
|
835
863
|
If the cell type cannot be determined from the type of `param3`.
|
|
836
|
-
|
|
837
|
-
Example
|
|
838
|
-
-------
|
|
839
|
-
|
|
840
|
-
.. code:: python
|
|
841
|
-
|
|
842
|
-
doc = Document("write.numbers")
|
|
843
|
-
sheets = doc.sheets
|
|
844
|
-
tables = sheets[0].tables
|
|
845
|
-
table = tables[0]
|
|
846
|
-
table.write(1, 1, "This is new text")
|
|
847
|
-
table.write("B7", datetime(2020, 12, 25))
|
|
848
|
-
doc.save("new-sheet.numbers")
|
|
849
864
|
"""
|
|
850
865
|
# TODO: write needs to retain/init the border
|
|
851
866
|
(row, col, value) = self._validate_cell_coords(*args)
|
|
@@ -1050,8 +1065,27 @@ class Table(Cacheable): # noqa: F811
|
|
|
1050
1065
|
self.num_cols -= num_cols
|
|
1051
1066
|
self._model.number_of_columns(self._table_id, self.num_cols)
|
|
1052
1067
|
|
|
1053
|
-
def merge_cells(self, cell_range):
|
|
1054
|
-
"""
|
|
1068
|
+
def merge_cells(self, cell_range: Union[str, List[str]]) -> None:
|
|
1069
|
+
"""
|
|
1070
|
+
Convert a cell range or list of cell ranges into merged cells.
|
|
1071
|
+
|
|
1072
|
+
Parameters
|
|
1073
|
+
----------
|
|
1074
|
+
cell_range: str | List[str]
|
|
1075
|
+
Cell range(s) to merge in A1 notation
|
|
1076
|
+
|
|
1077
|
+
Example
|
|
1078
|
+
--------
|
|
1079
|
+
.. code:: python
|
|
1080
|
+
|
|
1081
|
+
>>> table.cell("B2")
|
|
1082
|
+
<numbers_parser.cell.TextCell object at 0x102c0d390>
|
|
1083
|
+
>>> table.cell("B2").is_merged
|
|
1084
|
+
False
|
|
1085
|
+
>>> table.merge_cells("B2:C2")
|
|
1086
|
+
>>> table.cell("B2").is_merged
|
|
1087
|
+
True
|
|
1088
|
+
"""
|
|
1055
1089
|
if isinstance(cell_range, list):
|
|
1056
1090
|
for x in cell_range:
|
|
1057
1091
|
self.merge_cells(x)
|
|
@@ -1074,6 +1108,48 @@ class Table(Cacheable): # noqa: F811
|
|
|
1074
1108
|
cell._set_merge(merge_cells.get((row, col)))
|
|
1075
1109
|
|
|
1076
1110
|
def set_cell_border(self, *args):
|
|
1111
|
+
"""
|
|
1112
|
+
Set the borders for a cell.
|
|
1113
|
+
|
|
1114
|
+
Cell references can be row-column offsers or Excel/Numbers-style A1 notation. Borders
|
|
1115
|
+
can be applied to multiple sides of a cell by passing a list of sides. The name(s)
|
|
1116
|
+
of the side(s) must be one of ``"top"``, ``"right"``, ``"bottom"`` or ``"left"``.
|
|
1117
|
+
|
|
1118
|
+
Numbers supports different border styles for each cell within a merged cell range
|
|
1119
|
+
for those cells that are on the outer part of the merge. ``numbers-parser`` will
|
|
1120
|
+
ignore attempts to set these invisible cell edges and issue a ``RuntimeWarning``.
|
|
1121
|
+
|
|
1122
|
+
.. code-block:: python
|
|
1123
|
+
|
|
1124
|
+
# Dashed line for B7's right border
|
|
1125
|
+
table.set_cell_border(6, 1, "right", Border(5.0, RGB(29, 177, 0), "dashes"))
|
|
1126
|
+
# Solid line starting at B7's left border and running for 3 rows
|
|
1127
|
+
table.set_cell_border("B7", "left", Border(8.0, RGB(29, 177, 0), "solid"), 3)
|
|
1128
|
+
|
|
1129
|
+
:Args (row-column):
|
|
1130
|
+
* **param1** (*int*): The row number (zero indexed).
|
|
1131
|
+
* **param2** (*int*): The column number (zero indexed).
|
|
1132
|
+
* **param3** (*str | List[str]*): Which side(s) of the cell to apply the border to.
|
|
1133
|
+
* **param4** (:py:class:`Border`): The border to add.
|
|
1134
|
+
* **param5** (*int*, *optinal*, default: 1): The length of the stroke to add.
|
|
1135
|
+
|
|
1136
|
+
:Args (A1):
|
|
1137
|
+
* **param1** (*str*): A cell reference using Excel/Numbers-style A1 notation.
|
|
1138
|
+
* **param2** (*str | List[str]*): Which side(s) of the cell to apply the border to.
|
|
1139
|
+
* **param3** (:py:class:`Border`): The border to add.
|
|
1140
|
+
* **param4** (*int*, *optional*, default: 1): The length of the stroke to add.
|
|
1141
|
+
|
|
1142
|
+
Raises
|
|
1143
|
+
------
|
|
1144
|
+
TypeError:
|
|
1145
|
+
If an invalid number of arguments is passed or if the types of the arguments
|
|
1146
|
+
are invalid.
|
|
1147
|
+
|
|
1148
|
+
Warns
|
|
1149
|
+
-----
|
|
1150
|
+
RuntimeWarning:
|
|
1151
|
+
If any of the sides to which the border is applied have been merged.
|
|
1152
|
+
""" # noqa: E501
|
|
1077
1153
|
(row, col, *args) = self._validate_cell_coords(*args)
|
|
1078
1154
|
if len(args) == 2:
|
|
1079
1155
|
(side, border_value) = args
|
|
@@ -1094,9 +1170,24 @@ class Table(Cacheable): # noqa: F811
|
|
|
1094
1170
|
self.set_cell_border(row, col, s, border_value, length)
|
|
1095
1171
|
return
|
|
1096
1172
|
|
|
1097
|
-
|
|
1173
|
+
cell = self._data[row][col]
|
|
1174
|
+
if cell.is_merged and (
|
|
1175
|
+
(side == "right" and cell.size[1] > 1) or (side == "bottom" and cell.size[0] > 1)
|
|
1176
|
+
):
|
|
1098
1177
|
warn(
|
|
1099
|
-
f"
|
|
1178
|
+
f"{side} edge of [{row},{col}] is merged; border not set",
|
|
1179
|
+
RuntimeWarning,
|
|
1180
|
+
stacklevel=2,
|
|
1181
|
+
)
|
|
1182
|
+
return
|
|
1183
|
+
elif isinstance(cell, MergedCell) and (
|
|
1184
|
+
(side == "top" and cell.row_start < row)
|
|
1185
|
+
or (side == "right" and cell.col_end > col)
|
|
1186
|
+
or (side == "bottom" and cell.row_end > row)
|
|
1187
|
+
or (side == "left" and cell.col_start < col)
|
|
1188
|
+
):
|
|
1189
|
+
warn(
|
|
1190
|
+
f"{side} edge of [{row},{col}] is merged; border not set",
|
|
1100
1191
|
RuntimeWarning,
|
|
1101
1192
|
stacklevel=2,
|
|
1102
1193
|
)
|
|
@@ -1121,92 +1212,144 @@ class Table(Cacheable): # noqa: F811
|
|
|
1121
1212
|
|
|
1122
1213
|
Cell references can be **row-column** offsers or Excel/Numbers-style **A1** notation.
|
|
1123
1214
|
|
|
1215
|
+
.. code:: python
|
|
1216
|
+
|
|
1217
|
+
table.set_cell_formatting(
|
|
1218
|
+
"C1",
|
|
1219
|
+
"date",
|
|
1220
|
+
date_time_format="EEEE, d MMMM yyyy"
|
|
1221
|
+
)
|
|
1222
|
+
table.set_cell_formatting(
|
|
1223
|
+
0,
|
|
1224
|
+
4,
|
|
1225
|
+
"number",
|
|
1226
|
+
decimal_places=3,
|
|
1227
|
+
negative_style=NegativeNumberStyle.RED
|
|
1228
|
+
)
|
|
1229
|
+
|
|
1124
1230
|
:Parameters:
|
|
1125
1231
|
* **args** (*list*, *optional*) – Positional arguments for cell reference and data format type (see below)
|
|
1126
1232
|
* **kwargs** (*dict*, *optional*) - Key-value pairs defining a formatting options for each data format (see below).
|
|
1127
1233
|
|
|
1128
1234
|
:Args (row-column):
|
|
1129
|
-
* **param1** (
|
|
1130
|
-
* **param2** (
|
|
1131
|
-
* **param3** (
|
|
1235
|
+
* **param1** (*int*): The row number (zero indexed).
|
|
1236
|
+
* **param2** (*int*): The column number (zero indexed).
|
|
1237
|
+
* **param3** (*str*): Data format type for the cell (see "data formats" below).
|
|
1132
1238
|
|
|
1133
1239
|
:Args (A1):
|
|
1134
|
-
* **param1** (
|
|
1135
|
-
* **param2** (
|
|
1240
|
+
* **param1** (*str*): A cell reference using Excel/Numbers-style A1 notation.
|
|
1241
|
+
* **param2** (*str*): Data format type for the cell (see "data formats" below).
|
|
1242
|
+
|
|
1243
|
+
:Raises:
|
|
1244
|
+
* **TypeError** -
|
|
1245
|
+
If a tickbox is chosen for anything other than ``bool`` values.
|
|
1136
1246
|
|
|
1137
1247
|
:Warns:
|
|
1138
1248
|
* **RuntimeWarning** -
|
|
1139
1249
|
If ``use_accounting_style`` is used with
|
|
1140
|
-
any ``negative_style`` other than ``NegativeNumberStyle.MINUS
|
|
1250
|
+
any ``negative_style`` other than ``NegativeNumberStyle.MINUS``, or
|
|
1251
|
+
if a rating is out of range 0 to 5 (rating is clamped to these values).
|
|
1141
1252
|
|
|
1142
1253
|
All formatting styles share a name and a type, described in the **Common**
|
|
1143
1254
|
parameters in the following table. Additional key-value pairs configure the format
|
|
1144
|
-
depending upon the value of ``kwargs["type"]``.
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
*
|
|
1148
|
-
* ``"currency"``: A decimal formatted with a currency symbol.
|
|
1149
|
-
* ``"datetime"``: A date and time value with custom formatting.
|
|
1150
|
-
* ``"fraction"``: A number formatted as the nearest fraction.
|
|
1151
|
-
* ``"percentage"``: A number formatted as a percentage
|
|
1152
|
-
* ``"number"``: A decimal number.
|
|
1153
|
-
* ``"scientific"``: A decimal number with scientific notation.
|
|
1154
|
-
|
|
1155
|
-
:Common keys:
|
|
1156
|
-
* **name** (``str``) – The name of the custom format. If no name is provided,
|
|
1255
|
+
depending upon the value of ``kwargs["type"]``.
|
|
1256
|
+
|
|
1257
|
+
:Common Args:
|
|
1258
|
+
* **name** (*str*) – The name of the custom format. If no name is provided,
|
|
1157
1259
|
one is generated using the scheme ``Custom Format``, ``Custom Format 1``, ``Custom Format 2``, etc.
|
|
1158
|
-
* **type** (
|
|
1159
|
-
create
|
|
1260
|
+
* **type** (*str, optional, default: number*) – The type of format to
|
|
1261
|
+
create:
|
|
1262
|
+
|
|
1263
|
+
* ``"base"``: A number base in the range 2-36.
|
|
1264
|
+
* ``"currency"``: A decimal formatted with a currency symbol.
|
|
1265
|
+
* ``"datetime"``: A date and time value with custom formatting.
|
|
1266
|
+
* ``"fraction"``: A number formatted as the nearest fraction.
|
|
1267
|
+
* ``"percentage"``: A number formatted as a percentage
|
|
1268
|
+
* ``"number"``: A decimal number.
|
|
1269
|
+
* ``"scientific"``: A decimal number with scientific notation.
|
|
1270
|
+
* ``"tickbox"``: A checkbox (bool values only).
|
|
1271
|
+
* ``"rating"``: A star rating from 0 to 5.
|
|
1272
|
+
* ``"slider"``: A range slider.
|
|
1273
|
+
* ``"stepper"``: An up/down value stepper.
|
|
1274
|
+
* ``"popup"``: A menu of options.
|
|
1160
1275
|
|
|
1161
1276
|
:``"base"``:
|
|
1162
|
-
* **base_use_minus_sign** (
|
|
1277
|
+
* **base_use_minus_sign** (*int, optional, default: 10*) – The integer
|
|
1163
1278
|
base to represent the number from 2-36.
|
|
1164
|
-
* **base_use_minus_sign** (
|
|
1279
|
+
* **base_use_minus_sign** (*bool, optional, default: True*) – If ``True``
|
|
1165
1280
|
use a standard minus sign, otherwise format as two's compliment (only
|
|
1166
1281
|
possible for binary, octal and hexadecimal.
|
|
1167
|
-
* **base_places** (
|
|
1282
|
+
* **base_places** (*int, optional, default: 0*) – The number of
|
|
1168
1283
|
decimal places, or ``None`` for automatic.
|
|
1169
1284
|
|
|
1170
1285
|
:``"currency"``:
|
|
1171
|
-
* **currency** (
|
|
1286
|
+
* **currency** (*str, optional, default: "GBP"*) – An ISO currency
|
|
1172
1287
|
code, e.g. ``"GBP"`` or ``"USD"``.
|
|
1173
|
-
* **decimal_places** (
|
|
1288
|
+
* **decimal_places** (*int, optional, default: 2*) – The number of
|
|
1174
1289
|
decimal places, or ``None`` for automatic.
|
|
1175
|
-
* **negative_style** (
|
|
1290
|
+
* **negative_style** (*:py:class:`~numbers_parser.NegativeNumberStyle`, optional, default: NegativeNumberStyle.MINUS*) – How negative numbers are represented.
|
|
1176
1291
|
See `Negative number formats <#negative-formats>`_.
|
|
1177
|
-
* **show_thousands_separator** (
|
|
1292
|
+
* **show_thousands_separator** (*bool, optional, default: False*) – ``True``
|
|
1178
1293
|
if the number should include a thousands seperator, e.g. ``,``
|
|
1179
|
-
* **use_accounting_style** (
|
|
1294
|
+
* **use_accounting_style** (*bool, optional, default: False*) – ``True``
|
|
1180
1295
|
if the currency symbol should be formatted to the left of the cell and
|
|
1181
1296
|
separated from the number value by a tab.
|
|
1182
1297
|
|
|
1183
1298
|
:``"datetime"``:
|
|
1184
|
-
* **date_time_format** (
|
|
1299
|
+
* **date_time_format** (*str, optional, default: "dd MMM YYY HH:MM"*) – A POSIX
|
|
1185
1300
|
strftime-like formatting string of `Numbers date/time
|
|
1186
1301
|
directives <#datetime-formats>`_.
|
|
1187
1302
|
|
|
1188
1303
|
:``"fraction"``:
|
|
1189
|
-
* **fraction_accuracy** (
|
|
1304
|
+
* **fraction_accuracy** (*:py:class:`~numbers_parser.FractionAccuracy`, optional, default: FractionAccuracy.THREE* – The
|
|
1190
1305
|
precision of the faction.
|
|
1191
1306
|
|
|
1192
1307
|
:``"percentage"``:
|
|
1193
|
-
* **decimal_places** (
|
|
1308
|
+
* **decimal_places** (*float, optional, default: None*) – number of
|
|
1194
1309
|
decimal places, or ``None`` for automatic.
|
|
1195
|
-
* **negative_style** (
|
|
1310
|
+
* **negative_style** (*:py:class:`~numbers_parser.NegativeNumberStyle`, optional, default: NegativeNumberStyle.MINUS*) – How negative numbers are represented.
|
|
1196
1311
|
See `Negative number formats <#negative-formats>`_.
|
|
1197
|
-
* **show_thousands_separator** (
|
|
1312
|
+
* **show_thousands_separator** (*bool, optional, default: False*) – ``True``
|
|
1198
1313
|
if the number should include a thousands seperator, e.g. ``,``
|
|
1199
1314
|
|
|
1200
1315
|
:``"scientific"``:
|
|
1201
|
-
* **decimal_places** (
|
|
1316
|
+
* **decimal_places** (*float, optional, default: None*) – number of
|
|
1202
1317
|
decimal places, or ``None`` for automatic.
|
|
1203
1318
|
|
|
1204
|
-
|
|
1319
|
+
:``"tickbox"``:
|
|
1320
|
+
* No additional parameters defined.
|
|
1321
|
+
|
|
1322
|
+
:``"rating"``:
|
|
1323
|
+
* No additional parameters defined.
|
|
1324
|
+
|
|
1325
|
+
:``"slider"``:
|
|
1326
|
+
* **control_format** (*ControlFormattingType, optional, default: ControlFormattingType.NUMBER*) - the format
|
|
1327
|
+
of the data in the slider. Valid options are ``"base"``, ``"currency"``,
|
|
1328
|
+
``"datetime"``, ``"fraction"``, ``"percentage"``, ``"number"``,
|
|
1329
|
+
or ``"scientific". Each format allows additional parameters identical to those
|
|
1330
|
+
available for the formats themselves. For example, a slider using fractions
|
|
1331
|
+
is configured with ``fraction_accuracy``.
|
|
1332
|
+
* **increment** (*float, optional, default: 1*) - the slider's minimum value
|
|
1333
|
+
* **maximum** (*float, optional, default: 100*) - the slider's maximum value
|
|
1334
|
+
* **minimum** (*float, optional, default: 1*) - increment value for the slider
|
|
1335
|
+
|
|
1336
|
+
:`"stepper"``:
|
|
1337
|
+
* **control_format** (*ControlFormattingType, optional, default: ControlFormattingType.NUMBER*) - the format
|
|
1338
|
+
of the data in the stepper. Valid options are ``"base"``, ``"currency"``,
|
|
1339
|
+
``"datetime"``, ``"fraction"``, ``"percentage"``, ``"number"``,
|
|
1340
|
+
or ``"scientific"``. Each format allows additional parameters identical to those
|
|
1341
|
+
available for the formats themselves. For example, a stepper using fractions
|
|
1342
|
+
is configured with ``fraction_accuracy``.
|
|
1343
|
+
* **increment** (*float, optional, default: 1*) - the stepper's minimum value
|
|
1344
|
+
* **maximum** (*float, optional, default: 100*) - the stepper's maximum value
|
|
1345
|
+
* **minimum** (*float, optional, default: 1*) - increment value for the stepper
|
|
1346
|
+
|
|
1347
|
+
:`"popup"``:
|
|
1348
|
+
* **popup_values** (*List[str|int|float], optional, default: None*) – values
|
|
1349
|
+
for the popup menu
|
|
1350
|
+
* **allow_none** (*bool, optional, default: True*) - If ``True``
|
|
1351
|
+
include a blank value in the list
|
|
1205
1352
|
|
|
1206
|
-
.. code:: python
|
|
1207
|
-
|
|
1208
|
-
>>> table.set_cell_formatting("C1", "date", date_time_format="EEEE, d MMMM yyyy")
|
|
1209
|
-
>>> table.set_cell_formatting(0, 4, "number", decimal_places=3, negative_style=NegativeNumberStyle.RED)
|
|
1210
1353
|
|
|
1211
1354
|
""" # noqa: E501
|
|
1212
1355
|
(row, col, *args) = self._validate_cell_coords(*args)
|
|
@@ -1218,11 +1361,11 @@ class Table(Cacheable): # noqa: F811
|
|
|
1218
1361
|
raise TypeError("no type defined for cell format")
|
|
1219
1362
|
|
|
1220
1363
|
if format_type == "custom":
|
|
1221
|
-
self.
|
|
1364
|
+
self._set_cell_custom_format(row, col, **kwargs)
|
|
1222
1365
|
else:
|
|
1223
|
-
self.
|
|
1366
|
+
self._set_cell_data_format(row, col, format_type, **kwargs)
|
|
1224
1367
|
|
|
1225
|
-
def
|
|
1368
|
+
def _set_cell_custom_format(self, row: int, col: int, **kwargs) -> None:
|
|
1226
1369
|
if "format" not in kwargs:
|
|
1227
1370
|
raise TypeError("no format provided for custom format")
|
|
1228
1371
|
|
|
@@ -1237,33 +1380,57 @@ class Table(Cacheable): # noqa: F811
|
|
|
1237
1380
|
raise TypeError("format must be a CustomFormatting object or format name")
|
|
1238
1381
|
|
|
1239
1382
|
cell = self._data[row][col]
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
elif custom_format.type == CustomFormattingType.TEXT and not isinstance(cell, TextCell):
|
|
1247
|
-
type_name = type(cell).__name__
|
|
1248
|
-
raise TypeError(f"cannot use text formatting for cells of type {type_name}")
|
|
1383
|
+
type_name = type(cell).__name__
|
|
1384
|
+
format_type_name = custom_format.type.name.lower()
|
|
1385
|
+
if type_name not in CUSTOM_FORMATTING_ALLOWED_CELLS[format_type_name]:
|
|
1386
|
+
raise TypeError(
|
|
1387
|
+
f"cannot use {format_type_name} formatting for cells of type {type_name}"
|
|
1388
|
+
)
|
|
1249
1389
|
|
|
1250
1390
|
format_id = self._model.custom_format_id(self._table_id, custom_format)
|
|
1251
1391
|
cell._set_formatting(format_id, custom_format.type)
|
|
1252
1392
|
|
|
1253
|
-
def
|
|
1393
|
+
def _set_cell_data_format(self, row: int, col: int, format_type_name: str, **kwargs) -> None:
|
|
1254
1394
|
try:
|
|
1255
|
-
format_type = FormattingType[
|
|
1395
|
+
format_type = FormattingType[format_type_name.upper()]
|
|
1256
1396
|
except (KeyError, AttributeError):
|
|
1257
|
-
raise TypeError(f"unsuported cell format type '{
|
|
1397
|
+
raise TypeError(f"unsuported cell format type '{format_type_name}'") from None
|
|
1258
1398
|
|
|
1259
1399
|
cell = self._data[row][col]
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
raise TypeError(
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
raise TypeError(f"cannot set formatting for cells of type {type_name}")
|
|
1400
|
+
type_name = type(cell).__name__
|
|
1401
|
+
if type_name not in FORMATTING_ALLOWED_CELLS[format_type_name]:
|
|
1402
|
+
raise TypeError(
|
|
1403
|
+
f"cannot use {format_type_name} formatting for cells of type {type_name}"
|
|
1404
|
+
)
|
|
1266
1405
|
|
|
1267
1406
|
format = Formatting(type=format_type, **kwargs)
|
|
1268
|
-
|
|
1269
|
-
|
|
1407
|
+
if format_type_name in FORMATTING_ACTION_CELLS:
|
|
1408
|
+
control_id = self._model.control_cell_archive(self._table_id, format_type, format)
|
|
1409
|
+
else:
|
|
1410
|
+
control_id = None
|
|
1411
|
+
|
|
1412
|
+
is_currency = True if format_type == FormattingType.CURRENCY else False
|
|
1413
|
+
if format_type_name in ["slider", "stepper"]:
|
|
1414
|
+
if "control_format" in kwargs:
|
|
1415
|
+
try:
|
|
1416
|
+
control_format = kwargs["control_format"].name
|
|
1417
|
+
number_format_type = FormattingType[control_format]
|
|
1418
|
+
is_currency = (
|
|
1419
|
+
True
|
|
1420
|
+
if kwargs["control_format"] == ControlFormattingType.CURRENCY
|
|
1421
|
+
else False
|
|
1422
|
+
)
|
|
1423
|
+
except (KeyError, AttributeError):
|
|
1424
|
+
raise TypeError(
|
|
1425
|
+
"unsupported number format '{control_format}' for format_type_name"
|
|
1426
|
+
) from None
|
|
1427
|
+
else:
|
|
1428
|
+
number_format_type = FormattingType.NUMBER
|
|
1429
|
+
format_id = self._model.format_archive(self._table_id, number_format_type, format)
|
|
1430
|
+
elif format_type_name == "popup":
|
|
1431
|
+
popup_format_type = FormattingType.TEXT if isinstance(cell, TextCell) else True
|
|
1432
|
+
format_id = self._model.format_archive(self._table_id, popup_format_type, format)
|
|
1433
|
+
else:
|
|
1434
|
+
format_id = self._model.format_archive(self._table_id, format_type, format)
|
|
1435
|
+
|
|
1436
|
+
cell._set_formatting(format_id, format_type, control_id, is_currency=is_currency)
|