From ca3984734dace91da9b51b1ad7d6a4135548a874 Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Sun, 21 Aug 2022 00:49:47 -0500 Subject: [PATCH] Refactory and documented Calc Ranges --- doc/content/es/calc/cells/_index.md | 85 ++++ doc/content/es/calc/cells/methods/_index.md | 23 + .../es/calc/cells/properties/_index.md | 344 ++++++++++++++ doc/content/es/calc/ranges/_index.md | 83 ++++ doc/content/es/calc/ranges/methods/_index.md | 42 ++ .../es/calc/ranges/properties/_index.md | 46 ++ source/easymacro/__init__.py | 3 + source/easymacro/easycalc.py | 445 ++++++++++++++++-- source/easymacro/easydialog.py | 4 +- source/easymacro/easymain.py | 15 + source/easymacro/easyshape.py | 144 ++++++ source/easymacro/easytools.py | 11 - 12 files changed, 1197 insertions(+), 48 deletions(-) create mode 100644 doc/content/es/calc/cells/_index.md create mode 100644 doc/content/es/calc/cells/methods/_index.md create mode 100644 doc/content/es/calc/cells/properties/_index.md create mode 100644 doc/content/es/calc/ranges/_index.md create mode 100644 doc/content/es/calc/ranges/methods/_index.md create mode 100644 doc/content/es/calc/ranges/properties/_index.md create mode 100644 source/easymacro/easyshape.py diff --git a/doc/content/es/calc/cells/_index.md b/doc/content/es/calc/cells/_index.md new file mode 100644 index 0000000..55c5ce1 --- /dev/null +++ b/doc/content/es/calc/cells/_index.md @@ -0,0 +1,85 @@ ++++ +title = "Celdas y rangos" +weight = 3 ++++ + +#### Trabajar con celdas y rangos + +### selection + +Referencia por selección actual. + +```python +seleccion = app.selection +app.debug(seleccion) +``` + +``` +20/08/2022 15:32:36 - DEBUG - Cell: $Sheet1.$A$2 +20/08/2022 15:32:39 - DEBUG - Range: $Sheet1.$C$8:$D$11 +``` + +### address + +Referencia por dirección. + +```python +hoja = app.active_sheet +celda = hoja['A1'] +rango = hoja['C10:D15'] + +app.debug(celda) +app.debug(rango) +``` + + +### position + +Referencia por posición. + +Para celdas: `HOJA[fila,columna]` + +Para rangos: `HOJA[fila_inicial:fila_final, columna_inicial:columna_final]` + + +```python +hoja = app.active_sheet + +# ~ Cell A10 +celda = hoja[9,0] + +# ~ Range A1:C10 +rango = hoja[0:10,0:3] +``` + + +### iter + +Iterar cada celda de un rango. + +```python +hoja = app.active_sheet +rango = hoja['B10:C15'] + +for celda in rango: + app.debug(celda) +``` + + +### contains + +Verificar si un rango esta dentro de otro. + +```python +hoja = app.active_sheet + +celda = hoja['C5'] +rango = hoja['A1:E10'] + +resultado = celda in rango +app.debug(resultado) + +celda = hoja['C50'] +resultado = celda in rango +app.debug(resultado) +``` diff --git a/doc/content/es/calc/cells/methods/_index.md b/doc/content/es/calc/cells/methods/_index.md new file mode 100644 index 0000000..c73bf4a --- /dev/null +++ b/doc/content/es/calc/cells/methods/_index.md @@ -0,0 +1,23 @@ ++++ +title = "Métodos" +weight = 2 ++++ + + +### clear + +Limpia el rango. Por default solo borra datos. Mire [API CellFlags][1] para más información. + +```python +rango.clear() +``` + +Para borrar todo. + +```python +rango.clear(app.ALL) +``` + + + +[1]: https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1sheet_1_1CellFlags.html diff --git a/doc/content/es/calc/cells/properties/_index.md b/doc/content/es/calc/cells/properties/_index.md new file mode 100644 index 0000000..62bf54d --- /dev/null +++ b/doc/content/es/calc/cells/properties/_index.md @@ -0,0 +1,344 @@ ++++ +title = "Propiedades" +weight = 1 ++++ + + +### is_cell + +Verdadero (True) si el rango es de una sola celda. + +```python +hoja = app.active_sheet + +celda = hoja['A1'] +app.debug(celda.is_cell) + +rango = hoja['A1:C5'] +app.debug(rango.is_cell) +``` + + +### name + +Devuelve la dirección de la celda o rango como texto. + +```python +hoja = app.active_sheet + +celda = hoja['A1'] +app.debug(celda.name) + +rango = hoja['A1:C5'] +app.debug(rango.name) +``` + + +### address + +Devuelve la dirección de la celda como una estructura: `com.sun.star.table.CellAddress` + +```python +hoja = app.active_sheet + +celda = hoja['A1'] +if celda.is_cell: + app.debug(celda.address) +``` + + +### range_address + +Devuelve la dirección del rango como una estructura: `com.sun.star.table.CellRangeAddress` + +```python +hoja = app.active_sheet + +rango = hoja['A1:C5'] +if not rango.is_cell: + app.debug(rango.range_address) +``` + + +### filas y columnas + +Devolver el tamaño del rango en filas y columnas. + +```python +hoja = app.active_sheet + +rango = hoja['A1:E100'] +filas = len(rango) +columnas = rango.len_columns +app.debug(filas, columnas) +``` + + +### sheet + +Devuelve la hoja padre. + +```python +rango = hoja['A1:C5'] + +hoja = rango.sheet +app.debug(hoja) +``` + + +### doc + +Devuelve el documento padre. + +```python +rango = hoja['A1:C5'] + +doc = rango.doc +app.debug(doc) +``` + + +### style + +Devuelve o aplica el estilo de celda. + +```python +rango = hoja['A1:C5'] +rango.style = 'Good' +``` + + +### current_region + +Devuelve la región actual. + +```python +celda = hoja['A1'] +rango = celda.current_region +app.debug(rango) +``` + + +### range_data + +Devuelve la región actual del rango excepto la primer fila. + +```python +celda = hoja['A1'] +rango = celda.range_data +app.debug(rango) +``` + + +### back_color + +Devuelve o aplica el color de fondo del rango. + +```python +rango = hoja['A1:E10'] +rango.back_color = 'red' +``` + +### type + +Devuelve el tipo de contenido de la celda: texto, número o formula. + +```python +celda = hoja['A1'] +app.debug(celda.type) +``` + +### error + +Si la celda tiene una formula con error, devuelve el número de error. + +```python +celda = hoja['A1'] +app.debug(celda.error) +``` + + +### string + +Devuelve o establece el contenido de la celda como texto. + +```python +celda = hoja['A1'] +celda.string = 'Maldito Mundo' +app.debug(celda.type, celda.string) + +celda = hoja['A2'] +celda.string = 12345 +app.debug(celda.type, celda.string) +``` + + +### float + +Devuelve o establece el contenido de la celda como valor. + +```python +celda = hoja['A1'] +celda.float = 12345 +app.debug(celda.type, celda.float) +``` + + +### formula + +Devuelve o establece la formula de la celda. + +```python +celda = hoja['A1'] +celda.formula = '=RAND()' +app.debug(celda.type, celda.formula) +``` + + +### date + +Devuelve o establece el contenido de la celda como fecha. + +```python +celda = hoja['A1'] +celda.date = app.dates.date(1974, 1, 15) +app.debug(type(celda.date), celda.date) +``` + +``` +20/08/2022 18:38:53 - DEBUG - 1974-01-15 +``` + +### time + +Devuelve o establece el contenido de la celda como tiempo. + +```python +celda = hoja['A1'] +celda.time = app.dates.time(10, 11, 12) +app.debug(type(celda.time), celda.time) +``` + + +### datetime + +Devuelve o establece el contenido de la celda como fecha y tiempo. + +```python +celda = hoja['A1'] +celda.datetime = app.dates.datetime(1974, 1, 15, 10, 11, 12) +app.debug(type(celda.datetime), celda.datetime) +``` + +### value + +Devuelve o establece el valor de la celda, estableciendo el tipo de dato automáticamente. + +```python +hoja = app.active_sheet + +celda = hoja['A1'] +celda.value = 'Soy Texto' +app.debug(celda.type, celda.value) + +celda = hoja['A2'] +celda.value = 12345 +app.debug(celda.type, celda.value) + +celda = hoja['A3'] +celda.value = '=RAND()' +app.debug(celda.type, celda.value) + +celda = hoja['A4'] +celda.value = app.dates.date(1974, 1, 15) +app.debug(celda.type, celda.value) +``` + + +### data_array + +Devuelve o establece los datos de un rango. Es un alias de DataArray. + +```python +rango = app.selection +datos = rango.data_array +app.debug(datos) +rango.data_array = datos +``` + +{{% notice warning %}} +El tamaño de los datos, debe ser **exactamente** del tamaño del rango destino. De lo contrario obtendrá un error. +{{% /notice %}} + + +### formula_array + +Devuelve o establece los datos de un rango. Es un alias de FormulaArray. + +```python +rango = app.selection + +datos = rango.data_array +app.debug(datos) +datos = rango.formula_array +app.debug(datos) +rango.formula_array = datos +``` + +{{% notice tip %}} +**data_array** devolverá los resultados de las celdas con formulas. **formula_array** devolverá las formulas en dichas celdas. +{{% /notice %}} + + +### data + +Alias de `data_array` al obtener los datos. Al establecer los datos, si es un rango se comporta como `data_array`, pero si es una celda, se autoajusta al tamaño de los datos. + +```python +hoja = app.active_sheet +celda = hoja['A1'] +datos = ( + (1, 'Uno'), + (2, 'Dos'), + (3, 'Tres'), +) + +celda.data = datos +app.debug(celda.current_region.data) +``` + +{{% notice tip %}} +Siempre valide que haya suficientes celdas libres para los datos para evitar sobreescribirlos. +{{% /notice %}} + + +### dict + +Devuelve o establece los datos como diccionarios. + +```python +hoja = app.active_sheet +celda = hoja['A1'] + +datos = ( + {'No': 1, 'Nombre': 'Ingrid'}, + {'No': 2, 'Nombre': 'Sophia'}, + {'No': 3, 'Nombre': 'Scarlette'}, +) + +celda.dict = datos +app.debug(celda.current_region.dict) +``` + + +### next_free + +Devuelve la siguiente celda libre después de la región actual. + +```python +hoja = app.active_sheet +celda = hoja['A1'] + +celda_libre = celda.next_free +app.debug(celda_libre) +``` diff --git a/doc/content/es/calc/ranges/_index.md b/doc/content/es/calc/ranges/_index.md new file mode 100644 index 0000000..75f8852 --- /dev/null +++ b/doc/content/es/calc/ranges/_index.md @@ -0,0 +1,83 @@ ++++ +title = "Conjuntos de Rangos" +weight = 2 ++++ + +#### Trabajar con conjuntos de rangos + +### Selección + +Obtener una referencia desde la selección actual. Deben de estar seleccionados más de un rango de celdas. + +```python +doc = app.active +seleccion = doc.selection +app.debug(seleccion) +``` + +``` +20/08/2022 13:21:17 - DEBUG - Ranges: ('Sheet1.A5:C8', 'Sheet1.E11:F14') +``` + + +### len + +Contar los rangos. + +```python +doc = app.active +contar = len(doc.selection) +app.debug(contar) +``` + + +### iter + +Iterar entre los rangos. + +```python +doc = app.active +for rango in doc.selection: + app.debug(rango) +``` + +``` +20/08/2022 13:27:03 - DEBUG - Range: $Sheet1.$B$4:$D$7 +20/08/2022 13:27:03 - DEBUG - Range: $Sheet1.$G$10:$H$14 +``` + + +### index + +Referencia a un rango por índice. + +```python +doc = app.active +rangos = doc.selection + +rango = rangos[1] +app.debug(rango) +``` + +### address + +Referencia a un rango por su dirección. + +```python +rango = rangos['Hoja1.A1:B5'] +app.debug(rango) +``` + + +### contain + +Verificar si un rango esta en la colección. + +```python +doc = app.active +hoja = doc.active +rangos = doc.selection + +resultado = hoja['D5:F10'] in rangos +app.debug(resultado) +``` diff --git a/doc/content/es/calc/ranges/methods/_index.md b/doc/content/es/calc/ranges/methods/_index.md new file mode 100644 index 0000000..dc3b4d6 --- /dev/null +++ b/doc/content/es/calc/ranges/methods/_index.md @@ -0,0 +1,42 @@ ++++ +title = "Métodos" +weight = 2 ++++ + + +### ranges + +Crear un nuevo contender de rangos vacío. + +```python +doc = app.active +rangos = doc.ranges() +app.debug(rangos) +``` + + +### add + +```python +doc = app.active +hoja = doc.active + +rangos = doc.ranges() +rangos.add(hoja['A1:B2']) +rangos.add(hoja['D5:F10']) +app.debug(rangos) +``` + + +### remove + +```python +rangos.remove(hoja['A1:B2']) +``` + + +### get_ranges + +```python +``` + diff --git a/doc/content/es/calc/ranges/properties/_index.md b/doc/content/es/calc/ranges/properties/_index.md new file mode 100644 index 0000000..e55e4a0 --- /dev/null +++ b/doc/content/es/calc/ranges/properties/_index.md @@ -0,0 +1,46 @@ ++++ +title = "Propiedades" +weight = 1 ++++ + + +# names + +Devolver las direcciones de los rangos. + +```python +doc = app.active +rangos = doc.selection + +nombres = rangos.names +app.debug(nombres) +``` + + +### data + +Devolver y establecer datos. + +```python +doc = app.active +rangos = doc.selection + +datos = rangos.data +app.debug(datos) +rangos.data = datos +``` + +{{% notice warning %}} +Cada rango debe tener exactamente el mismo tamaño. +{{% /notice %}} + + +### style + +Establecer el estilo de todos los rangos. + +```python +doc = app.active +rangos = doc.selection +rangos.style = 'Good' +``` diff --git a/source/easymacro/__init__.py b/source/easymacro/__init__.py index 87172f6..560a5dd 100644 --- a/source/easymacro/__init__.py +++ b/source/easymacro/__init__.py @@ -9,10 +9,12 @@ from .easydocs import LODocuments def __getattr__(name): classes = { 'active': LODocuments().active, + 'active_sheet': LODocuments().active.active, 'clipboard': ClipBoard, 'cmd': LOMain.commands, 'color': Color(), 'config': Config, + 'current_region': LODocuments().active.selection.current_region, 'dates': Dates, 'dialog': LODialog, 'dispatch': LOMain.dispatch, @@ -27,6 +29,7 @@ def __getattr__(name): 'menus': LOMenus(), 'paths': Paths, 'url': URL, + 'selection': LODocuments().active.selection, 'set_config': set_app_config, 'shell': Shell, 'shortcuts': LOShortCuts(), diff --git a/source/easymacro/easycalc.py b/source/easymacro/easycalc.py index 787e297..c7958d9 100644 --- a/source/easymacro/easycalc.py +++ b/source/easymacro/easycalc.py @@ -1,10 +1,166 @@ #!/usr/bin/env python3 +import datetime +from decimal import Decimal from typing import Any, Union -from .easymain import log, BaseObject, Color, dict_to_property, run_in_thread +from com.sun.star.sheet import CellFlags +from com.sun.star.table.CellContentType import EMPTY, VALUE, TEXT, FORMULA + +from .easymain import (log, DATE_OFFSET, + BaseObject, Color, + dict_to_property, run_in_thread, set_properties +) from .easydoc import LODocument from .easyevents import EventsRangeSelectionListener, LOEvents +from .easyshape import LOShapes, LOShape + + +SECONDS_DAY = 60 * 60 * 24 +ONLY_VALUES = CellFlags.VALUE + CellFlags.DATETIME + CellFlags.STRING + + +class LOCellStyle(): + + def __init__(self, obj): + self._obj = obj + + def __str__(self): + return f'CellStyle: {self.name}' + + @property + def obj(self): + return self._obj + + @property + def name(self): + return self.obj.Name + + @property + def properties(self): + properties = self.obj.PropertySetInfo.Properties + data = {p.Name: getattr(self.obj, p.Name) for p in properties} + return data + @properties.setter + def properties(self, values): + set_properties(self.obj, values) + + +class LOCellStyles(): + + def __init__(self, obj, doc): + self._obj = obj + self._doc = doc + + def __len__(self): + return len(self.obj) + + def __getitem__(self, index): + return LOCellStyle(self.obj[index]) + + def __setitem__(self, key, value): + self.obj[key] = value + + def __delitem__(self, key): + if not isinstance(key, str): + key = key.Name + del self.obj[key] + + def __contains__(self, item): + return item in self.obj + + @property + def obj(self): + return self._obj + + @property + def names(self): + return self.obj.ElementNames + + def new(self, name: str): + obj = self._doc.create_instance('com.sun.star.style.CellStyle') + self.obj[name] = obj + return LOCellStyle(obj) + + +class LOCalcRanges(object): + + def __init__(self, obj): + self._obj = obj + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + pass + + def __len__(self): + return self._obj.Count + + def __iter__(self): + self._index = 0 + return self + + def __next__(self): + try: + r = self.obj[self._index] + rango = LOCalcRange(r) + except IndexError: + raise StopIteration + + self._index += 1 + return rango + + def __contains__(self, item): + return self._obj.hasByName(item.name) + + def __getitem__(self, index): + r = self.obj[index] + rango = LOCalcRange(r) + return rango + + def __str__(self): + s = f'Ranges: {self.names}' + return s + + @property + def obj(self): + return self._obj + + @property + def names(self): + return self.obj.ElementNames + + @property + def data(self): + rows = [r.data for r in self] + return rows + @data.setter + def data(self, values): + for i, data in enumerate(values): + self[i].data = data + + @property + def style(self): + return '' + @style.setter + def style(self, value): + for r in self: + r.style = value + + def add(self, rangos: Any): + if isinstance(rangos, LOCalcRange): + rangos = (rangos,) + for r in rangos: + self.obj.addRangeAddress(r.range_address, False) + return + + def remove(self, rangos: Any): + if isinstance(rangos, LOCalcRange): + rangos = (rangos,) + for r in rangos: + self.obj.removeRangeAddress(r.range_address) + return class LOCalcRange(): @@ -14,6 +170,14 @@ class LOCalcRange(): self._obj = obj self._is_cell = obj.ImplementationName == self.CELL + def __contains__(self, rango): + if isinstance(rango, LOCalcRange): + address = rango.range_address + else: + address = rango.RangeAddress + result = self.cursor.queryIntersection(address) + return bool(result.Count) + def __getitem__(self, index): return LOCalcRange(self.obj[index]) @@ -42,8 +206,7 @@ class LOCalcRange(): def __len__(self): ra = self.range_address rows = ra.EndRow - ra.StartRow + 1 - cols = ra.EndColumn - ra.StartColumn + 1 - return rows, cols + return rows def __str__(self): s = f'Range: {self.name}' @@ -53,74 +216,257 @@ class LOCalcRange(): @property def obj(self): + """Get original object pyUNO""" return self._obj + @property + def len_columns(self): + """Get len columns""" + ra = self.range_address + cols = ra.EndColumn - ra.StartColumn + 1 + return cols + @property def is_cell(self): + """True if range is cell""" return self._is_cell @property def name(self): + """Return the range address like string""" return self.obj.AbsoluteName @property def address(self): + """Return com.sun.star.table.CellAddress""" return self.obj.CellAddress @property def range_address(self): + """Return com.sun.star.table.CellRangeAddress""" return self.obj.RangeAddress @property def sheet(self): + """Return sheet parent""" return LOCalcSheet(self.obj.Spreadsheet) @property def doc(self): + """Return doc parent""" doc = self.obj.Spreadsheet.DrawPage.Forms.Parent - return LODocCalc(doc) - - @property - def cursor(self): - cursor = self.obj.Spreadsheet.createCursorByRange(self.obj) - return cursor - - def offset(self, rows=0, cols=1): - ra = self.range_address - col = ra.EndColumn + cols - row = ra.EndRow + rows - return LOCalcRange(self.sheet[row, col].obj) + return LOCalc(doc) @property def style(self): + """Get or set cell style""" return self.obj.CellStyle @style.setter def style(self, value): self.obj.CellStyle = value + @property + def current_region(self): + """Return current region""" + cursor = self.cursor + cursor.collapseToCurrentRegion() + rango = self.obj.Spreadsheet[cursor.AbsoluteName] + return LOCalcRange(rango) + + @property + def back_color(self): + """Get or set cell back color""" + return self._obj.CellBackColor + @back_color.setter + def back_color(self, value): + self._obj.CellBackColor = Color()(value) + + @property + def type(self): + """Get type of content. + See https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1table.html#affea688ab9e00781fa35d8a790d10f0e + """ + return self.obj.Type + + @property + def error(self): + """Get number error in formulas""" + return self.obj.getError() + @property def string(self): + """Get or set content like string""" return self.obj.String @string.setter def string(self, value): self.obj.setString(value) @property - def data(self): - return self.obj.getDataArray() - @data.setter - def data(self, values): - if self._is_cell: - self.to_size(len(values[0]), len(values)).data = values - else: - self.obj.setDataArray(values) + def float(self): + """Get or set content like value""" + return self.obj.Value + @float.setter + def float(self, value): + self.obj.setValue(value) @property - def current_region(self): - cursor = self.cursor - cursor.collapseToCurrentRegion() - rango = self.obj.Spreadsheet[cursor.AbsoluteName] - return LOCalcRange(rango) + def formula(self): + """Get or set formula""" + return self.obj.Formula + @formula.setter + def formula(self, value): + self.obj.setFormula(value) + + @property + def date(self): + """Get or set date""" + value = int(self.obj.Value) + date = datetime.date.fromordinal(value + DATE_OFFSET) + return date + @date.setter + def date(self, value): + d = value.toordinal() + self.float = d - DATE_OFFSET + + @property + def time(self): + """Get or set time""" + seconds = self.obj.Value * SECONDS_DAY + time_delta = datetime.timedelta(seconds=seconds) + time = (datetime.datetime.min + time_delta).time() + return time + @time.setter + def time(self, value): + d = (value.hour * 3600 + value.minute * 60 + value.second) / SECONDS_DAY + self.float = d + + @property + def datetime(self): + """Get or set datetime""" + return datetime.datetime.combine(self.date, self.time) + @datetime.setter + def datetime(self, value): + d = value.toordinal() + t = (value - datetime.datetime.fromordinal(d)).seconds / SECONDS_DAY + self.float = d - DATE_OFFSET + t + + @property + def value(self): + """Get or set value, automatically get type data""" + v = None + if self.type == VALUE: + v = self.float + elif self.type == TEXT: + v = self.string + elif self.type == FORMULA: + v = self.formula + return v + @value.setter + def value(self, value): + if isinstance(value, str): + if value[0] in '=': + self.formula = value + else: + self.string = value + elif isinstance(value, Decimal): + self.float = float(value) + elif isinstance(value, (int, float, bool)): + self.float = value + elif isinstance(value, datetime.datetime): + self.datetime = value + elif isinstance(value, datetime.date): + self.date = value + elif isinstance(value, datetime.time): + self.time = value + + @property + def data_array(self): + """Get or set DataArray""" + return self.obj.DataArray + @data_array.setter + def data_array(self, data): + self.obj.DataArray = data + + @property + def formula_array(self): + """Get or set FormulaArray""" + return self.obj.FormulaArray + @formula_array.setter + def formula_array(self, data): + self.obj.FormulaArray = data + + @property + def data(self): + """Get like data_array, for set, if range is cell, automatically ajust size range. + """ + return self.data_array + @data.setter + def data(self, values): + if self.is_cell: + cols = len(values[0]) + rows = len(values) + if cols == 1 and rows == 1: + self.data_array = values + else: + self.to_size(cols, rows).data = values + else: + self.data_array = values + + @property + def dict(self): + """Get or set data from to dict""" + rows = self.data + k = rows[0] + data = [dict(zip(k, r)) for r in rows[1:]] + return data + @dict.setter + def dict(self, values): + data = [tuple(values[0].keys())] + data += [tuple(d.values()) for d in values] + self.data = data + + @property + def next_free(self): + """Get next free cell""" + ra = self.current_region.range_address + col = ra.StartColumn + row = ra.EndRow + 1 + return LOCalcRange(self.sheet[row, col].obj) + + @property + def range_data(self): + """Get range without headers""" + rango = self.current_region + ra = rango.range_address + row1 = ra.StartRow + 1 + row2 = ra.EndRow + 1 + col1 = ra.StartColumn + col2 = ra.EndColumn + 1 + return LOCalcRange(self.sheet[row1:row2, col1:col2].obj) + + @property + def array_formula(self): + return self.obj.ArrayFormula + @array_formula.setter + def array_formula(self, value): + self.obj.ArrayFormula = value + + @property + def cursor(self): + cursor = self.obj.Spreadsheet.createCursorByRange(self.obj) + return cursor + + def clear(self, what: int=ONLY_VALUES): + """Clear contents""" + # ~ http://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1sheet_1_1CellFlags.html + self.obj.clearContents(what) + return + + def offset(self, rows=0, cols=1): + ra = self.range_address + col = ra.EndColumn + cols + row = ra.EndRow + rows + return LOCalcRange(self.sheet[row, col].obj) def to_size(self, cols: int, rows: int): cursor = self.cursor @@ -137,6 +483,9 @@ class LOCalcSheet(BaseObject): def __getitem__(self, index): return LOCalcRange(self.obj[index]) + def __enter__(self): + return self + def __exit__(self, exc_type, exc_value, traceback): pass @@ -313,17 +662,19 @@ class LOCalc(LODocument): @property def selection(self): - sel = self.obj.CurrentSelection - type_obj = sel.ImplementationName + """Get current seleccion""" + sel = None + selection = self.obj.CurrentSelection + type_obj = selection.ImplementationName if type_obj in self.TYPE_RANGES: - sel = LOCalcRange(sel) + sel = LOCalcRange(selection) elif type_obj == self.RANGES: - sel = LOCalcRanges(sel) + sel = LOCalcRanges(selection) elif type_obj == self.SHAPE: - if len(sel) == 1: - sel = LOShape(sel[0]) + if len(selection) == 1: + sel = LOShape(selection[0]) else: - sel = LOShapes(sel) + sel = LOShapes(selection) else: log.debug(type_obj) return sel @@ -341,13 +692,21 @@ class LOCalc(LODocument): @property def events(self): + """Get class events""" return LOEvents(self.obj.Events) + def ranges(self): + """Create ranges container""" + obj = self._create_instance('com.sun.star.sheet.SheetCellRanges') + return LOCalcRanges(obj) + def new_sheet(self): + """Create new instance sheet""" s = self._create_instance('com.sun.star.sheet.Spreadsheet') return s def select(self, rango: Any): + """Select range""" obj = rango if hasattr(rango, 'obj'): obj = rango.obj @@ -374,6 +733,7 @@ class LOCalc(LODocument): return def remove_range_selection_listener(self): + """Remove range listener""" if not self._listener_range_selection is None: self._cc.removeRangeSelectionListener(self._listener_range_selection) return @@ -495,3 +855,18 @@ class LOCalc(LODocument): for i, n in enumerate(names): self.move(n, i) return + + def get_ranges(self, address: str): + """Get the same range address in each sheet. + """ + ranges = self.ranges() + ranges.add([sheet[address] for sheet in self]) + return ranges + + @property + def cs(self): + return self.cell_styles + @property + def cell_styles(self): + obj = self.obj.StyleFamilies['CellStyles'] + return LOCellStyles(obj, self) diff --git a/source/easymacro/easydialog.py b/source/easymacro/easydialog.py index 116efa4..2877dd8 100644 --- a/source/easymacro/easydialog.py +++ b/source/easymacro/easydialog.py @@ -6,9 +6,9 @@ from com.sun.star.view.SelectionType import SINGLE, MULTI, RANGE from .easyevents import * from .easydocs import LODocuments -from .easymain import log, TITLE, Color, BaseObject, create_instance +from .easymain import log, TITLE, Color, BaseObject, create_instance, set_properties from .easytools import LOInspect, Paths, Services -from .easytools import _, set_properties +from .easytools import _ __all__ = [ diff --git a/source/easymacro/easymain.py b/source/easymacro/easymain.py index 6fec984..fcc4e82 100644 --- a/source/easymacro/easymain.py +++ b/source/easymacro/easymain.py @@ -18,6 +18,7 @@ from com.sun.star.datatransfer import XTransferable, DataFlavor __all__ = [ + 'ALL', 'DESKTOP', 'INFO_DEBUG', 'IS_MAC', @@ -54,6 +55,9 @@ IS_WIN = OS == 'Windows' IS_MAC = OS == 'Darwin' +ALL = 1023 + + LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s' LOG_DATE = '%d/%m/%Y %H:%M:%S' if IS_WIN: @@ -185,6 +189,17 @@ def run_in_thread(fn: Any) -> Any: return run +def set_properties(model, properties): + if 'X' in properties: + properties['PositionX'] = properties.pop('X') + if 'Y' in properties: + properties['PositionY'] = properties.pop('Y') + keys = tuple(properties.keys()) + values = tuple(properties.values()) + model.setPropertyValues(keys, values) + return + + # ~ https://github.com/django/django/blob/main/django/utils/functional.py#L61 class classproperty: diff --git a/source/easymacro/easyshape.py b/source/easymacro/easyshape.py new file mode 100644 index 0000000..9a67329 --- /dev/null +++ b/source/easymacro/easyshape.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python3 + + +class LOShapes(object): + _type = 'ShapeCollection' + + def __init__(self, obj): + self._obj = obj + + def __len__(self): + return self.obj.Count + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + pass + + def __iter__(self): + self._index = 0 + return self + + def __next__(self): + try: + s = self.obj[self._index] + shape = LOShape(s) + except IndexError: + raise StopIteration + + self._index += 1 + return shape + + def __str__(self): + return 'Shapes' + + @property + def obj(self): + return self._obj + + +class LOShape(): + IMAGE = 'com.sun.star.drawing.GraphicObjectShape' + + def __init__(self, obj): + self._obj = obj + + def __str__(self): + return f'Shape: {self.name}' + + @property + def obj(self): + return self._obj + + @property + def properties(self): + # ~ properties = self.obj.PropertySetInfo.Properties + # ~ data = {p.Name: getattr(self.obj, p.Name) for p in properties} + data = self.obj.PropertySetInfo.Properties + keys = [p.Name for p in data] + values = self.obj.getPropertyValues(keys) + data = dict(zip(keys, values)) + return data + @properties.setter + def properties(self, values): + set_properties(self.obj, values) + + @property + def shape_type(self): + return self.obj.ShapeType + + @property + def name(self): + return self.obj.Name + @name.setter + def name(self, value): + self.obj.Name = value + + @property + def is_image(self): + return self.shape_type == self.IMAGE + + @property + def is_shape(self): + return self.shape_type != self.IMAGE + + @property + def size(self): + s = self.obj.Size + return s + @size.setter + def size(self, value): + self.obj.Size = value + + @property + def width(self): + s = self.obj.Size + return s.Width + @width.setter + def width(self, value): + s = self.size + s.Width = value + self.size = s + + @property + def height(self): + s = self.obj.Size + return s.Height + @height.setter + def height(self, value): + s = self.size + s.Height = value + self.size = s + + @property + def position(self): + return self.obj.Position + @property + def x(self): + return self.position.X + @property + def y(self): + return self.position.Y + + @property + def string(self): + return self.obj.String + @string.setter + def string(self, value): + self.obj.String = value + + @property + def title(self): + return self.obj.Title + @title.setter + def title(self, value): + self.obj.Title = value + + @property + def description(self): + return self.obj.Description + @description.setter + def description(self, value): + self.obj.Description = value + diff --git a/source/easymacro/easytools.py b/source/easymacro/easytools.py index bf765c8..f6a9172 100644 --- a/source/easymacro/easytools.py +++ b/source/easymacro/easytools.py @@ -313,17 +313,6 @@ def render(template, data): return s.safe_substitute(**data) -def set_properties(model, properties): - if 'X' in properties: - properties['PositionX'] = properties.pop('X') - if 'Y' in properties: - properties['PositionY'] = properties.pop('Y') - keys = tuple(properties.keys()) - values = tuple(properties.values()) - model.setPropertyValues(keys, values) - return - - class Services(): @classproperty