|
| 1 | +## Spreader |
| 2 | + |
| 3 | +A fast spreadsheet logic library. |
| 4 | + |
| 5 | +[](https://opensource.org/licenses/BSD-3-Clause) |
| 6 | +[](https://pypi.org/project/spreader.py) |
| 7 | +[](https://docs.python.org/3/whatsnew/3.7.html) |
| 8 | + |
| 9 | +<!-- References --> |
| 10 | + |
| 11 | +[dynamicformulas]: <https://support.microsoft.com/en-us/office/dynamic-array-formulas-and-spilled-array-behavior-205c6b06-03ba-4151-89a1-87a7eb36e531> (Dynamic Array Formulas) |
| 12 | + |
| 13 | +<!-- End References --> |
| 14 | + |
| 15 | +Spreader is a zero dependency library that implements spreadsheet logic - reading and writing data and formulas into cells, automatic recalculation, copying and moving cells, adding and deleting rows and columns and so on. It does not implement any spreadsheet UI. |
| 16 | +It is currently in alpha stage. |
| 17 | + |
| 18 | +## Install |
| 19 | + |
| 20 | +Spreader is a C++ library. In order to install it from source you will need |
| 21 | +- Working C++ compiler supporting C++20 standard |
| 22 | + - macOS: Xcode 14 or above |
| 23 | + - Windows: Visual Studio 2022 or above |
| 24 | + - Linux: GCC 11.3 or above |
| 25 | +- On Linux you will also need Python development support (the python-dev or python3-dev package in most Linux distributions). |
| 26 | +No further external dependencies are required. |
| 27 | + |
| 28 | +You can use pip to install the module |
| 29 | + |
| 30 | +```bash |
| 31 | +pip install eg.spreader |
| 32 | +``` |
| 33 | + |
| 34 | +## Quick start |
| 35 | + |
| 36 | +```python |
| 37 | +from eg.spreader import Sheet |
| 38 | +... |
| 39 | + |
| 40 | +#Create a new sheet |
| 41 | +sheet = Sheet() |
| 42 | +#Set a cell to contain plain value. Instead of "A2" |
| 43 | +#you can also say Point(x=0, y=1) or just (0, 1) or [0, 1]. |
| 44 | +#Columns are x and rows y, zero-based |
| 45 | +sheet.setValueCell("A2", "Hello ") |
| 46 | +sheet.setValueCell("B3", "World!") |
| 47 | +#Set a cell to contain formula |
| 48 | +sheet.setFormulaCell("A1", "A2 & B3") |
| 49 | +#Read calculated formula result |
| 50 | +val = s.getValue("A1") |
| 51 | +#val is "Hello Wolrd!" here |
| 52 | +``` |
| 53 | + |
| 54 | +## Limits |
| 55 | + |
| 56 | +Maximum possible size of a sheet is currently 65536 x 2147483648 (e.g. 2<sup>16</sup> x 2<sup>31</sup>) cells. |
| 57 | +You can query this value via `Sheet.maxSize` property. |
| 58 | +Note that currently, sheets are held entirely in memory (there is no 'offloading' of unused data to some kind of storage). |
| 59 | +Thus, depending on how much memory you have available the actual limit on how big a sheet you will be able to create might |
| 60 | +be lower than maximum. |
| 61 | + |
| 62 | +There is no hard limit on things like: |
| 63 | +- Maximum length of cell's string value |
| 64 | +- Maximum number of function arguments (for functions that support variable number of arguments like `SUM`) |
| 65 | +- Maximum level of nesting in formulas |
| 66 | +or anything else in the library. Instead, those are limited by available memory. In practice such limit is quite a bit larger than |
| 67 | +traditional `255` of Excel. |
| 68 | + |
| 69 | + |
| 70 | +## Cell and Area coordinates |
| 71 | + |
| 72 | +Cell coordinates in the API can be given in the usual "A1", "B2" format. Alternatively, and slightly faster, you can use `Point(x=..., y=...)` objects or `(x, y)` tuples or `[x, y]` lists. In the later case `x` and `y` are zero based so `Point(x=0, y=0)` and `(0, 0)` correspond to "A1". |
| 73 | + |
| 74 | +Similarly area coordinates can be given either as "A1:B2" or as `Rect(Point(x=..., y=...), Size(width=..., height=...))` or as |
| 75 | +`(x, y, width, height)` or `[x, y, width, height]`, whichever suits your needs more. As with individual cells, using "A1:B2" format is slightly slower. |
| 76 | + |
| 77 | +## Cell values |
| 78 | + |
| 79 | +Spreader supports the following cell value types: `None` (e.g. blank cell), `bool`, `float`, `str` and instances of its own |
| 80 | +`ErrorValue` class (e.g. `Errors.InvalidReference`) that represent the usual spreadsheet #-errors (e.g. `#REF!`). |
| 81 | + |
| 82 | +Numbers contained in a cell are never NaNs and never infinite. Setting a NaN or infinite value cell will result in an `ErrorValue` instead. |
| 83 | + |
| 84 | +Strings you set into a cell via `sheet.setValueCell()` call do not need to be escaped - they are always taken to be literal. |
| 85 | +For example `sheet.setValueCell("A1", "=3")` will result in a cell holding literal "=3" value as if you typed `'=3` in Excel |
| 86 | +or other spreadsheets. |
| 87 | + |
| 88 | +## Supported formulas |
| 89 | + |
| 90 | +Spreader supports all the operators (e.g. `=`, `+`, `>`, `*` etc.) supported by Excel. |
| 91 | + |
| 92 | +[Dynamic array formulas][dynamicformulas] are fully supported. Therefor there is no need for `ARRAYFORMULA` and it isn't recognized. |
| 93 | + |
| 94 | +The following functions are currently implemented: |
| 95 | +<details> |
| 96 | + <summary>Expand</summary> |
| 97 | + |
| 98 | +* **A** \ |
| 99 | +ADDRESS, AVERAGE, AVERAGEA, AVERAGEIF |
| 100 | +* **C** \ |
| 101 | +CEIL, CHOOSE, COLUMN, CONCAT, CONCATENATE, COUNT, COUNTA |
| 102 | +* **D** \ |
| 103 | +DATE, DATEDIF, DAY, DAYS |
| 104 | +* **E** \ |
| 105 | +EDATE, EOMONTH, ERROR.TYPE |
| 106 | +* **F** \ |
| 107 | +FIND, FLOOR |
| 108 | +* **H** \ |
| 109 | +HLOOKUP, HOUR |
| 110 | +* **I** \ |
| 111 | +IF, INDEX, INDIRECT, INT, ISBLANK, ISERR, ISERROR, ISEVEN, ISLOGICAL, ISNA, ISNONTEXT, ISNUMBER, ISODD, ISOWEEKNUM, ISTEXT |
| 112 | +* **L** \ |
| 113 | +LEFT, LEN, LOWER |
| 114 | +* **M** \ |
| 115 | +MATCH, MAX, MAXA, MID, MIN, MINA, MINUTE, MOD, MONTH, MROUND |
| 116 | +* **N** \ |
| 117 | +NOT, NOW |
| 118 | +* **O** \ |
| 119 | +OR |
| 120 | +* **R** \ |
| 121 | +REPLACE, RIGHT, ROUND, ROUNDDOWN, ROUNDUP, ROW |
| 122 | +* **S** \ |
| 123 | +SECOND, SIGN, STDEV, STDEV.P, STDEV.S, STDEVA, STDEVP, STDEVPA, SUBSTITUTE, SUM, SUMIF, SWITCH |
| 124 | +* **T** \ |
| 125 | +TIME, TODAY, TRANSPOSE, TRIM |
| 126 | +* **U** \ |
| 127 | +UPPER |
| 128 | +* **V** \ |
| 129 | +VLOOKUP |
| 130 | +* **W** \ |
| 131 | +WEEKDAY, WEEKNUM |
| 132 | +* **X** \ |
| 133 | +XOR |
| 134 | +* **Y** \ |
| 135 | +YEAR |
| 136 | +</details> |
| 137 | + |
| 138 | +## Recalculation |
| 139 | + |
| 140 | +By default sheet recalculation is automatic. You can suspend it using `sheet.suspendRecalc()` and resume it again using |
| 141 | +`sheet.resumeRecalc()`. Suspend calls can nest and each is "undone" by a corresponding resume. While suspended you can |
| 142 | +manually recalculate a sheet using `sheet.recalculate()`. |
| 143 | + |
| 144 | +## Manipulating sheet size |
| 145 | + |
| 146 | +Similar to how visual spreadsheets behave sheet size (as returned by `sheet.size()`) is dynamic and you don't manipulate |
| 147 | +it directly. When you set any cell to a non-null value sheet automatically expands to encompass it if it is outside |
| 148 | +the current size. It doesn't automatically contract, however. To reduce size you can use `sheet.deleteColumns()` and |
| 149 | +`sheet.deleteRows()` calls. Those, as well as `sheet.insertColumns()` and `sheet.insertRows()` can be used to |
| 150 | +delete/insert rows and columns anywhere in the sheet. Just like in visual spreadsheets all the references in formulas |
| 151 | +will be automatically adjusted to account for insertion or deletion. |
| 152 | + |
| 153 | +## Copying and moving cells |
| 154 | + |
| 155 | +Normal copying and moving semantics of spreadsheets are also fully supported. You can use: |
| 156 | +- `sheet.copyCell()` to copy a single cell to a single cell or area. |
| 157 | +- `sheet.copyCells()` (note the plural) to copy an area to another location given by its top left corner |
| 158 | +- `sheet.moveCell()` to move a single cell to a new location (also a single cell) |
| 159 | +- `sheet.moveCells()` to move a cell area to a new location given by its top left corner |
| 160 | + |
| 161 | +For all these calls the formula references are adjusted in normal spreadsheet fashion. |
| 162 | + |
| 163 | + |
| 164 | +## Roadmap and missing features |
| 165 | + |
| 166 | +Spreader is currently in alpha - implemented features work and work well but many desirable things are missing. |
| 167 | +In particular, it is currently impossible to implement a full, performant spreadsheet UI on top of it. This is |
| 168 | +mostly due to lack of things such as change notifications, undo and support of keeping track of cell formatting. |
| 169 | +With this in mind the following features are on the roadmap |
| 170 | + |
| 171 | +- Change notifications to enable clients to react to cells changed during recalculation |
| 172 | +- Ability to associate abstract formatting information with cells rows and columns. Spreader itself doesn't care |
| 173 | +about formatting - it just needs to keep track of it for clients to act upon. |
| 174 | + - Support conditional formatting formulas. Similar to above associate opaque format with boolean formulas and tell |
| 175 | + the client which one to use. |
| 176 | +- Support optional undo. It needs to be optional because it will slow things down and not all clients need it. |
| 177 | +- Support more functions in formulas |
| 178 | +- Support localized formula input. Currently formula syntax must use US English syntax: `.` as decimal separator, `,` to separate |
| 179 | +arguments. Excel allows using `,` and `;` for languages that use comma as decimal separator. |
| 180 | +- Serialization/deserialization. Likely in `xlsx` and `json`. |
| 181 | +- Maybe: support doing spreadsheet math using decimals rather than doubles. |
| 182 | + |
| 183 | + |
0 commit comments