Compare commits

...

13 Commits

Author SHA1 Message Date
El Mau 2069ac0ba8 Add tables for Writer 2024-01-07 21:37:52 -06:00
El Mau 0b116337c4 Add del sheet 2024-01-02 21:56:42 -06:00
El Mau d337678d4e Add custom shapes 2024-01-02 19:46:51 -06:00
El Mau 12c6c5deaf Create DB Firebird 2024-01-01 22:22:36 -06:00
El Mau 74a781044c Add get/set original properties 2023-12-29 16:36:40 -06:00
El Mau 5a02f50cc6 Add styles 2023-12-17 23:57:30 -06:00
El Mau d0c5f3a8ed Add actions to controls 2023-12-07 19:08:49 -06:00
El Mau 3aaec4df56 Add control layout manager 2023-09-19 21:36:09 -06:00
Mauricio 3313dddae2 Add pattern field control 2023-06-28 17:57:38 -06:00
Mauricio cbf560abef Update version 2023-05-04 02:42:40 -06:00
Mauricio 70654bf40f Add events mouse and focus 2023-04-25 23:28:45 -06:00
Mauricio b8954d7ade Add remove, save, clone images 2023-04-24 23:09:03 -06:00
Mauricio 70e1933537 Add shape 2023-04-24 00:15:16 -06:00
76 changed files with 13274 additions and 236 deletions

1
.gitignore vendored
View File

@ -5,3 +5,4 @@ build/
*.lock
bk/
site/
update.sh

View File

@ -1,6 +1,19 @@
v 0.6.0 [17-Dec-2023]
---------------------
- Add control styles.
v 0.5.0 [07-Dec-2023]
---------------------
- Add acctions to controls.
- Refactorize documetation.
v 0.4.0 [20-Sep-2023]
---------------------
- Add control layout manager.
v 0.3.0 [23-Apr-2023]
---------------------
- Add method post
- Add method post.
v 0.2.0 [23-Sep-2022]
---------------------

View File

@ -14,11 +14,11 @@ This library have a cost of maintenance of 1 euro every year.
In order of preferred.
* G1: `A5DdXxCKPw3QKWVdDVs7CzkNugNUW1sHu5zDJFWxCU2h`
Mauricio Baeza
```
Euros
IBAN: BE60 9671 0556 5870
SWIFT / BIC: TRWIBEB1XXX
```
* G1: `A5DdXxCKPw3QKWVdDVs7CzkNugNUW1sHu5zDJFWxCU2h`
```

View File

@ -1 +1 @@
0.3.0
0.6.0

198
docs/en/docs/debug.md Normal file
View File

@ -0,0 +1,198 @@
## Tools for debug
<br>
### **INFO_DEBUG**
Show debugging information in a message box.
If you have any problems in your code during the development of your macros, you can [open a ticket][1] support in the system ticket of this project. Always copy the debugging information shown in your issue.
```py
import easymacro as app
def info():
app.msgbox(app.INFO_DEBUG)
return
```
![Info debug](img/install_01.png)
### **debug**
Show information at the terminal.
```py
import easymacro as app
def test_debug():
msg = 'Verify this information...'
app.debug(msg)
return
```
To view this message, you need to start LibreOffice from the command line:
```
soffice --calc
```
After executing the previous macro, you should see:
```
21/04/2023 17:04:49 - DEBUG - Verify this information...
```
<br>
### **info**
Show information messages at the terminal.
```py
import easymacro as app
def test_info():
msg = 'Starting process...'
app.info(msg)
return
```
```
11/08/2022 18:23:53 - INFO - Starting process...
```
<br>
### **error**
Show error messages at the terminal.
```py
import easymacro as app
def test_error():
msg = 'Error 505'
app.error(msg)
return
```
```
11/08/2022 18:27:34 - ERROR - Error 505
```
<br>
### **save_log**
Save log in file, automatically add date and time.
```py
import easymacro as app
def test_save_log():
app.save_log('/home/mau/log.txt', 'PyUNO')
app.save_log('/home/mau/log.txt', 'World Damn')
return
```
```
cat ~/log.txt
2022-08-11 18:30:11 - 'PyUNO'
2022-08-11 18:30:11 - 'World Damn'
```
<br>
### **msgbox**
Show any information in a message box.
```python
import easymacro as app
def message():
msg = 'Please, consume less.'
app.msgbox(msg)
msg = ('one', 2, 'three')
app.msgbox(msg)
msg = {'name': 'Teresa'}
app.msgbox(msg)
app.msgbox(app)
return
```
<br>
### **catch_exception**
Capture any error that occurs when running a macro.
!!! warning inline end "Caution"
Use this method only in development time. **Do not use it in production**.
```py
import easymacro as app
@app.catch_exception
def test_capture_error():
r = 1 / 0
return
```
```
11/08/2022 18:44:36 - ERROR - test_capture_error
Traceback (most recent call last):
File "/home/mau/.config/libreoffice/4/user/Scripts/python/pythonpath/easymacro/easytools.py", line 115, in func
return f(*args, **kwargs)
File "/home/mau/.config/libreoffice/4/user/Scripts/python/test.py", line 18, in test_capturar_error
r = 1 / 0
ZeroDivisionError: division by zero
```
<br>
### **mri**
[MRI][2] is the best extension to inspect any UNO LibreOffice object. You need to install it first so you can use it.
```py
import easymacro as app
def inspect_object():
obj = app.active
app.mri(obj)
return
```
<br>
### **inspect**
Inspect an object.
```py
import easymacro as app
def inspect_object():
doc = app.active
data = app.inspect(doc)
for p in data.properties:
app.debug(p)
for m in data.methods:
app.debug(m)
return
```
Send information of the object to worksheet.
```python
def inspect_object():
doc = app.active
app.inspect(doc, True)
return
```
[1]: https://git.cuates.net/elmau/easymacro/issues
[2]: https://github.com/hanya/MRI

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

View File

Before

Width:  |  Height:  |  Size: 380 B

After

Width:  |  Height:  |  Size: 380 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

29
docs/en/docs/index.md Normal file
View File

@ -0,0 +1,29 @@
# Welcome to EasyMacro's documentation
**Free Software, not gratis software**
<br>
**easymacro** is a library to develop more easily macros in LibreOffice with Python. It is an abstraction layer between the extensive and complex UNO LibreOffice API and your code.
You will probably be happier if you use it :)
You can use **easymacro** with any extension or directly on your macros.
<br>
- Symily or related projects in Python:
* [python-ooo-dev-tools](https://python-ooo-dev-tools.readthedocs.io)
* [oooscript](https://oooscript.readthedocs.io)
* [python-ooouno-ex](https://github.com/Amourspirit/python-ooouno-ex)
- Symily or related projects in other languages:
* [Java LibreOffice Programming](https://flywire.github.io/lo-p/)
<br>
Contributions:
- Moneda Libre Ğ1 (Junas):<br>
`A5DdXxCKPw3QKWVdDVs7CzkNugNUW1sHu5zDJFWxCU2h`
<br>

47
docs/en/docs/install.md Normal file
View File

@ -0,0 +1,47 @@
## Clone repository
Clone the repository in your favorite project directory.
```
git clone https://git.cuates.net/elmau/easymacro
```
Move into the directory
```
cd easymacro/source
```
copy the `easymacro` folder into the **pythonpath** folder in the Python macro folder within your user profile. Replace **USER** by your real user.
```
/home/USER/.config/libreoffice/4/user/Scripts/python/pythonpath
```
or use a symbolic link (recommended). Replace **PATH** by the absolute route where **easymacro** is located in your file system and **USER** by your username.
```
ln -s PATH/easymacro/source/easymacro /home/USER/.config/libreoffice/4/user/Scripts/python/pythonpath/
```
## Test
In your favorite macros file, for example **mymacros.py**. Use your favorite plain text editor or IDE. Remember, replace **USER** by your real user.
```
geany /home/USER/.config/libreoffice/4/user/Scripts/python/mymacros.py
```
Copy the following code:
```py
import easymacro as app
def main():
app.msgbox(app.INFO_DEBUG)
return
```
Run the **main** macro in LibreOffice, if you see a message box with information similar to the following!
![Test install](img/install_01.png)
It is all ready to start developing macros with **easymacro**.
Happy programming!

View File

@ -0,0 +1,178 @@
!!! tip "Attention"
Start date in Calc and Python are different.
<br>
### **today**
Get current date.
```py
d = app.dates
app.msgbox(d.today)
```
<br>
### **now**
Get current date and time.
```py
d = app.dates
app.msgbox(d.now)
```
<br>
### **time**
Get current time.
```py
d = app.dates
app.msgbox(d.now.time())
```
<br>
### **epoch**
Get [Unix time][1]
```py
d = app.dates
app.msgbox(d.epoch)
```
<br>
### **date**
Get a date.
```py
d = app.dates
date = d.date(1974, 1, 15)
app.msgbox(date)
```
<br>
### **time**
Get a time.
```py
d = app.dates
time = d.time(10, 20, 15)
app.msgbox(time)
```
<br>
### **datetime**
Get date and time.
```py
d = app.dates
dt = d.datetime(1974, 1, 15, 10, 11, 12)
app.msgbox(dt)
```
<br>
### **str_to_date**
String to date. Look this [excellent info][2]
```py
d = app.dates
str_date = '1974-01-15'
template = '%Y-%m-%d'
obj_date = d.str_to_date(str_date, template)
app.msgbox(obj_date)
app.msgbox(type(obj_date))
```
Get a valid date for insert in a Calc cell.
```py
d = app.dates
str_date = '1974-01-15'
template = '%Y-%m-%d'
obj_date = d.str_to_date(str_date, template, True)
app.msgbox(obj_date)
app.msgbox(type(obj_date))
```
<br>
### **calc_to_date**
Converts a value to Python date, for example, the initial date set in Calc.
```py
d = app.dates
value = 0
obj_date = d.calc_to_date(value)
app.msgbox(obj_date)
app.msgbox(type(obj_date))
```
<br>
### **sleep**
Pause execution for X seconds.
!!! tip inline end "Attention"
The pause is blocking.
```py
d = app.dates
app.sleep(3)
app.msgbox('End')
```
<br>
### **start** and **end**
Measure time in seconds.
```py
d = app.dates
d.start()
app.sleep(5)
seconds = d.end()
app.msgbox(seconds)
```
Get timedelta instead of seconds
```py
d = app.dates
d.start()
app.sleep(5)
td = d.end(False)
app.msgbox(td)
```
[1]: https://en.wikipedia.org/wiki/Unix_time
[2]: https://strftime.org

123
docs/en/docs/tools/index.md Normal file
View File

@ -0,0 +1,123 @@
---
title: Information
---
Remember, import first the library.
```py
import easymacro as app
```
<br>
## About PC
<br>
### **OS**
Get Operate System.
```py
app.msgbox(app.OS)
```
<br>
### **DESKTOP**
Get desktop type, only GNU/Linux.
```py
app.msgbox(app.DESKTOP)
```
<br>
### **PC**
Get PC name.
```py
app.msgbox(app.PC)
```
<br>
### **USER**
Get user name.
```py
app.msgbox(app.USER)
```
<br>
### **IS_WIN**
If OS is Windows.
```py
app.msgbox(app.IS_WIN)
```
<br>
### **IS_MAC**
IF OS is MAC
```py
app.msgbox(app.IS_MAC)
```
<br>
## About LibreOffice
### **NAME**
Application name.
```py
app.msgbox(app.NAME)
```
<br>
### **VERSION**
Version.
```py
app.msgbox(app.VERSION)
```
<br>
### **LANG**
Language
```py
app.msgbox(app.LANG)
```
<br>
### **LANGUAGE**
Language with variant.
```py
app.msgbox(app.LANGUAGE)
```
<br>
### **IS_APPIMAGE**
If LibreOffice use by AppImage.
```py
app.msgbox(app.IS_APPIMAGE)
```
<br>
### **IS_FLATPAK**
If LibreOffice is use by FlatPak.
```py
app.msgbox(app.IS_FLATPAK)
```

View File

@ -0,0 +1,75 @@
## Message Box
### **msgbox**
Show standard message.
```py
message = 'Fucking World'
title = 'My Macro'
app.msgbox(message, title)
```
![msgbox](../img/tools_msg_01.png)
<br>
### **warning**
Show message with warning icon.
```py
message = 'Caution, this action is dangerous'
title = 'My Macro'
app.warning(message, title)
```
![warning](../img/tools_msg_02.png)
<br>
### **errorbox**
Show message with error icon.
```py
message = 'ERROR: contact support'
title = 'My Macro'
app.errorbox(message, title)
```
![error](../img/tools_msg_03.png)
<br>
### **question**
Ask a question by showing the interrogation icon and displaying the command buttons `Yes` and `No`. The answer is always True if user select `yes` and False otherwise.
```py
message = 'Python is easy?'
title = 'My Macro'
result = app.question(message, title)
app.msgbox(result)
```
![question](../img/tools_msg_04.png)
<br>
### **inputbox**
Shows a message to user, allowing to capture an answer.
```py
message = 'Capture your name'
name = app.inputbox(message)
app.msgbox(name)
```
![inputbox](../img/tools_msg_05.png)
To hide on screen what the user typing, util for request passwords.
```py
message = 'Type your password'
echochar = '*'
password = app.inputbox(message, echochar=echochar)
app.msgbox(password)
```
![inputbox](../img/tools_msg_06.png)

44
docs/en/mkdocs.yml Normal file
View File

@ -0,0 +1,44 @@
site_name: EasyMacro for LibreOffice
site_url: https://doc.cuates.net/easymacro/
repo_url: https://git.cuates.net/elmau/easymacro/
nav:
- Home: index.md
- Install: install.md
- Debug: debug.md
- Tools:
- tools/index.md
- Messages: tools/messages.md
- Dates and time: tools/datetime.md
theme:
name: material
locale: en
font: false
highlightjs: true
palette:
primary: green
features:
- navigation.path
- content.code.copy
- content.code.select
markdown_extensions:
- admonition
- pymdownx.details
- pymdownx.superfences
- pymdownx.highlight:
anchor_linenums: true
line_spans: __span
pygments_lang_class: true
- pymdownx.inlinehilite
- pymdownx.snippets
- pymdownx.tabbed:
alternate_style: true
- tables
- pymdownx.keys
extra:
alternate:
- name: English
link: /easymacro
lang: en
- name: Español
link: /easymacro/langs/es
lang: es

601
docs/es/docs/calc/cells.md Normal file
View File

@ -0,0 +1,601 @@
## Trabajar con celdas y rangos
---
### **address**
Referencia por dirección.
```py
hoja = app.active.active
celda = hoja['A1']
rango = hoja['C10:D15']
app.debug(celda)
app.debug(rango)
```
<br>
### **doc**
Devuelve el documento padre.
```py
hoja = app.active.active
rango = hoja['A1:C5']
doc = rango.doc
app.debug(doc)
```
<br>
### **in**
Verificar si un rango esta dentro de otro.
```py
hoja = app.active.active
celda = hoja['C5']
rango = hoja['A1:E10']
resultado = celda in rango
app.debug(resultado)
celda = hoja['C50']
resultado = celda in rango
app.debug(resultado)
```
<br>
### **iter**
Iterar cada celda de un rango.
```py
hoja = app.active.active
rango = hoja['B10:C15']
for celda in rango:
app.debug(celda)
```
<br>
### **position**
Referencia por posición.
Para celdas: `HOJA[fila,columna]`
Para rangos: `HOJA[fila_inicial:fila_final, columna_inicial:columna_final]`
```py
hoja = app.active.active
# ~ Cell A10
celda = hoja[9,0]
# ~ Range A1:C10
rango = hoja[0:10,0:3]
```
!!! tip "Atención"
Recuerde que las filas y columnas empiezan en cero, por lo que `fila_final` y la `columna_final` deben de ser la fila y columna deseada más 1.
<br>
### **selection**
Referencia por selección actual.
```py
seleccion = app.active.selection
app.debug(seleccion)
```
!!! tip "Atención"
Recuerde que la selección actual pueden ser varias cosas diferentes.
<br>
## Propiedades
---
### **address**
Devuelve la dirección de la celda como una estructura: `com.sun.star.table.CellAddress`
```py
hoja = app.active.active
celda = hoja['A1']
if celda.is_cell:
app.debug(celda.address)
```
<br>
### **back_color**
Devuelve o aplica el color de fondo del rango.
```py
hoja = app.active.active
rango = hoja['A1:E10']
rango.back_color = 'red'
```
<br>
### **current_region**
Devuelve la región actual.
```py
hoja = app.active.active
celda = hoja['A1']
rango = celda.current_region
app.debug(rango)
```
<br>
### **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.
```py
hoja = app.active.active
celda = hoja['A1']
datos = (
(1, 'Uno'),
(2, 'Dos'),
(3, 'Tres'),
)
celda.data = datos
app.debug(celda.current_region.data)
```
!!! warning "Cuidado"
Siempre valide que haya suficientes celdas libres para los datos para evitar sobreescribirlos.
<br>
### **data_array**
Devuelve o establece los datos de un rango. Es un alias de `DataArray`.
```py
rango = app.active.selection
datos = rango.data_array
app.debug(datos)
rango.data_array = datos
```
!!! warning "Cuidado"
El tamaño de los datos, debe ser **exactamente** del tamaño del rango destino. De lo contrario obtendrá un error.
<br>
### **date**
Devuelve o establece el contenido de la celda como fecha.
```py
hoja = app.active.active
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 - <class 'datetime.date'> 1974-01-15
```
<br>
### **datetime**
Devuelve o establece el contenido de la celda como fecha y tiempo.
```python
hoja = app.active.active
celda = hoja['A1']
celda.datetime = app.dates.datetime(1974, 1, 15, 10, 11, 12)
app.debug(type(celda.datetime), celda.datetime)
```
<br>
### **dict**
Devuelve o establece los datos como diccionarios. Automáticamente ajusta el rango al tamaño de los datos.
```py
hoja = app.active.active
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)
```
!!! warning "Consejo"
Siempre valide que haya suficientes celdas libres para los datos para evitar sobreescribirlos, a menos que eso sea lo que realmente quiera.
<br>
### **error**
Si la celda tiene una formula con error, devuelve el número de error.
```py
hoja = app.active.active
celda = hoja['A1']
celda.value = '=RAND()/0'
app.debug(celda.error)
```
<br>
### **float**
Devuelve o establece el contenido de la celda como valor.
```py
hoja = app.active.active
celda = hoja['A1']
celda.float = 12345
app.debug(celda.type, celda.float)
```
<br>
### **formula**
Devuelve o establece la formula de la celda.
```py
hoja = app.active.active
celda = hoja['A1']
celda.formula = '=RAND()'
app.debug(celda.type, celda.formula)
```
<br>
### **formula_array**
Devuelve o establece los datos de un rango. Es un alias de `FormulaArray`.
```py
rango = app.active.selection
datos = rango.data_array
app.debug(datos)
datos = rango.formula_array
app.debug(datos)
rango.formula_array = datos
```
!!! tip "Consejo"
**data_array** devolverá los resultados de las celdas con formulas. **formula_array** devolverá las formulas en dichas celdas.
<br>
### **is_cell**
Verdadero (True) si el objeto es de una celda.
```py
hoja = app.active.active
celda = hoja['A1']
app.debug(celda.is_cell)
rango = hoja['A1:C5']
app.debug(rango.is_cell)
```
<br>
### **is_range**
Verdadero (True) si el objeto es de un rango.
```py
hoja = app.active.active
celda = hoja['A1']
app.debug(celda.is_range)
rango = hoja['A1:C5']
app.debug(rango.is_range)
```
<br>
### **len**
Devolver el tamaño del rango en filas y columnas.
```py
hoja = app.active.active
rango = hoja['A1:E100']
filas = len(rango)
columnas = rango.len_columns
app.debug(filas, columnas)
```
<br>
### **name**
Devuelve la dirección de la celda o rango como texto.
```py
hoja = app.active.active
celda = hoja['A1']
app.debug(celda.name)
rango = hoja['A1:C5']
app.debug(rango.name)
```
<br>
### **next_free**
Devuelve la siguiente celda libre después de la región actual.
```py
hoja = app.active.active
celda = hoja['A1']
celda_libre = celda.next_free
app.debug(celda_libre)
```
<br>
### **range_address**
Devuelve la dirección del rango como una estructura: `com.sun.star.table.CellRangeAddress`
```py
hoja = app.active.active
rango = hoja['A1:C5']
if rango.is_range:
app.debug(rango.range_address)
```
<br>
### **range_data**
Devuelve la región actual del rango excepto la primer fila.
```py
hoja = app.active.active
celda = hoja['A1']
rango = celda.range_data
app.debug(rango)
```
<br>
### **sheet**
Devuelve la hoja padre.
```py
hoja = app.active.active
rango = hoja['A1:C5']
hoja = rango.sheet
app.debug(hoja)
```
<br>
### **string**
Devuelve o establece el contenido de la celda como texto.
```py
hoja = app.active.active
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)
```
<br>
### **style**
Devuelve o aplica el estilo de celda.
```py
hoja = app.active.active
rango = hoja['A1:C5']
rango.style = 'Good'
```
<br>
### **time**
Devuelve o establece el contenido de la celda como tiempo.
```py
hoja = app.active.active
celda = hoja['A1']
celda.time = app.dates.time(10, 11, 12)
app.debug(type(celda.time), celda.time)
```
<br>
### **type**
Devuelve el tipo de contenido de la celda: texto, número o formula.
```py
hoja = app.active.active
celda = hoja['A1']
app.debug(celda.type)
```
!!! tip "Consejo"
Asegurese de que la selección siempre sea una sola celda.
<br>
### **value**
Devuelve o establece el valor de la celda, estableciendo el tipo de dato automáticamente.
```py
hoja = app.active.active
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)
```
```sh
23/04/2023 19:49:05 - DEBUG - <Enum instance com.sun.star.table.CellContentType ('TEXT')> Soy Texto
23/04/2023 19:49:05 - DEBUG - <Enum instance com.sun.star.table.CellContentType ('VALUE')> 12345.0
23/04/2023 19:49:05 - DEBUG - <Enum instance com.sun.star.table.CellContentType ('FORMULA')> =RAND()
23/04/2023 19:49:05 - DEBUG - <Enum instance com.sun.star.table.CellContentType ('VALUE')> 27044.0
```
<br>
## Métodos
---
### **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)
```
<br>
### **len**
Devuelve el tamaño en filas y columnas del rango.
```py
hoja = app.active.active
rango = hoja['A1:C100']
filas, columnas = rango.len()
app.debug(filas, columnas)
```
<br>
### **offset**
Devuelve la celda inmediata a la derecha en la misma fila.
```py
cell = app.active.active['A1']
app.debug(cell)
cell = cell.offset()
app.debug(cell)
```
```
23/04/2023 21:22:15 - DEBUG - Cell: $Sheet1.$A$1
23/04/2023 21:22:15 - DEBUG - Cell: $Sheet1.$B$1
```
Se puede establecer la distancia a la celda a devolver en filas y columnas.
```py
cell = app.active.active['A1']
app.debug(cell)
cell = cell.offset(4,2)
app.debug(cell)
```
```
23/04/2023 21:24:59 - DEBUG - Cell: $Sheet1.$A$1
23/04/2023 21:24:59 - DEBUG - Cell: $Sheet1.$C$5
```
<br>
### **to_size**
Espande el tamaño del rango actual tantas filas y columnas se pasen como argumentos.
```py
cell = app.active.active['A1']
app.debug(cell)
cell = cell.to_size(10,5)
app.debug(cell)
```
```
23/04/2023 21:30:56 - DEBUG - Cell: $Sheet1.$A$1
23/04/2023 21:30:56 - DEBUG - Range: $Sheet1.$A$1:$E$10
```
<br>
[1]: https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1sheet_1_1CellFlags.html

394
docs/es/docs/calc/index.md Normal file
View File

@ -0,0 +1,394 @@
---
title: Documento
---
Devolver el documento activo.
### **active**
```py
doc = app.active
```
<br>
## Propiedades
---
### **active**
Devuelve la hoja activa.
```py
doc = app.active
hoja = doc.active
app.debug(hoja)
```
<br>
### **headers**
Mostrar u ocultar encabezados de filas y columnas.
```py
doc = app.active
app.msgbox(doc.headers)
doc.headers = not doc.headers
app.msgbox(doc.headers)
doc.headers = not doc.headers
```
<br>
### **names**
Devolver una tupla con los nombres de todas las hojas.
```py
doc = app.active
nombres = doc.names
app.debug(nombres)
```
<br>
### **selection**
Devolver la selección activa.
!!! warning inline end "Cuidado"
La selección actual pueden ser muchas cosas diferentes.
```py
doc = app.active
seleccion = doc.selection
app.debug(seleccion)
```
<br>
### **tabs**
Mostrar u ocultar las pestañas de las hojas.
```py
doc = app.active
app.msgbox(doc.tabs)
doc.tabs = not doc.tabs
app.msgbox(doc.tabs)
doc.tabs = not doc.tabs
```
<br>
## Métodos
---
### **activate**
Activar hoja, argumento como objeto.
```py
doc = app.active
hoja = doc[-1]
doc.activate(hoja)
```
Activar hoja por nombre.
```py
doc = app.active
doc.activate('Hoja3')
```
<br>
### **copy**
Copiar hoja dentro del mismo documento.
```py
doc = app.active
hoja = doc[0]
doc.copy_sheet(hoja, 'Otra hoja')
```
Por nombre.
```py
doc.copy_sheet('Hoja1', 'Nueva Hoja')
```
Si no se establece el nuevo nombre, se generá de forma automática: `nombre + índice`.
```py
doc.copy_sheet(hoja)
```
<br>
### **copy_from**
Copiar hojas de otro documento. Copiar con el mismo nombre.
```py
doc = app.active
documento_origen = app.docs['Contactos.ods']
nombre_origen = 'Nombres'
doc.copy_from(documento_origen, nombre_origen)
```
Copiar con un nuevo nombre.
```py
doc.copy_from(documento_origen, nombre_origen, 'NuevoNombre')
```
Si solo se establece el documento origen, se copian todas las hojas.
```py
doc.copy_from(documento_origen)
```
!!! info "Información"
Si algún nombre de hoja en el origen existe en el destino, se copiará con un nuevo índice concatenado el nombre origen.
<br>
### **insert**
Inserta una nueva hoja, se devuelve la hoja insertada.
```py
doc = app.active
hoja = doc.insert('HojaInsertada')
```
Insertar varias hojas. Devolverá la última insertada.
```py
nombres = ('Enero', 'Febrero', 'Marzo')
hoja = doc.insert(nombres)
app.debug(hoja)
```
Creando una nueva instancia y asignandola.
```py
doc = app.active
doc['NuevaHoja'] = doc.new_sheet()
```
!!! warning "Cuidado"
Si la hoja existe, obtendrá un error, verifique siempre que no exista primero.
<br>
### **move**
Mover hojas.
Pasar la hoja como objeto, de forma predetermianda se mueve a la última posición.
```py
doc = app.active
hoja = doc[0]
doc.move(hoja)
```
!!! info "Información"
Al mover una hoja, cambian sus índices.
Pasar la hoja por nombre.
```py
doc = app.active
doc.move('Hoja1')
```
Especificar la posición destino.
```py
doc = app.active
hoja = doc[0]
doc.move(hoja, 2)
```
<br>
### **remove**
!!! warning "Cuidado"
Siempre debe haber al menos una hoja en un documento.
Eliminar hoja por índice.
```py
doc = app.active
sheet = doc[0]
doc.remove(sheet)
```
Eliminar por nombre.
```py
doc.remove('Hoja2')
```
<br>
### **start_range_selection**
Permitir al usuario seleccionar un rango. Es necesario pasarle una clase con el nombre que prefiera (`Eventos` es recomendable) con dos métodos para procesar la captura como en el siguiente ejemplo:
```py
class Eventos():
def __init__(self, doc):
self.doc = doc
def range_selection_done(self, range_selection):
if range_selection:
app.debug(range_selection)
self.doc.remove_range_selection_listener()
return
def range_selection_aborted(self):
self.doc.remove_range_selection_listener()
return
def main():
doc = app.active
doc.start_range_selection(Eventos)
return
```
<br>
### **select**
Seleccionar una celda o rango.
```py
doc = app.active
cell = doc[0]['A1']
doc.select(cell)
```
!!! tip "Consejo"
**No es necesario** seleccionar celdas o rangos para manipularlos.
<br>
### **sort**
Ordenar hojas en orden alfabetico.
```py
doc = app.active
doc.sort()
```
Ordenar de forma inversa.
```py
doc = app.active
doc.sort(True)
```
<br>
## Eventos
---
Obtener una tupla con los eventos soportados por el documento.
```py
doc = app.active
nombres = doc.events.names
for nombre in nombres:
app.debug(nombre)
```
```sh
23/04/2023 13:54:10 - DEBUG - OnStartApp
23/04/2023 13:54:10 - DEBUG - OnCloseApp
23/04/2023 13:54:10 - DEBUG - OnCreate
23/04/2023 13:54:10 - DEBUG - OnNew
23/04/2023 13:54:10 - DEBUG - OnLoadFinished
23/04/2023 13:54:10 - DEBUG - OnLoad
23/04/2023 13:54:10 - DEBUG - OnPrepareUnload
23/04/2023 13:54:10 - DEBUG - OnUnload
23/04/2023 13:54:10 - DEBUG - OnSave
23/04/2023 13:54:10 - DEBUG - OnSaveDone
23/04/2023 13:54:10 - DEBUG - OnSaveFailed
23/04/2023 13:54:10 - DEBUG - OnSaveAs
23/04/2023 13:54:10 - DEBUG - OnSaveAsDone
23/04/2023 13:54:10 - DEBUG - OnSaveAsFailed
23/04/2023 13:54:10 - DEBUG - OnCopyTo
23/04/2023 13:54:10 - DEBUG - OnCopyToDone
23/04/2023 13:54:10 - DEBUG - OnCopyToFailed
23/04/2023 13:54:10 - DEBUG - OnFocus
23/04/2023 13:54:10 - DEBUG - OnUnfocus
23/04/2023 13:54:10 - DEBUG - OnPrint
23/04/2023 13:54:10 - DEBUG - OnViewCreated
23/04/2023 13:54:10 - DEBUG - OnPrepareViewClosing
23/04/2023 13:54:10 - DEBUG - OnViewClosed
23/04/2023 13:54:10 - DEBUG - OnModifyChanged
23/04/2023 13:54:10 - DEBUG - OnTitleChanged
23/04/2023 13:54:10 - DEBUG - OnVisAreaChanged
23/04/2023 13:54:10 - DEBUG - OnModeChanged
23/04/2023 13:54:10 - DEBUG - OnStorageChanged
```
<br>
### Asignar una macro a un evento.
```py
def doc_on_focus(event):
app.debug('Documento activado...')
return
def main():
doc = app.active
events = doc.events
if 'OnFocus' in events:
macro = {'library': 'test', 'name': 'doc_on_focus'}
events['OnFocus'] = macro
return
```
<br>
### Eliminar la asignación del evento.
```py
doc = app.active
doc.events['OnFocus'] = {}
```
O
```python
doc = app.active
doc.events.remove('OnFocus')
```
<br>

175
docs/es/docs/calc/ranges.md Normal file
View File

@ -0,0 +1,175 @@
## Trabajar con conjuntos de rangos
---
### **address**
Referencia a un rango por su dirección.
```py
rango = rangos['Hoja1.A1:B5']
app.debug(rango)
```
<br>
### **in**
Verificar si un rango esta en la colección.
```py
doc = app.active
hoja = doc.active
rangos = doc.selection
resultado = hoja['D5:F10'] in rangos
app.debug(resultado)
```
<br>
### **index**
Referencia a un rango por índice.
```py
doc = app.active
rangos = doc.selection
rango = rangos[1]
app.debug(rango)
```
<br>
### **iter**
Iterar entre los rangos.
```py
doc = app.active
for rango in doc.selection:
app.debug(rango)
```
```sh
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
```
<br>
### **len**
Contar los rangos en la colección.
```py
doc = app.active
contar = len(doc.selection)
app.debug(contar)
```
<br>
### **selection**
Obtener una referencia desde la selección actual. Deben de estar seleccionados más de un rango de celdas.
```py
doc = app.active
seleccion = doc.selection
app.debug(seleccion)
```
```sh
20/08/2022 13:21:17 - DEBUG - Ranges: ('Sheet1.A5:C8', 'Sheet1.E11:F14')
```
<br>
## Propiedades
---
### **data**
Devolver y establecer datos.
```py
doc = app.active
rangos = doc.selection
datos = rangos.data
app.debug(datos)
rangos.data = datos
```
!!! warning "Cuidado"
Al asignar esta propiedad, cada matriz de datos debe tener exactamente el mismo tamaño del rango destino.
<br>
### **names**
Devolver las direcciones de los rangos.
```py
doc = app.active
rangos = doc.selection
nombres = rangos.names
app.debug(nombres)
```
<br>
### **style**
Establecer el estilo de todos los rangos de la colección.
```py
doc = app.active
rangos = doc.selection
rangos.style = 'Good'
```
<br>
## Métodos
---
### **add**
```py
doc = app.active
hoja = doc.active
rangos = doc.ranges()
rangos.add(hoja['A1:B2'])
rangos.add(hoja['D5:F10'])
app.debug(rangos)
```
<br>
### **ranges**
Crear un nuevo contender de rangos vacío.
```py
doc = app.active
rangos = doc.ranges()
app.debug(rangos)
```
<br>
### **remove**
```py
rangos.remove(hoja['A1:B2'])
```
<br>

354
docs/es/docs/calc/sheets.md Normal file
View File

@ -0,0 +1,354 @@
---
title: Hojas
---
## Trabajar con hojas
---
### Referencia por **índice**
```py
doc = app.active
hoja = doc[0]
app.debug(hoja.name)
```
### Referencia por **nombre**
```py
doc = app.active
hoja = doc['datos']
app.debug(hoja.name)
```
### **in**
Verificar por nombre si una hoja existe.
```py
doc = app.active
existe = 'Hoja2' in doc
app.debug(existe)
```
### **len**
Contar la cantidad de hojas en el documento.
```py
doc = app.active
contar = len(doc)
app.debug(contar)
```
### **iter**
Recorrer todas las hojas.
```py
doc = app.active
for hoja in doc:
app.debug(hoja)
```
<br>
## Propiedades
---
### **code_name**
Nombre editable y accesible solo por código.
```py
hoja = app.active.active
app.msgbox(hoja.code_name)
hoja.code_name = 'datos'
app.msgbox(hoja.code_name)
```
<br>
### **color**
Color de la pestaña.
```py
hoja = app.active.active
app.msgbox(hoja.color)
hoja.color = 'red'
app.msgbox(hoja.color)
# RGB
hoja.color = (125, 200, 10)
app.msgbox(hoja.color)
```
<br>
### **doc**
Devuelve al documento Calc donde esta la hoja.
```py
hoja = app.active.active
doc = hoja.doc
app.debug(doc.title)
```
<br>
### **is_protected**
Devuelve verdadero (True) si la hoja esta protegida
```py
hoja = app.active.active
esta_protegida = hoja.is_protected
app.debug(esta_protegida)
```
<br>
### **name**
Nombre visible y editable por el usuario.
```py
hoja = app.active.active
app.debug(hoja.name)
hoja.name = 'Nuevo Nombre'
app.debug(hoja.name)
```
<br>
### **password**
Establecer una contraseña.
!!! tip "Consejo"
Si la hoja ya esta protegida, aún sin contraseña, este método no tendrá ningún efecto.
```py
hoja = app.active.active
hoja.password = 'siscaloburropanzon'
app.debug(hoja.is_protected)
```
<br>
### **used_area**
Referencia al área de usuario actual.
```py
hoja = app.active.active
rango = hoja.used_area
app.debug(rango)
```
<br>
### **visible**
Muestra u oculta la hoja
```py
hoja = app.active.active
app.msgbox(hoja.visible)
hoja.visible = not hoja.visible
app.msgbox(hoja.visible)
hoja.visible = not hoja.visible
```
!!! tip "Cuidado"
Solo funcionará con dos o más hojas, por que debe haber al menos una visible. Siempre puedes trabajar con todo el documento oculto.
<br>
## Métodos
---
### **activate**
Pasar el foco a la hoja.
!!! tip inline end "Recuerde"
No es necesario activar una hoja para manipularse.
```py
doc = app.active
hoja = doc[-1]
hoja.activate()
```
<br>
### **copy**
!!! tip "Consejo"
Siempre valide que no exista el nuevo nombre.
```py
doc = app.active
hoja = doc[0]
nuevo_nombre = f'{hoja.name}_2'
if not nuevo_nombre in doc:
hoja.copy(nuevo_nombre)
```
Si no se establece el nuevo nombre, se generá de forma automática: `nombre + índice`.
```py
hoja.copy()
```
### **copy_to**
Copiar la hoja a otro documento. Se usa el mismo nombre.
```py
doc = app.active
hoja = doc.active
documento_nuevo = app.docs.new()
hoja.copy_to(documento_nuevo)
```
Usar un nuevo nombre.
```python
hoja.copy_to(documento_nuevo, 'Nuevo nombre')
```
<br>
### **move**
Mover a la última posición.
```py
doc = app.active
hoja = doc[0]
hoja.move()
```
Mover a una posición especifica.
```py
doc = app.active
hoja = doc[0]
hoja.move(3)
```
<br>
### **remove**
Remover hoja.
```py
sheet = app.active.active
sheet.remove()
```
!!! warning "Cuidado"
Siempre debe existir al menos una hoja.
<br>
### **unprotect**
Quitar contraseña.
```py
hoja = app.active.active
hoja.password = 'siscaloburropanzon'
app.msgbox(hoja.is_protected)
hoja.unprotect('siscaloburropanzon')
app.msgbox(hoja.is_protected)
```
<br>
## Eventos
---
### **names**
Obtener una tupla con los eventos soportados por la hoja.
```py
hoja = app.active.active
for nombre in hoja.events.names:
app.debug(nombre)
```
```sh
23/04/2023 16:59:26 - DEBUG - OnFocus
23/04/2023 16:59:26 - DEBUG - OnUnfocus
23/04/2023 16:59:26 - DEBUG - OnSelect
23/04/2023 16:59:26 - DEBUG - OnDoubleClick
23/04/2023 16:59:26 - DEBUG - OnRightClick
23/04/2023 16:59:26 - DEBUG - OnChange
23/04/2023 16:59:26 - DEBUG - OnCalculate
```
<br>
### Asignar una macro a un evento.
```py
def on_select(source):
app.debug(source.AbsoluteName)
return
def main():
doc = app.active
hoja = doc.active
if 'OnSelect' in hoja.events:
macro = {'library': 'test', 'name': 'on_select'}
hoja.events['OnSelect'] = macro
return
```
<br>
### Eliminar la asignación del evento.
```py
hoja.events['OnSelect'] = {}
```
O
```py
hoja.events.remove('OnFocus')
```
<br>

132
docs/es/docs/dialogs/cmd.md Normal file
View File

@ -0,0 +1,132 @@
# Botón de comando (button)
---
## Propiedades
---
### **type**
Devuelve el tipo de control.
```py
app.debug(button.type)
```
<br>
### **value**
Devuelve o establece la etiqueta del control.
```py
button.value = 'Cerrar'
```
<br>
## Métodos
---
## Eventos
---
### **action**
Se ejecuta al dar click con el botón primario del ratón y con las teclas ++enter++ y ++"Barra Espaciadora"++ cuando el botón de comando tiene el foco.
```py
def button_action(self, event):
return
```
<br>
### **click**
Se ejecuta al dar click con cualquier botón del ratón.
```py
def button_click(self, event):
return
```
<br>
### **double_click**
Se ejecuta al dar doble click con cualquier botón del ratón.
```py
def button_double_click(self, event):
return
```
## Propiedades pyUNO
| Nombre | Descripción |
| ------ | ----------- |
| Align | |
| BackgroundColor | |
| ContextWritingMode | |
| DefaultButton | |
| DefaultControl | |
| EnableVisible | |
| Enabled | |
| FocusOnClick | |
| FontCharWidth | |
| FontCharset | |
| FontDescriptor | |
| FontEmphasisMark | |
| FontFamily | |
| FontHeight | |
| FontKerning | |
| FontName | |
| FontOrientation | |
| FontPitch | |
| FontRelief | |
| FontSlant | |
| FontStrikeout | |
| FontStyleName | |
| FontType | |
| FontUnderline | |
| FontWeight | |
| FontWidth | |
| FontWordLineMode | |
| Graphic | |
| Height | |
| HelpText | |
| HelpURL | |
| ImageAlign | |
| ImagePosition | |
| ImageURL | |
| Label | |
| MultiLine | |
| Name | |
| PositionX | |
| PositionY | |
| Printable | |
| PushButtonType | |
| ReferenceDevice | |
| Repeat | |
| RepeatDelay | |
| ResourceResolver | |
| State | |
| Step | |
| TabIndex | |
| Tabstop | |
| Tag | |
| TextColor | |
| TextLineColor | |
| Toggle | |
| VerticalAlign | |
| Width | |
| WritingMode | |
<br>

View File

@ -0,0 +1,47 @@
# Imagen (image)
---
## Propiedades
---
## Métodos
---
## Eventos
---
## Propiedades pyUNO
| Nombre | Descripción |
| ------ | ----------- |
| BackgroundColor | |
| Border | |
| BorderColor | |
| ContextWritingMode | |
| DefaultControl | |
| EnableVisible | |
| Enabled | |
| Graphic | |
| Height | |
| HelpText | |
| HelpURL | |
| ImageURL | |
| Name | |
| PositionX | |
| PositionY | |
| Printable | |
| ResourceResolver | |
| ScaleImage | |
| ScaleMode | |
| Step | |
| TabIndex | |
| Tabstop | |
| Tag | |
| Width | |
| WritingMode | |
<br>

View File

@ -0,0 +1,267 @@
# Crear cuadros de diálogo
### **create**
---
#### Desde "Mis Macros"
Si el cuadro de diálogo esta en la librería `Standard`.
```py
def crear_cuadro_de_dialogo1():
propiedades = {
'Location': 'user',
'Name': 'Dialog1',
}
dialog = app.dialog.create(propiedades)
dialog.open()
return
```
Si el cuadro de diálogo esta en otra librería.
```py
def crear_cuadro_de_dialogo2():
propiedades = {
'Location': 'user',
'Library': 'MiAplicacion',
'Name': 'Dialog1',
}
dialog = app.dialog.create(propiedades)
dialog.open()
return
```
<br>
#### Dentro de un archivo
Si el cuadro de diálogo esta en el documento activo en la librería `Standard`.
```py
def crear_cuadro_de_dialogo3():
propiedades = {
'Location': 'document',
'Name': 'Dialog1',
}
dialog = app.dialog.create(propiedades)
dialog.open()
return
```
Si el cuadro de diálogo esta en otro documento en la librería `Standard`.
```py
def crear_cuadro_de_dialogo4():
documento = app.docs['OtroArchivo.ods']
propiedades = {
'Location': 'document',
'Document': documento,
'Name': 'Dialog1',
}
dialog = app.dialog.create(propiedades)
dialog.open()
return
```
<br>
#### Desde archivo
Crear un cuadro de diálogo desde un archivo `xdl` previamente creado desde el editor de cuadros de diálogo dentro de LibreOffice y exportado a un archivo.
```py
def crear_cuadro_de_dialogo():
path = '/home/elmau/Desktop/mi_dialogo.xdl'
propiedades = {'Path': path}
dialog = app.dialog.create(propiedades)
dialog.open()
return
```
<br>
#### Crear dinámicamente
Crear un cuadro de diálogo vacío.
```py
def crear_cuadro_de_dialogo():
dialog = app.dialog.create()
dialog.open()
return
```
Cambiar las propiedades iniciales.
```py
propiedades = dict(
Name = 'MiDialogo',
Title = 'Maldito Mundo',
Width = 200,
Height = 100,
)
dialog = app.dialog.create(propiedades)
dialog.open()
```
<br>
!!! tip Importante
Cualquier propiedad usada al crear el cuadro de diálogo, debe ser la original del control pyUNO.
#### Propiedades
| Nombre | Descripción |
| ------ | ----------- |
| AllDialogChildren | |
| BackgroundColor | Color de fondo |
| Closeable | Permite cerrar el cuadro de diálogo con el icono `X` |
| Decoration |
| DefaultControl |
| DesktopAsParent |
| DialogSourceURL |
| Enabled |
| FontCharWidth |
| FontCharset |
| FontDescriptor |
| FontEmphasisMark |
| FontFamily |
| FontHeight |
| FontKerning |
| FontName |
| FontOrientation |
| FontPitch |
| FontRelief |
| FontSlant |
| FontStrikeout |
| FontStyleName |
| FontType |
| FontUnderline |
| FontWeight |
| FontWidth |
| FontWordLineMode |
| Graphic |
| HScroll |
| Height | Altura del control |
| HelpText |
| HelpURL |
| ImageURL |
| Moveable |
| Name | Nombre del control |
| PositionX | Posición en el eje X |
| PositionY | Posición en el eje Y |
| ResourceResolver |
| ScrollHeight |
| ScrollLeft |
| ScrollTop |
| ScrollWidth |
| Sizeable | Si el control cambia de tamaño |
| Step |
| TabIndex |
| Tag |
| TextColor |
| TextLineColor |
| Title | Título del cuadro de diálogo |
| VScroll | Mostrar barra de desplazamiento vertial |
| Width | Ancho del control |
<br>
### **add_control**
---
Agrega un control al cuadro de diálogo.
```py
propiedades = {
'Type': 'label',
'Name': 'lbl_info',
'Label': 'Esta es una etiqueta.',
'Width': 100,
'Height': 15,
'Border': 1,
'X': 5,
'Y': 5,
}
dialog.add_control(propiedades)
```
<br>
## Eventos
---
Todos los eventos de todos los controles de un cuadro de diálogo se controlan por medio de una clase Python que se pasa a la propiedad `events` del cuadro de diálogo.
Cada método de esta clase que tenga en su nombre la estructura que corresponde al nombre del control más el nombre del evento, responderá a dicho evento de dicho control.
`NOMBRE_CONTROL_NOMBRE_EVENTO`
Ejemplo:
```py
import easymacro as app
class Eventos():
def __init__(self, dialog):
self.d = dialog
def cmd_cerrar_action(self, event):
self.d.close(1)
return
def main():
propiedades = dict(
Name = 'MiDialogo',
Title = 'Maldito Mundo',
Width = 200,
Height = 100,
)
dialog = app.dialog.create(propiedades)
dialog.events = Eventos
propiedades = {
'Type': 'button',
'Name': 'cmd_cerrar',
'Label': 'Cerrar',
'Width': 40,
'Height': 15,
}
dialog.add_control(propiedades)
dialog.open()
return
```
<br>
Cada control agregado al cuadro de diálogo es instanciado como una propiedad del mismo cuadro de diálogo. En el ejemplo anterior, el botón de comando `cmd_cerrar` es accesible desde cualquier variable que apunte al cuadro de diálogo, en el mismo ejemplo anterior en:
```py
dialog.cmd_cerrar
```
Como en la clase `Eventos` en:
```py
self.d.cmd_cerrar
```
<br>
## Controles
---
#### [Etiqueta (label)](label/)
#### [Botón de comando (button)](cmd/)
<br>

View File

@ -0,0 +1,101 @@
# Etiqueta (label)
---
## Propiedades
---
### **type**
Devuelve el tipo de control.
```py
app.debug(label.type)
```
<br>
### **value**
Devuelve o establece la etiqueta del control.
```py
label.value = 'Mi etiqueta'
```
<br>
## Métodos
---
## Eventos
---
### **click**
Se ejecuta al dar click con cualquier botón del ratón.
```py
def label_click(self, event):
return
```
<br>
## Propiedades pyUNO
| Nombre | Descripción |
| ------ | ----------- |
| Align | |
| BackgroundColor | |
| Border | |
| BorderColor | |
| ContextWritingMode | |
| DefaultControl | |
| EnableVisible | |
| Enabled | |
| FontCharWidth | |
| FontCharset | |
| FontDescriptor | |
| FontEmphasisMark | |
| FontFamily | |
| FontHeight | |
| FontKerning | |
| FontName | |
| FontOrientation | |
| FontPitch | |
| FontRelief | |
| FontSlant | |
| FontStrikeout | |
| FontStyleName | |
| FontType | |
| FontUnderline | |
| FontWeight | |
| FontWidth | |
| FontWordLineMode | |
| Height | |
| HelpText | |
| HelpURL | |
| Label | |
| MultiLine | |
| Name | |
| NoLabel | |
| PositionX | |
| PositionY | |
| Printable | |
| ReferenceDevice | |
| ResourceResolver | |
| Step | |
| TabIndex | |
| Tabstop | |
| Tag | |
| TextColor | |
| TextLineColor | |
| VerticalAlign | |
| Width | |
| WritingMode | |

View File

@ -0,0 +1,88 @@
# Hipervínculo (link)
---
## Propiedades
### **label**
Devuelve o establece la etiqueta.
<br>
### **url**
Devuelve o establece el vínculo web.
<br>
### **value**
Devuelve o establece el vínculo web y la etiqueta.
<br>
---
## Métodos
---
## Eventos
---
## Propiedades pyUNO
| Nombre | Descripción |
| ------ | ----------- |
| Align | |
| BackgroundColor | |
| Border | |
| BorderColor | |
| ContextWritingMode | |
| DefaultControl | |
| EnableVisible | |
| Enabled | |
| FontCharWidth | |
| FontCharset | |
| FontDescriptor | |
| FontEmphasisMark | |
| FontFamily | |
| FontHeight | |
| FontKerning | |
| FontName | |
| FontOrientation | |
| FontPitch | |
| FontRelief | |
| FontSlant | |
| FontStrikeout | |
| FontStyleName | |
| FontType | |
| FontUnderline | |
| FontWeight | |
| FontWidth | |
| FontWordLineMode | |
| Height | |
| HelpText | |
| HelpURL | |
| Label | |
| MultiLine | |
| Name | |
| NoLabel | |
| PositionX | |
| PositionY | |
| Printable | |
| ResourceResolver | |
| Step | |
| TabIndex | |
| Tabstop | |
| Tag | |
| TextColor | |
| TextLineColor | |
| URL | |
| VerticalAlign | |
| Width | |
| WritingMode | |
<br>

View File

@ -0,0 +1,17 @@
# Control (label)
---
## Propiedades
---
## Métodos
---
## Eventos
---
## Propiedades pyUNO

View File

@ -0,0 +1,112 @@
# Cuadro de texto (text)
---
## Propiedades
---
### **value**
Establece o recupera el valor del control.
```py
app.debug(text.value)
```
## Métodos
---
### **set_focus**
Envía el foco al control.
<br>
## Eventos
---
### **focus_gained**
Se produce al recibir el foco.
<br>
### **focus_lost**
Se produce al perder el foco.
<br>
### **text_changed**
Se produce al cambiar el contenido del control.
<br>
## Propiedades pyUNO
| Nombre | Descripción |
| ------ | ----------- |
| Align | |
| AutoHScroll | |
| AutoVScroll | |
| BackgroundColor | |
| Border | |
| BorderColor | |
| ContextWritingMode | |
| DefaultControl | |
| EchoChar | |
| EnableVisible | |
| Enabled | |
| FontCharWidth | |
| FontCharset | |
| FontDescriptor | |
| FontEmphasisMark | |
| FontFamily | |
| FontHeight | |
| FontKerning | |
| FontName | |
| FontOrientation | |
| FontPitch | |
| FontRelief | |
| FontSlant | |
| FontStrikeout | |
| FontStyleName | |
| FontType | |
| FontUnderline | |
| FontWeight | |
| FontWidth | |
| FontWordLineMode | |
| HScroll | |
| HardLineBreaks | |
| Height | |
| HelpText | |
| HelpURL | |
| HideInactiveSelection | |
| LineEndFormat | |
| MaxTextLen | |
| MultiLine | |
| Name | |
| PaintTransparent | |
| PositionX | |
| PositionY | |
| Printable | |
| ReadOnly | |
| ResourceResolver | |
| Step | |
| TabIndex | |
| Tabstop | |
| Tag | |
| Text | |
| TextColor | |
| TextLineColor | |
| VScroll | |
| VerticalAlign | |
| Width | |
| WritingMode | |
<br>

123
docs/es/docs/dp/drawpage.md Normal file
View File

@ -0,0 +1,123 @@
---
title: DrawPage
---
La página de dibujo es la capa gráfica donde se agregan las formas y las imágenes en los documentos. La forma de acceder es la misma, solo cambia el tipo de documento.
## Hoja de Calc
### **draw_page**
```py
hoja = app.active.active
pagina_dibujo = hoja.draw_page
```
### **dp**
Alias de `draw_page`
```py
hoja = app.active.active
pagina_dibujo = hoja.dp
```
### **shapes**
Alias de `draw_page`
```py
hoja = app.active.active
pagina_dibujo = hoja.shapes
```
<br>
Una vez referenciada, sus métodos y propiedades son las mismas.
---
<br>
## Formas
---
### **index**
Acceso por índice
```py
forma = formas[0]
app.debug(forma)
```
Acceso por nombre
```py
forma = formas['Rectangulo1']
app.debug(forma)
```
<br>
### **in**
Verificar si una forma esta en la colección por nombre.
```py
app.debug('Shape 1' in formas)
```
<br>
### **iter**
Iterar los elementos gráficos de la página de dibujo.
```py
for f in formas:
app.debug(f)
```
<br>
### **len**
Contar los elementos gráficos en la página de dibujo.
```py
app.debug(len(formas))
```
<br>
## Métodos
---
### **add**
Agrega una nueva forma (un rectángulo) y la devuelve.
```py
forma = formas.add('Rectangle')
app.debug(forma)
```
De forma predeterminada la posición será a 1 cm en Y y X y de 3 cm de ancho y alto. Estos valores se pueden modificar al crear la forma. Todas las unidades en milésimas de centímetro.
```py
opciones = dict(
Name = 'mi_rectangulo_1',
Width = 5000,
Height = 2000,
X = 3000,
Y = 1000,
)
forma = formas.add('Rectangle', opciones)
```
<br>

202
docs/es/docs/dp/shapes.md Normal file
View File

@ -0,0 +1,202 @@
## Propiedades
Todas las unidades en milésimas de centímetro.
---
### **anchor**
Anclaje de la forma, por default se agrega a la página.
#### Anclaje a una celda
```py
celda = hoja['A1']
forma.anchor = celda
```
<br>
### **height**
Alto de la forma.
```py
forma.height = 2000
```
<br>
### **name**
Devuelve o establece el nombre de la forma.
```py
app.debug(forma.name)
forma.name = 'otro_rectangulo'
app.debug(forma.name)
```
<br>
### **obj**
**Solo lectura.** Devuelve el objeto original pyUNO.
```py
objeto = forma.obj
```
<br>
### **possize**
Devuelve o establece la posición y el tamaño de la forma.
```py
posicion_y_tamaño = dict(
Width = 5000,
Height = 2000,
X = 3000,
Y = 1000,
)
forma.possize = posicion_y_tamaño
```
<br>
### **properties**
Devuelve un diccionario con todas las propiedades de la forma.
```py
for p, v in forma.properties.items():
app.debug(f'{p} = {v}')
```
Al establecer solo aplica las que se pasen en un diccionario.
```py
propiedades = dict(
LineColor = app.color('red'),
LineWidth = 100,
)
forma.properties = propiedades
```
!!! tip "Atención"
Aquí hay que usar los nombres "originales" de las propiedades de la forma en el API de LibreOffice.
<br>
### **resize_with_cell**
Si el tamaño de la forma cambia con la celda, solo cuando el anclaje es a una celda.
```py
forma.resize_with_cell = True
```
<br>
### **shape_type**
**Solo lectura**. Devuelve el tipo de forma.
```py
app.debug(forma.shape_type)
```
```
24/04/2023 13:55:32 - DEBUG - com.sun.star.drawing.RectangleShape
```
<br>
### **width**
Ancho de la forma.
```py
forma.width = 6000
```
<br>
### **x**
Posición en el eje X.
```py
forma.x = 5000
```
<br>
### **y**
Posición en el eje Y.
```py
forma.y = 1000
```
## Métodos
---
### **clone**
Clona la imagen en la misma hoja.
```py
imagen.clone()
```
Clona la imagen en otra hoja.
```py
imagen.clone(doc['Hoja5'].draw_page)
```
<br>
### **remove**
Eliminar la forma.
```py
forma.remove()
```
<br>
### **save**
Guarda la imagen en disco. Si no se pasa ningún argumento, toma la ruta del documento y el nombre de la imagen.
!!! tip inline end "Atención"
El documento ya debe estar guardado
```py
imagen.save()
```
Podemos pasar una ruta diferente, seguira tomando el nombre de la imagen.
```py
ruta = '/home/elmau/imagenes'
imagen.save(ruta)
```
Podemos cambiar el nombre.
```py
ruta = '/home/elmau/imagenes'
name = f'{hoja.name}_nuevo_nombre'
imagen.save(path, name)
```
<br>

View File

@ -0,0 +1,155 @@
---
title: Índice
---
# Ejemplos reales de uso
---
## Calc
---
### Solicitar precios
Consultar datos en json a una página web y vaciarlos en una hoja.
=== "EasyMacro"
```py
import easymacro as app
def obtener_precios():
URL = 'https://api.binance.com/api/v3/ticker/price'
respuesta = app.url.get(URL)
if respuesta.status_code == 200:
datos = respuesta.json()
app.active.active['A1'].dict = datos
return
```
=== "Sin EasyMacro"
```py
import uno
import json
from urllib import request
def obtener_precios():
URL = 'https://api.binance.com/api/v3/ticker/price'
data = json.loads(request.urlopen(URL).read().decode())
doc = XSCRIPTCONTEXT.getDocument()
sheet = doc.CurrentController.ActiveSheet
sheet[0,0].String = 'symbol'
sheet[0,1].String = 'price'
for i, row in enumerate(data):
sheet[i + 1,0].String = row['symbol']
sheet[i + 1,1].Value = float(row['price'])
return
```
<br>
### Clonar imagenes
Clonar todas las imagenes de una hoja a otra.
=== "EasyMacro"
```py
doc = app.active
for image in doc[0].dp:
if image.is_image:
image.clone(doc[1].dp)
```
=== "Sin EasyMacro"
```py
import uno
import io
import unohelper
from com.sun.star.io import IOException, XOutputStream
from com.sun.star.beans import PropertyValue
CTX = uno.getComponentContext()
SM = CTX.getServiceManager()
class LOShape():
def __init__(self, shape):
self._shape = shape
class OutputStream(unohelper.Base, XOutputStream):
def __init__(self):
self._buffer = b''
self.closed = 0
@property
def buffer(self):
return self._buffer
def closeOutput(self):
self.closed = 1
def writeBytes(self, seq):
if seq.value:
self._buffer = seq.value
def flush(self):
pass
def clone_image(self, doc, to_sheet):
stream = self._shape.GraphicStream
buffer = self.OutputStream()
size, data = stream.readBytes(buffer, stream.available())
stream = SM.createInstanceWithContext('com.sun.star.io.SequenceInputStream', CTX)
stream.initialize((data,))
image = doc.createInstance('com.sun.star.drawing.GraphicObjectShape')
gp = SM.createInstance('com.sun.star.graphic.GraphicProvider')
properties = (PropertyValue(Name='InputStream', Value=stream),)
image.Graphic = gp.queryGraphic(properties)
to_sheet.DrawPage.add(image)
image.Size = self._shape.Size
image.Position = self._shape.Position
return
def clone_shape(self, doc, to_sheet):
for p in self._shape.CustomShapeGeometry:
if p.Name == 'Type':
type_shape = p.Value.title()
service = f'com.sun.star.drawing.{type_shape}Shape'
shape = doc.createInstance(service)
shape.Size = self._shape.Size
shape.Position = self._shape.Position
to_sheet.DrawPage.add(shape)
return
def main():
IMAGE = 'com.sun.star.drawing.GraphicObjectShape'
SHAPE = 'com.sun.star.drawing.CustomShape'
doc = XSCRIPTCONTEXT.getDocument()
source = doc.CurrentController.ActiveSheet
target = doc.Sheets[1]
for shape in source.DrawPage:
s = LOShape(shape)
if shape.ShapeType == IMAGE:
s.clone_image(doc, target)
elif shape.ShapeType == SHAPE:
s.clone_shape(doc, tar
```
<br>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
docs/es/docs/img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 B

View File

@ -24,9 +24,26 @@ nav:
- docs/index.md
- Propiedades: docs/properties.md
- Métodos: docs/methods.md
- Calc:
- calc/index.md
- Hojas: calc/sheets.md
- Rangos: calc/ranges.md
- Celdas: calc/cells.md
- Página de dibujo:
- dp/drawpage.md
- Formas: dp/shapes.md
- Cuadros de diálogo:
- dialogs/index.md
- Etiqueta: dialogs/label.md
- Botón de comando: dialogs/cmd.md
- Cuadro de texto: dialogs/text.md
- Hipervínculo: dialogs/link.md
- Imagen: dialogs/image.md
- Ejemplos de uso:
- examples/index.md
theme:
name: material
locale: es
locale: en
font: false
highlightjs: true
palette:
@ -45,12 +62,15 @@ markdown_extensions:
pygments_lang_class: true
- pymdownx.inlinehilite
- pymdownx.snippets
- pymdownx.superfences
#~ extra:
#~ alternate:
#~ - name: Español
#~ link: /
#~ lang: es
#~ - name: English
#~ link: /en/
#~ lang: en
- pymdownx.tabbed:
alternate_style: true
- tables
- pymdownx.keys
extra:
alternate:
- name: English
link: /easymacro
lang: en
- name: Español
link: /easymacro/langs/es
lang: es

View File

@ -1,13 +1,35 @@
#!/usr/bin/env python3
# ~ easymacro - for easily develop macros in LibreOffice
# ~ Copyright (C) 2020-2023 Mauricio Baeza (elmau)
# ~ This program is free software: you can redistribute it and/or modify it
# ~ under the terms of the GNU General Public License as published by the
# ~ Free Software Foundation, either version 3 of the License, or (at your
# ~ option) any later version.
# ~ This program is distributed in the hope that it will be useful, but
# ~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# ~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# ~ for more details.
# ~ You should have received a copy of the GNU General Public License along
# ~ with this program. If not, see <http://www.gnu.org/licenses/>.
from .easymain import *
from .easydialog import *
from .easytools import *
from .constants import *
from .easydocs import LODocuments
from .easydrawpage import LOGalleries
__version__ = '0.5.0'
__author__ = 'Mauricio Baeza (elMau)'
def __getattr__(name):
classes = {
'active': LODocuments().active,
@ -29,7 +51,6 @@ def __getattr__(name):
'menus': LOMenus(),
'paths': Paths,
'url': URL,
'selection': LODocuments().active.selection,
'set_config': set_app_config,
'shell': Shell,
'shortcuts': LOShortCuts(),

View File

@ -0,0 +1,24 @@
#!/usr/bin/env python
# ~ Is de sum of:
# ~ https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1sheet_1_1CellFlags.html
from com.sun.star.sheet import CellFlags
from .easyuno import BitmapMode, CellContentType, FillStyle, LineStyle
__all__ = [
'ALL',
'ONLY_DATA',
'BitmapMode',
'CellContentType',
'FillStyle',
'LineStyle',
]
# ~ VALUE, DATETIME, STRING, ANNOTATION, FORMULA
ONLY_DATA = 31
ALL = 1023
SECONDS_DAY = 86400

View File

@ -1,6 +1,61 @@
#!/usr/bin/env python3
from .easydoc import LODocument
from .easymain import BaseObject
class LOBaseSource(BaseObject):
_type = 'Base Source'
def __init__(self, obj):
super().__init__(obj)
def __str__(self):
return f'{self._type}:'
# ~ main_form = frm_container.component.getDrawPage.getforms.getbyindex(0)
class LOBaseForm(BaseObject):
_type = 'Base Form'
def __init__(self, obj):
super().__init__(obj)
def __str__(self):
return f'{self._type}: {self.name}'
@property
def name(self):
return self.obj.Name
def open(self):
self.obj.open()
return
def close(self):
return self.obj.close()
class LOBaseForms(BaseObject):
_type = 'Base Forms'
def __init__(self, obj):
super().__init__(obj)
def __str__(self):
return f'Base Forms'
def __len__(self):
"""Count forms"""
return self.obj.Count
def __contains__(self, item):
"""Contains"""
return item in self.obj
def __getitem__(self, index):
"""Index access"""
return LOBaseForm(self.obj[index])
class LOBase(LODocument):
@ -8,3 +63,10 @@ class LOBase(LODocument):
def __init__(self, obj):
super().__init__(obj)
def __str__(self):
return f'Base: {self.title}'
@property
def forms(self):
return LOBaseForms(self.obj.FormDocuments)

View File

@ -4,11 +4,8 @@ import datetime
from decimal import Decimal
from typing import Any, Union
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,
BaseObject, Color, LOMain,
dict_to_property, run_in_thread, set_properties
)
from .easydoc import LODocument
@ -16,83 +13,16 @@ from .easyevents import EventsRangeSelectionListener, LOEvents
from .easyshape import LOShapes, LOShape
from .easydrawpage import LODrawPage
from .easyforms import LOForms
from .easystyles import LOStyleFamilies
from .constants import ONLY_DATA, SECONDS_DAY, CellContentType
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)
# ~ IsFiltered,
# ~ IsManualPageBreak,
# ~ IsStartOfNewPage
class LOSheetRows():
def __init__(self, sheet, obj):
def __init__(self, sheet, obj, range_address):
self._sheet = sheet
self._obj = obj
self._ra = range_address
def __getitem__(self, index):
if isinstance(index, int):
@ -106,7 +36,8 @@ class LOSheetRows():
return self.obj.Count
def __str__(self):
return self.obj.Name
name = f'Rows: {self._ra.StartRow} to {self._ra.EndRow}'
return name
@property
def obj(self):
@ -119,6 +50,27 @@ class LOSheetRows():
def visible(self, value):
self._obj.IsVisible = value
@property
def is_filtered(self):
return self._obj.IsFiltered
@is_filtered.setter
def is_filtered(self, value):
self._obj.IsFiltered = value
@property
def is_manual_page_break(self):
return self._obj.IsManualPageBreak
@is_manual_page_break.setter
def is_manual_page_break(self, value):
self._obj.IsManualPageBreak = value
@property
def is_start_of_new_page(self):
return self._obj.IsStartOfNewPage
@is_start_of_new_page.setter
def is_start_of_new_page(self, value):
self._obj.IsStartOfNewPage = value
@property
def color(self):
return self.obj.CellBackColor
@ -239,6 +191,7 @@ class LOCalcRange():
def __init__(self, obj):
self._obj = obj
self._is_cell = obj.ImplementationName == self.CELL
self._shape = None
def __contains__(self, rango):
if isinstance(rango, LOCalcRange):
@ -274,9 +227,7 @@ class LOCalcRange():
pass
def __len__(self):
ra = self.range_address
rows = ra.EndRow - ra.StartRow + 1
return rows
return self.obj.Rows.Count
def __str__(self):
s = f'Range: {self.name}'
@ -303,6 +254,7 @@ class LOCalcRange():
@property
def is_range(self):
"""True if range is not cell"""
return not self._is_cell
@property
@ -433,11 +385,11 @@ class LOCalcRange():
def value(self):
"""Get or set value, automatically get type data"""
v = None
if self.type == VALUE:
if self.type == CellContentType.VALUE:
v = self.float
elif self.type == TEXT:
elif self.type == CellContentType.TEXT:
v = self.string
elif self.type == FORMULA:
elif self.type == CellContentType.FORMULA:
v = self.formula
return v
@value.setter
@ -523,6 +475,17 @@ class LOCalcRange():
col2 = ra.EndColumn + 1
return LOCalcRange(self.sheet[row1:row2, col1:col2].obj)
@property
def shape(self):
return self._shape
@shape.setter
def shape(self, shape):
shape.anchor = self
shape.resize_with_cell = True
shape.possize = self.possize
return
# ~ Fix
@property
def array_formula(self):
return self.obj.ArrayFormula
@ -535,12 +498,10 @@ class LOCalcRange():
cursor = self.obj.Spreadsheet.createCursorByRange(self.obj)
return cursor
# ~ To doc
@property
def position(self):
return self.obj.Position
# ~ To doc
@property
def size(self):
return self.obj.Size
@ -561,20 +522,19 @@ class LOCalcRange():
def y(self):
return self.obj.Position.Y
# ~ To doc
@property
def possize(self):
data = {
'Width': self.size.Width,
'Height': self.size.Height,
'X': self.position.X,
'Y': self.position.Y,
'Width': self.width,
'Height': self.height,
'X': self.x,
'Y': self.y,
}
return data
@property
def rows(self):
return LOSheetRows(self.sheet, self.obj.Rows)
return LOSheetRows(self.sheet, self.obj.Rows, self.range_address)
@property
def row(self):
@ -585,24 +545,47 @@ class LOCalcRange():
c2 = ra.EndColumn + 1
return LOCalcRange(self.sheet[r1:r2, c1:c2].obj)
def clear(self, what: int=ONLY_VALUES):
def clear(self, what: int=ONLY_DATA):
"""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):
"""Get cell offset"""
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):
"""Expande range to size"""
cursor = self.cursor
cursor.collapseToSize(cols, rows)
rango = self.obj.Spreadsheet[cursor.AbsoluteName]
return LOCalcRange(rango)
def len(self):
"""Get size of range in rows and columns"""
ra = self.range_address
rows = ra.EndRow - ra.StartRow + 1
cols = ra.EndColumn - ra.StartColumn + 1
return rows, cols
def select(self):
""" """
self.doc.select(self.obj)
return
def to_image(self):
""" """
self.select()
self.doc.copy()
args = {'SelectedFormat': 141}
url = 'ClipboardFormatItems'
LOMain.dispatch(self.doc.frame, url, args)
return self.sheet.shapes[-1]
class LOCalcSheet(BaseObject):
@ -623,10 +606,12 @@ class LOCalcSheet(BaseObject):
@property
def doc(self):
"""Get parent doc"""
return LOCalc(self.obj.DrawPage.Forms.Parent)
@property
def name(self):
"""Get name sheet"""
return self._obj.Name
@name.setter
def name(self, value):
@ -634,6 +619,7 @@ class LOCalcSheet(BaseObject):
@property
def code_name(self):
"""Get code name"""
return self._obj.CodeName
@code_name.setter
def code_name(self, value):
@ -641,6 +627,7 @@ class LOCalcSheet(BaseObject):
@property
def visible(self):
"""Get visible"""
return self._obj.IsVisible
@visible.setter
def visible(self, value):
@ -648,6 +635,7 @@ class LOCalcSheet(BaseObject):
@property
def color(self):
"""Get color tab"""
return self._obj.TabColor
@color.setter
def color(self, value):
@ -655,12 +643,14 @@ class LOCalcSheet(BaseObject):
@property
def used_area(self):
"""Get used area"""
cursor = self.create_cursor()
cursor.gotoEndOfUsedArea(True)
return self[cursor.AbsoluteName]
@property
def is_protected(self):
"""Get is protected"""
return self._obj.isProtected()
@property
@ -668,10 +658,12 @@ class LOCalcSheet(BaseObject):
return ''
@password.setter
def password(self, value):
"""Set password protect"""
self.obj.protect(value)
@property
def draw_page(self):
"""Get draw page"""
return LODrawPage(self.obj.DrawPage)
@property
def dp(self):
@ -686,6 +678,7 @@ class LOCalcSheet(BaseObject):
@property
def events(self):
"""Get events"""
return LOEvents(self.obj.Events)
@property
@ -701,6 +694,7 @@ class LOCalcSheet(BaseObject):
return
def unprotect(self, value):
"""Unproyect sheet"""
try:
self.obj.unprotect(value)
return True
@ -709,6 +703,7 @@ class LOCalcSheet(BaseObject):
return False
def move(self, pos: int=-1):
"""Move sheet"""
index = pos
if pos < 0:
index = len(self.doc)
@ -716,10 +711,12 @@ class LOCalcSheet(BaseObject):
return
def remove(self):
"""Auto remove"""
self.doc.remove(self.name)
return
def copy(self, new_name: str='', pos: int=-1):
"""Copy sheet"""
index = pos
if pos < 0:
index = len(self.doc)
@ -727,8 +724,7 @@ class LOCalcSheet(BaseObject):
return new_sheet
def copy_to(self, doc: Any, target: str='', pos: int=-1):
"""Copy sheet to other document.
"""
"""Copy sheet to other document."""
index = pos
if pos < 0:
index = len(doc)
@ -808,6 +804,10 @@ class LOCalc(LODocument):
"""Insert new sheet"""
self._sheets[key] = value
def __delitem__(self, key):
sheet = self._sheets[key]
self._sheets.removeByName(sheet.Name)
def __len__(self):
"""Count sheets"""
return self._sheets.Count
@ -891,6 +891,11 @@ class LOCalc(LODocument):
"""Get class events"""
return LOEvents(self.obj.Events)
@property
def styles(self):
ci = self.obj.createInstance
return LOStyleFamilies(self.obj.StyleFamilies, ci)
def ranges(self):
"""Create ranges container"""
obj = self._create_instance('com.sun.star.sheet.SheetCellRanges')
@ -909,7 +914,6 @@ class LOCalc(LODocument):
self._cc.select(obj)
return
@run_in_thread
def start_range_selection(self, controllers: Any, args: dict={}):
"""Start select range selection by user
@ -1064,5 +1068,4 @@ class LOCalc(LODocument):
return self.cell_styles
@property
def cell_styles(self):
obj = self.obj.StyleFamilies['CellStyles']
return LOCellStyles(obj, self)
return self.styles['CellStyles']

View File

@ -9,7 +9,7 @@ from .easydocs import LODocuments
from .easymain import (log, TITLE,
BaseObject, Color,
create_instance, set_properties)
from .easytools import _, LOInspect, Paths, Services
from .easytools import _, LOInspect, Paths, Services, debug, mri
__all__ = [
@ -18,16 +18,18 @@ __all__ = [
]
COLOR_ON_FOCUS = Color()('LightYellow')
# ~ COLOR_ON_FOCUS = Color()('LightYellow')
SEPARATION = 5
MODELS = {
'label': 'com.sun.star.awt.UnoControlFixedTextModel',
'text': 'com.sun.star.awt.UnoControlEditModel',
'button': 'com.sun.star.awt.UnoControlButtonModel',
'link': 'com.sun.star.awt.UnoControlFixedHyperlinkModel',
'image': 'com.sun.star.awt.UnoControlImageControlModel',
'pattern': 'com.sun.star.awt.UnoControlPatternFieldModel',
# ~ ToDo
'radio': 'com.sun.star.awt.UnoControlRadioButtonModel',
'checkbox': 'com.sun.star.awt.UnoControlCheckBoxModel',
'image': 'com.sun.star.awt.UnoControlImageControlModel',
'listbox': 'com.sun.star.awt.UnoControlListBoxModel',
'roadmap': 'com.sun.star.awt.UnoControlRoadmapModel',
'tree': 'com.sun.star.awt.tree.TreeControlModel',
@ -43,6 +45,7 @@ TYPE_CONTROL = {
'stardiv.Toolkit.UnoFixedTextControl': 'label',
'stardiv.Toolkit.UnoEditControl': 'text',
'stardiv.Toolkit.UnoButtonControl': 'button',
'stardiv.Toolkit.UnoImageControlControl': 'image',
}
IMPLEMENTATIONS = {
@ -50,6 +53,7 @@ IMPLEMENTATIONS = {
'link': 'stardiv.Toolkit.UnoFixedHyperlinkControl',
'roadmap': 'stardiv.Toolkit.UnoRoadmapControl',
'pages': 'stardiv.Toolkit.UnoMultiPageControl',
'text': 'stardiv.Toolkit.UnoEditControl',
}
@ -57,8 +61,9 @@ def add_listeners(events, control):
name = control.Model.Name
listeners = {
'addActionListener': EventsButton,
# ~ 'addMouseListener': EventsMouse,
# ~ 'addFocusListener': EventsFocus,
'addMouseListener': EventsMouse,
'addFocusListener': EventsFocus,
'addTextListener': EventsText,
# ~ 'addItemListener': EventsItem,
# ~ 'addKeyListener': EventsKey,
# ~ 'addTabListener': EventsTab,
@ -72,6 +77,9 @@ def add_listeners(events, control):
is_roadmap = control.ImplementationName == IMPLEMENTATIONS['roadmap']
is_pages = control.ImplementationName == IMPLEMENTATIONS['pages']
# ~ if control.ImplementationName == IMPLEMENTATIONS['text']:
# ~ mri(control)
for key, value in listeners.items():
if hasattr(control, key):
if is_grid and key == 'addMouseListener':
@ -84,7 +92,7 @@ def add_listeners(events, control):
control.addItemListener(EventsItemRoadmap(events, name))
continue
getattr(control, key)(listeners[key](events, name))
getattr(control, key)(value(events, name))
if is_grid:
controllers = EventsGrid(events, name)
@ -93,6 +101,37 @@ def add_listeners(events, control):
return
# ~ getAccessibleActionKeyBinding
class UnoActions():
ACTIONS = {
'press': 0,
'activate': 0,
}
def __init__(self, obj: Any):
self._obj = obj
self._ac = obj.AccessibleContext
self._actions = hasattr(self._ac, 'AccessibleActionCount')
def __str__(self):
return ', '.join(self.get())
def get(self):
actions = ()
if self._actions:
actions = [self._ac.getAccessibleActionDescription(i) for i in
range(self._ac.AccessibleActionCount)]
return actions
def press(self):
result = self._ac.doAccessibleAction(self.ACTIONS['press'])
return result
def activate(self):
result = self._ac.doAccessibleAction(self.ACTIONS['activate'])
return result
class UnoBaseObject(object):
def __init__(self, obj: Any):
@ -118,7 +157,8 @@ class UnoBaseObject(object):
@property
def properties(self):
data = LOInspect(self.obj).properties
properties = self.model.PropertySetInfo.Properties
data = {p.Name: getattr(self.model, p.Name) for p in properties}
return data
@properties.setter
def properties(self, properties: dict):
@ -308,6 +348,10 @@ class UnoBaseObject(object):
def ps(self, ps):
self.obj.setPosSize(ps.X, ps.Y, ps.Width, ps.Height, POSSIZE)
@property
def actions(self):
return UnoActions(self.obj)
def set_focus(self):
self.obj.setFocus()
return
@ -363,6 +407,41 @@ class UnoLabel(UnoBaseObject):
self.model.Label = value
class UnoLabelLink(UnoLabel):
def __init__(self, obj):
super().__init__(obj)
@property
def type(self):
return 'link'
@property
def value(self):
"""Get link"""
return self.model.URL
@value.setter
def value(self, value):
self.model.Label = value
self.model.URL = value
@property
def url(self):
"""Get link"""
return self.model.URL
@url.setter
def url(self, value):
self.model.URL = value
@property
def label(self):
"""Get label"""
return self.model.Label
@label.setter
def label(self, value):
self.model.Label = value
# ~ https://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1awt_1_1UnoControlEditModel.html
class UnoText(UnoBaseObject):
@ -418,14 +497,73 @@ class UnoButton(UnoBaseObject):
self.model.ImageURL = Paths.to_url(value)
class UnoImage(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
@property
def type(self):
return 'image'
@property
def value(self):
return self.model.ImageURL
@value.setter
def value(self, value):
if isinstance(value, str):
self.model.ImageURL = value
else:
self.model.Graphic = value._get_graphic()
class UnoPattern(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
@property
def type(self):
return 'pattern'
@property
def value(self):
return self.model.Text
@value.setter
def value(self, value):
self.model.Text = value
@property
def edit_mask(self):
return self.model.EditMask
@edit_mask.setter
def edit_mask(self, value):
self.model.EditMask = value
@property
def literal_mask(self):
return self.model.LiteralMask
@literal_mask.setter
def literal_mask(self, value):
self.model.LiteralMask = value
@property
def strict_format(self):
return self.model.StrictFormat
@strict_format.setter
def strict_format(self, value):
self.model.StrictFormat = value
UNO_CLASSES = {
'label': UnoLabel,
'text': UnoText,
'button': UnoButton,
# ~ 'link': UnoLabelLink,
'text': UnoText,
'link': UnoLabelLink,
'image': UnoImage,
'pattern': UnoPattern,
# ~ 'radio': UnoRadio,
# ~ 'checkbox': UnoCheckBox,
# ~ 'image': UnoImage,
# ~ 'listbox': UnoListBox,
# ~ 'roadmap': UnoRoadmap,
# ~ 'tree': UnoTree,
@ -441,10 +579,11 @@ class DialogBox(BaseObject):
SERVICE_DIALOG = 'com.sun.star.awt.UnoControlDialog'
def __init__(self, properties: dict={}):
self._controls = {}
obj = self._create(properties)
super().__init__(obj)
self._init_controls()
self._events = None
self._modal = False
def _create_from_path(self, path: str):
dp = create_instance(self.SERVICE, True)
@ -458,13 +597,16 @@ class DialogBox(BaseObject):
name = properties['Name']
library = properties.get('Library', 'Standard')
location = properties.get('Location', 'application').lower()
if location == 'user':
location = 'application'
url = f'vnd.sun.star.script:{library}.{name}?location={location}'
if location == 'document':
doc = LODocuments().active.obj
doc = properties.get('Document', LODocuments().active)
if hasattr(doc, 'obj'):
doc = doc.obj
dp = create_instance(self.SERVICE, arguments=doc)
else:
dp = create_instance(self.SERVICE, True)
@ -500,39 +642,30 @@ class DialogBox(BaseObject):
tipo = control.ImplementationName
name = control.Model.Name
if not tipo in TYPE_CONTROL:
log.debug(f'Type control: {tipo}')
debug(f'Type control: {tipo}')
raise AttributeError(f"Has no class '{tipo}'")
control = UNO_CLASSES[TYPE_CONTROL[tipo]](control)
setattr(self, name, control)
self._controls[name] = control
return
def execute(self):
return self.obj.execute()
def open(self):
def open(self, modal=False):
self._modal = modal
if modal:
self.visible = True
return
return self.execute()
def close(self, value=0):
value = self.obj.endDialog(value)
if self._modal:
self.visible = False
self.obj.dispose()
else:
value = self.obj.endDialog(value)
return value
# ~ def close(self, value=0):
# ~ if self._modal:
# ~ value = self.obj.endDialog(value)
# ~ else:
# ~ self.visible = False
# ~ self.obj.dispose()
# ~ return value
# ~ def show(self, modal=True):
# ~ self._modal = modal
# ~ if modal:
# ~ return self.obj.execute()
# ~ else:
# ~ self.visible = True
# ~ return
@property
def model(self):
return self.obj.Model
@ -576,6 +709,17 @@ class DialogBox(BaseObject):
def color_on_focus(self, value):
self._color_on_focus = get_color(value)
@property
def properties(self):
properties = self.model.PropertySetInfo.Properties
data = {}
for p in properties:
try:
data[p.Name] = getattr(self.model, p.Name)
except:
continue
return data
@property
def events(self):
return self._events
@ -597,13 +741,18 @@ class DialogBox(BaseObject):
return Paths.to_url(path)
def _special_properties(self, tipo, properties):
if tipo == 'link' and not 'Label' in properties:
if tipo == 'link' and 'URL' in properties and not 'Label' in properties:
properties['Label'] = properties['URL']
return properties
if tipo == 'label':
properties['VerticalAlign'] = properties.get('VerticalAlign', 1)
return properties
if tipo == 'button':
if 'ImageURL' in properties:
properties['ImageURL'] = self._set_image_url(properties['ImageURL'])
properties['ImageAlign'] = properties.get('ImageAlign', 0)
properties['FocusOnClick'] = properties.get('FocusOnClick', False)
return properties
@ -659,7 +808,7 @@ class DialogBox(BaseObject):
control.events = self.events
setattr(self, name, control)
self._controls[name] = control
return control

View File

@ -1,51 +1,54 @@
#!/usr/bin/env python3
import uno
import unohelper
from com.sun.star.io import IOException, XOutputStream
from .easymain import (log,
BaseObject, LOMain, Paths,
dict_to_property, create_instance
)
from .easyuno import IOStream
from .easyshape import LOShapes, LOShape
class IOStream(object):
"""Classe for input/output stream"""
class LOLayoutManager(BaseObject):
PR = 'private:resource/'
class OutputStream(unohelper.Base, XOutputStream):
def __init__(self, obj):
super().__init__(obj)
def __init__(self):
self._buffer = b''
self.closed = 0
def __getitem__(self, index):
""" """
name = index
if not name.startswith(self.PR):
name = self.PR + name
return self.obj.getElement(name)
@property
def buffer(self):
return self._buffer
@property
def visible(self):
""" """
return self.obj.isVisible()
@visible.setter
def visible(self, value):
self.obj.Visible = value
def closeOutput(self):
self.closed = 1
@property
def elements(self):
""" """
return [e.ResourceURL[17:] for e in self.obj.Elements]
def writeBytes(self, seq):
if seq.value:
self._buffer = seq.value
def show(self, name):
if not name.startswith(self.PR):
name = self.PR + name
self.obj.showElement(name)
return
def flush(self):
pass
def hide(self, name):
if not name.startswith(self.PR):
name = self.PR + name
self.obj.hideElement(name)
return
@classmethod
def buffer(cls):
return io.BytesIO()
@classmethod
def input(cls, buffer):
service = 'com.sun.star.io.SequenceInputStream'
stream = create_instance(service, True)
stream.initialize((uno.ByteSequence(buffer.getvalue()),))
return stream
@classmethod
def output(cls):
return cls.OutputStream()
# ~ def create(self, name):
# ~ return self.obj.createElement(name)
class LODocument(BaseObject):
@ -53,6 +56,7 @@ class LODocument(BaseObject):
def __init__(self, obj):
super().__init__(obj)
self._cc = obj.getCurrentController()
self._layout_manager = LOLayoutManager(self._cc.Frame.LayoutManager)
self._undo = True
def __enter__(self):
@ -149,6 +153,11 @@ class LODocument(BaseObject):
"""Get frame document"""
return self._cc.getFrame()
@property
def layout_manager(self):
""" """
return self._layout_manager
def _create_instance(self, name):
obj = self.obj.createInstance(name)
return obj
@ -233,6 +242,8 @@ class LODocument(BaseObject):
:type args: dict
:return: None if path or stream in memory
:rtype: bytes or None
https://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1document_1_1MediaDescriptor.html
"""
FILTERS = {
'xlsx': 'Calc MS Excel 2007 XML',
@ -244,8 +255,11 @@ class LODocument(BaseObject):
stream = None
path_target = 'private:stream'
if filter_name == 'svg':
filter_name = f'{self.type}_svg_Export'
else:
filter_name = FILTERS.get(filter_name, filter_name)
filter_name = FILTERS.get(filter_name, filter_name)
filter_data = dict_to_property(args, True)
filters = {
'FilterName': filter_name,
@ -261,7 +275,7 @@ class LODocument(BaseObject):
try:
self.obj.storeToURL(path_target, opt)
except Exception as e:
error(e)
log.error(e)
if not stream is None:
stream = stream.buffer
@ -279,6 +293,11 @@ class LODocument(BaseObject):
LOMain.dispatch(self.frame, 'Copy')
return
def cut(self):
"""Copy current selection"""
LOMain.dispatch(self.frame, 'Cut')
return
def paste(self):
"""Paste current content in clipboard"""
sc = create_instance('com.sun.star.datatransfer.clipboard.SystemClipboard')
@ -309,8 +328,25 @@ class LODocument(BaseObject):
self.obj.getUndoManager().clear()
return
def replace_ext(self, new_ext):
return Paths.with_suffix(self.path, new_ext)
def deselect(self):
LOMain.dispatch(self.frame, 'Deselect')
return
class LODrawImpress(LODocument):
def __init__(self, obj):
super().__init__(obj)
@property
def selection(self):
"""Get current selecction"""
sel = self.obj.CurrentSelection
if sel.Count == 1:
sel = LOShape(sel[0])
else:
sel = LOShapes(sel)
return sel

View File

@ -7,10 +7,13 @@ from .easywriter import LOWriter
from .easydraw import LODraw
from .easyimpress import LOImpress
from .easymath import LOMath
from .easybase import LOBase
from .easybase import LOBase, LOBaseSource
from .easyide import LOBasicIDE
DESKTOP = create_instance('com.sun.star.frame.Desktop', True)
class LODocuments():
"""Class for documents
"""
@ -37,7 +40,7 @@ class LODocuments():
# ~ BASE: 'com.sun.star.sdb.DocumentDataSource',
def __init__(self):
self._desktop = create_instance('com.sun.star.frame.Desktop', True)
self._desktop = DESKTOP
def __len__(self):
# ~ len(self._desktop.Components)
@ -88,10 +91,36 @@ class LODocuments():
@property
def active(self):
"""Get active doc"""
doc = self._desktop.getCurrentComponent()
obj = self._get_class_doc(doc)
active = self._desktop.getCurrentComponent()
obj = self._get_class_doc(active)
return obj
def is_registered(self, name):
dbc = create_instance('com.sun.star.sdb.DatabaseContext')
return dbc.hasRegisteredDatabase(name)
def _new_db(self, args: dict):
FIREBIRD = 'sdbc:embedded:firebird'
path = Paths(args.pop('Path'))
register = args.pop('Register', True)
name = args.pop('Name', '')
open_db = args.pop('Open', False)
if register and not name:
name = path.name
dbc = create_instance('com.sun.star.sdb.DatabaseContext')
db = dbc.createInstance()
db.URL = FIREBIRD
db.DatabaseDocument.storeAsURL(path.url, ())
if register:
dbc.registerDatabaseLocation(name, path.url)
if open_db:
return self.open(path.url, args)
return LOBaseSource(db)
def new(self, type_doc: str='calc', args: dict={}):
"""Create new document
@ -102,6 +131,9 @@ class LODocuments():
:return: New document
:rtype: Custom class
"""
if type_doc == 'base':
return self._new_db(args)
url = f'private:factory/s{type_doc}'
opt = dict_to_property(args)
doc = self._desktop.loadComponentFromURL(url, '_default', 0, opt)

View File

@ -2,6 +2,7 @@
from .easydoc import LODrawImpress
from .easydrawpage import LODrawPage
from .easymain import LOMain
class LODraw(LODrawImpress):
@ -17,3 +18,13 @@ class LODraw(LODrawImpress):
page = self.obj.DrawPages.getByName(index)
return LODrawPage(page)
@property
def active(self):
"""Get active page"""
return LODrawPage(self._cc.CurrentPage)
def paste(self):
"""Paste"""
LOMain.dispatch(self.frame, 'Paste')
return

View File

@ -1,5 +1,6 @@
#!/usr/bin/env python3
from pathlib import Path
from com.sun.star.awt import Size, Point
from .easymain import (
@ -14,13 +15,12 @@ from .easydoc import IOStream
DEFAULT_WH = 3000
DEFAULT_XY = 1000
TYPE_SHAPES = ('Line', 'Measure', 'Rectangle', 'Ellipse', 'Text', 'Connector',
'ClosedBezier', 'OpenBezier', 'PolyLine', 'PolyPolygon', 'ClosedFreeHand',
'OpenFreeHand')
# ~ class LOShapeBK(BaseObject):
# ~ @property
# ~ def cell(self):
# ~ return self.anchor
# ~ @property
# ~ def anchor(self):
# ~ obj = self.obj.Anchor
@ -145,6 +145,16 @@ class LODrawPage(BaseObject):
break
return result
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
def __len__(self):
"""Count shapes"""
return self.count
@property
def name(self):
return self.obj.Name
@ -172,14 +182,28 @@ class LODrawPage(BaseObject):
def _create_instance(self, name):
return self.doc.createInstance(name)
def select(self, shape):
if hasattr(shape, 'obj'):
shape = shape.obj
controller = self.doc.CurrentController
controller.select(shape)
return
def add(self, type_shape, options={}):
properties = options.copy()
"""Insert a shape in page, type shapes:
Line
Measure
Rectangle
Ellipse
Text
Connector
ClosedBezier
OpenBezier
PolyLine
PolyPolygon
ClosedFreeHand
OpenFreeHand
"""
index = self.count
default_height = DEFAULT_WH
@ -191,17 +215,22 @@ class LODrawPage(BaseObject):
y = properties.pop('Y', DEFAULT_XY)
name = properties.pop('Name', f'{type_shape.lower()}{index}')
service = f'com.sun.star.drawing.{type_shape}Shape'
if type_shape in TYPE_SHAPES:
service = f'com.sun.star.drawing.{type_shape}Shape'
else:
service = 'com.sun.star.drawing.CustomShape'
shape = self._create_instance(service)
shape.Size = Size(w, h)
shape.Position = Point(x, y)
shape.Name = name
self.obj.add(shape)
if not type_shape in TYPE_SHAPES:
shape.CustomShapeGeometry = dict_to_property({'Type': type_shape})
if properties:
set_properties(shape, properties)
# ~ return LOShape(self.obj[index], index)
return LOShape(self.obj[index])
def remove(self, shape):
@ -225,8 +254,9 @@ class LODrawPage(BaseObject):
image = self._create_instance('com.sun.star.drawing.GraphicObjectShape')
if isinstance(path, str):
image.GraphicURL = _P.to_url(path)
image.GraphicURL = Path(path).as_uri()
else:
# ~ URL = path
gp = create_instance('com.sun.star.graphic.GraphicProvider')
stream = IOStream.input(path)
properties = dict_to_property({'InputStream': stream})

View File

@ -5,14 +5,24 @@ import unohelper
from com.sun.star.awt import XActionListener
from com.sun.star.lang import XEventListener
from com.sun.star.awt import XFocusListener
from com.sun.star.util import XModifyListener
from com.sun.star.awt import XMouseListener
from com.sun.star.awt import XMouseMotionListener
from com.sun.star.awt import XTextListener
from com.sun.star.sheet import XRangeSelectionListener
from .easymain import Macro, dict_to_property
from .easymain import Macro, Color, dict_to_property
__all__ = [
'EventsButton',
'EventsFocus',
'EventsModify',
'EventsMouse',
'EventsMouseLink',
'EventsRangeSelectionListener',
'EventsText',
]
@ -80,15 +90,12 @@ class EventsRangeSelectionListener(EventsListenerBase, XRangeSelectionListener):
super().__init__(controller, '')
def done(self, event):
range_selection = event.RangeDescriptor
print(event)
event_name = 'range_selection_done'
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(range_selection)
getattr(self._controller, event_name)(event.RangeDescriptor)
return
def aborted(self, event):
range_selection = event.RangeDescriptor
event_name = 'range_selection_aborted'
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)()
@ -96,6 +103,9 @@ class EventsRangeSelectionListener(EventsListenerBase, XRangeSelectionListener):
class EventsButton(EventsListenerBase, XActionListener):
"""
See: https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1awt_1_1XActionListener.html
"""
def __init__(self, controller, name):
super().__init__(controller, name)
@ -105,3 +115,99 @@ class EventsButton(EventsListenerBase, XActionListener):
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
class EventsMouse(EventsListenerBase, XMouseListener, XMouseMotionListener):
def __init__(self, controller, name):
super().__init__(controller, name)
def mousePressed(self, event):
event_name = f'{self._name}_click'
if event.ClickCount == 2:
event_name = f'{self._name}_double_click'
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
def mouseReleased(self, event):
pass
def mouseEntered(self, event):
pass
def mouseExited(self, event):
pass
# ~ XMouseMotionListener
def mouseMoved(self, event):
event_name = f'{self._name}_mouse_moved'
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
def mouseDragged(self, event):
pass
class EventsMouseLink(EventsMouse):
def mouseEntered(self, event):
obj = event.Source.Model
obj.TextColor = Color()('blue')
return
def mouseExited(self, event):
obj = event.Source.Model
obj.TextColor = 0
return
class EventsFocus(EventsListenerBase, XFocusListener):
def __init__(self, controller, name):
super().__init__(controller, name)
def focusGained(self, event):
service = event.Source.Model.ImplementationName
if service == 'stardiv.Toolkit.UnoControlListBoxModel':
return
event_name = f'{self._name}_focus_gained'
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
# ~ obj = event.Source.ModelBackgroundColor = 16777184
return
def focusLost(self, event):
event_name = f'{self._name}_focus_lost'
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
# ~ event.Source.Model.BackgroundColor = -1
return
class EventsModify(EventsListenerBase, XModifyListener):
def __init__(self, controller, name):
super().__init__(controller, name)
def modified(self, event):
event_name = f'{self._name}_modified'
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
class EventsText(EventsListenerBase, XTextListener):
def __init__(self, controller, name):
super().__init__(controller, name)
def textChanged(self, event):
event_name = f'{self._name}_text_changed'
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return

View File

@ -24,13 +24,15 @@ from com.sun.star.beans import PropertyValue, NamedValue, StringPair
from com.sun.star.datatransfer import XTransferable, DataFlavor
from com.sun.star.ui.dialogs import TemplateDescription
# ~ from .constants import ALL
from .messages import MESSAGES
__all__ = [
'ALL',
'DESKTOP',
'INFO_DEBUG',
'IS_APPIMAGE',
'IS_FLATPAK',
'IS_MAC',
'IS_WIN',
'LANG',
@ -63,13 +65,12 @@ PC = platform.node()
USER = getpass.getuser()
IS_WIN = OS == 'Windows'
IS_MAC = OS == 'Darwin'
ALL = 1023
IS_FLATPAK = bool(os.getenv("FLATPAK_ID", ""))
IS_APPIMAGE = bool(os.getenv("APPIMAGE", ""))
LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
LOG_DATE = '%d/%m/%Y %H:%M:%S'
LOG_DATE = '%Y/%m/%d %H:%M:%S'
if IS_WIN:
logging.addLevelName(logging.ERROR, 'ERROR')
logging.addLevelName(logging.DEBUG, 'DEBUG')
@ -149,7 +150,6 @@ day = get_app_config(node, 'DD')
DATE_OFFSET = datetime.date(year, month, day).toordinal()
_info_debug = f"Python: {sys.version}\n\n{platform.platform()}\n\n" + '\n'.join(sys.path)
# ~ doc
INFO_DEBUG = f"{NAME} v{VERSION} {LANGUAGE}\n\n{_info_debug}"
@ -221,6 +221,16 @@ def set_properties(model, properties):
return
def get_properties(obj):
# ~ properties = obj.PropertySetInfo.Properties
# ~ values = {p.Name: getattr(obj, p.Name) for p in properties}
data = obj.PropertySetInfo.Properties
keys = [p.Name for p in data]
values = obj.getPropertyValues(keys)
properties = dict(zip(keys, values))
return properties
# ~ https://github.com/django/django/blob/main/django/utils/functional.py#L61
class classproperty:
@ -486,6 +496,7 @@ class LOMain():
"""Class for disable and enable commands
`See DispatchCommands <https://wiki.documentfoundation.org/Development/DispatchCommands>`_
https://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1configuration_1_1ConfigurationProvider.html
"""
@classmethod
def _set_app_command(cls, command: str, disable: bool) -> bool:
@ -500,6 +511,7 @@ class LOMain():
"""
NEW_NODE_NAME = f'zaz_disable_command_{command.lower()}'
name = 'com.sun.star.configuration.ConfigurationProvider'
# ~ name = 'com.sun.star.configuration.theDefaultProvider'
service = 'com.sun.star.configuration.ConfigurationUpdateAccess'
node_name = '/org.openoffice.Office.Commands/Execute/Disabled'
@ -507,9 +519,6 @@ class LOMain():
node = PropertyValue(Name='nodepath', Value=node_name)
update = cp.createInstanceWithArguments(service, (node,))
m = create_instance('mytools.Mri')
m.inspect(update)
result = True
try:
if disable:
@ -538,7 +547,7 @@ class LOMain():
return cls._set_app_command(command, True)
@classmethod
def enabled(cls, command) -> bool:
def enabled(cls, command: str) -> bool:
"""Enabled UNO command
:param command: UNO command to enabled
@ -600,13 +609,14 @@ class LOMain():
class ClipBoard(object):
SERVICE = 'com.sun.star.datatransfer.clipboard.SystemClipboard'
CLIPBOARD_FORMAT_TEXT = 'text/plain;charset=utf-16'
TEXT = 'text/plain;charset=utf-16'
IMAGE = 'application/x-openoffice-bitmap;windows_formatname="Bitmap"'
class TextTransferable(unohelper.Base, XTransferable):
def __init__(self, text):
df = DataFlavor()
df.MimeType = ClipBoard.CLIPBOARD_FORMAT_TEXT
df.MimeType = ClipBoard.TEXT
df.HumanPresentableName = 'encoded text utf-16'
self.flavors = (df,)
self._data = text
@ -632,7 +642,7 @@ class ClipBoard(object):
transferable = sc.getContents()
data = transferable.getTransferDataFlavors()
for df in data:
if df.MimeType == cls.CLIPBOARD_FORMAT_TEXT:
if df.MimeType == cls.TEXT:
break
if df:
text = transferable.getTransferData(df)
@ -841,6 +851,10 @@ class Paths(object):
"""
return Path(path).is_file()
@classmethod
def is_symlink(cls, path: str):
return Path(path).is_symlink()
@classmethod
def temp_file(self):
"""Make temporary file"""
@ -1042,10 +1056,13 @@ class Paths(object):
:return: Path with new extension
:rtype: str
"""
p = Paths(path)
name = f'{p.name}.{new_ext}'
path = cls.join(p.path, name)
return path
if not new_ext.startswith('.'):
new_ext = f'.{new_ext}'
return Path(path).with_suffix(new_ext)
@classmethod
def with_suffix(cls, path: str, new_ext: str):
return cls.replace_ext(path, new_ext)
@classmethod
def open(cls, path: str):

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,31 @@
#!/usr/bin/env python3
from typing import Any
from com.sun.star.awt import Size, Point
from .easymain import BaseObject, set_properties
from .easymain import (
BaseObject, Paths,
create_instance, dict_to_property, log, set_properties, get_properties
)
from .easyuno import (
BaseObjectProperties,
IOStream,
get_input_stream)
IMAGE = 'com.sun.star.drawing.GraphicObjectShape'
MIME_TYPE = {
'image/png': 'png',
'image/jpeg': 'jpg',
'image/svg': 'svg',
}
TYPE_MIME = {
'svg': 'image/svg',
'png': 'image/png',
'jpg': 'image/jpeg',
}
class LOShapes(object):
@ -41,38 +65,84 @@ class LOShapes(object):
return self._obj
class LOShape(BaseObject):
IMAGE = 'com.sun.star.drawing.GraphicObjectShape'
class LOShape(BaseObjectProperties):
def __init__(self, obj):
self._obj = obj
super().__init__(obj)
def __str__(self):
return f'Shape: {self.name}'
@property
def obj(self):
return self._obj
def sheet(self):
return self.anchor
@sheet.setter
def sheet(self, value):
self.anchor = value
@property
def cell(self):
return self.anchor
@cell.setter
def cell(self, value):
self.anchor = value
@property
def anchor(self):
from .easycalc import LOCalcSheet, LOCalcRange
TYPE_ANCHOR = {
'ScTableSheetObj': LOCalcSheet,
'ScCellObj': LOCalcRange,
}
"""Get anchor object"""
obj = self.obj.Anchor
implementation = obj.ImplementationName
if implementation in TYPE_ANCHOR:
obj = TYPE_ANCHOR[implementation](obj)
else:
log.debug(implementation)
return obj
@anchor.setter
def anchor(self, obj):
if hasattr(obj, 'obj'):
obj = obj.obj
self.obj.Anchor = obj
return
@property
def resize_with_cell(self):
"""If resize with cell"""
return self.obj.ResizeWithCell
@resize_with_cell.setter
def resize_with_cell(self, value):
self.obj.ResizeWithCell = value
return
@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
"""Get all properties"""
return get_properties(self.obj)
@properties.setter
def properties(self, values):
set_properties(self.obj, values)
@property
def shape_type(self):
"""Get type shape"""
return self.obj.ShapeType
@property
def type(self):
value = ''
if hasattr(self.obj, 'CustomShapeGeometry'):
for p in self.obj.CustomShapeGeometry:
if p.Name == 'Type':
value = p.Value
break
else:
value = self.shape_type
return value
@property
def name(self):
"""Get name"""
return self.obj.Name
@name.setter
def name(self, value):
@ -80,11 +150,11 @@ class LOShape(BaseObject):
@property
def is_image(self):
return self.shape_type == self.IMAGE
return self.shape_type == IMAGE
@property
def is_shape(self):
return self.shape_type != self.IMAGE
return self.shape_type != IMAGE
@property
def size(self):
@ -96,6 +166,7 @@ class LOShape(BaseObject):
@property
def width(self):
"""Width of shape"""
s = self.obj.Size
return s.Width
@width.setter
@ -106,6 +177,7 @@ class LOShape(BaseObject):
@property
def height(self):
"""Height of shape"""
s = self.obj.Size
return s.Height
@height.setter
@ -119,6 +191,7 @@ class LOShape(BaseObject):
return self.obj.Position
@property
def x(self):
"""Position X"""
return self.position.X
@x.setter
def x(self, value):
@ -126,6 +199,7 @@ class LOShape(BaseObject):
@property
def y(self):
"""Position Y"""
return self.position.Y
@y.setter
def y(self, value):
@ -181,11 +255,132 @@ class LOShape(BaseObject):
self.obj.LayerID = value
@property
def is_range(self):
return False
def mime_type(self):
mt = self.obj.GraphicURL.MimeType
mime_type = MIME_TYPE.get(mt, mt)
return mime_type
@property
def is_cell(self):
return False
def path(self):
return self.url
@property
def url(self):
if self.is_image:
url = Paths.to_system(self.obj.GraphicURL.OriginURL)
else:
url = self.obj.FillBitmapName
return url
@url.setter
def url(self, value):
self.obj.FillBitmapURL = Paths.to_url(value)
@property
def visible(self):
return self.obj.Visible
@visible.setter
def visible(self, value):
self.obj.Visible = value
@property
def doc(self):
return self.obj.Parent.Forms.Parent
@property
def text_box(self):
return self.obj.TextBox
@text_box.setter
def text_box(self, value):
self.obj.TextBox = value
@property
def text_box_content(self):
return self.obj.TextBoxContent
# ~ @property
# ~ def text_box_content(self):
# ~ controller = self.doc.CurrentController
# ~ vc = controller.ViewCursor
# ~ tbx = self.obj.TextBoxContent
# ~ vc.gotoRange(tbx.Start, False)
# ~ vc.gotoRange(tbx.End, True)
# ~ text = controller.getTransferable()
# ~ return text
def get_path(self, path: str='', name: str='', mime_type: str=''):
if not path:
path = Paths(self.doc.URL).path
if not name:
name = self.name.replace(' ', '_')
if mime_type:
file_name = f'{name}.{mime_type}'
else:
if self.is_image:
file_name = f'{name}.{self.mime_type}'
else:
file_name = f'{name}.svg'
path = Paths.join(path, file_name)
return path
def select(self):
controller = self.doc.CurrentController
controller.select(self.obj)
return
def remove(self):
"""Auto remove"""
self.obj.Parent.remove(self.obj)
return
def export(self, path: str=''):
if not path:
path = self.get_path()
mime_type = Paths(path).ext
options = {
'URL': Paths.to_url(path),
'MimeType': TYPE_MIME.get(mime_type, mime_type)}
args = dict_to_property(options)
export = create_instance('com.sun.star.drawing.GraphicExportFilter')
export.setSourceDocument(self.obj)
export.filter(args)
return path
def save(self, path: str=''):
"""Save image"""
if not path:
path = self.get_path()
if self.is_image:
data = IOStream.to_bin(self.obj.GraphicStream)
Paths.save_bin(path, data)
else:
self.export(path)
return path
def _get_graphic(self):
stream = self.obj.GraphicStream
buffer = IOStream.output()
_, data = stream.readBytes(buffer, stream.available())
stream = get_input_stream(data)
gp = create_instance('com.sun.star.graphic.GraphicProvider')
properties = dict_to_property({'InputStream': stream})
graphic = gp.queryGraphic(properties)
return graphic
def clone(self, draw_page: Any=None, x: int=1000, y: int=1000):
"""Clone image"""
image = self.doc.createInstance('com.sun.star.drawing.GraphicObjectShape')
image.Graphic = self._get_graphic()
if draw_page is None:
draw_page = self.obj.Parent
else:
if hasattr(draw_page, 'obj'):
draw_page = draw_page.obj
draw_page.add(image)
image.Size = self.size
position = self.position
position.X += x
position.Y += y
image.Position = position
return LOShape(image)

View File

@ -0,0 +1,143 @@
#!/usr/bin/env python
from .easymain import log, BaseObject, set_properties, get_properties
STYLE_FAMILIES = 'StyleFamilies'
class LOBaseStyles(BaseObject):
def __init__(self, obj, create_instance=None):
super().__init__(obj)
self._create_intance = create_instance
def __len__(self):
return self.obj.Count
def __contains__(self, item):
return self.obj.hasByName(item)
def __getitem__(self, index):
if index in self:
style = self.obj.getByName(index)
else:
raise IndexError
if self.NAME == STYLE_FAMILIES:
s = LOStyles(style, index, self._create_intance)
else:
s = LOStyle(style)
return s
def __iter__(self):
self._index = 0
return self
def __next__(self):
if self._index < self.obj.Count:
style = self[self.names[self._index]]
else:
raise StopIteration
self._index += 1
return style
@property
def names(self):
return self.obj.ElementNames
class LOStyle():
NAME = 'Style'
def __init__(self, obj):
self._obj = obj
def __str__(self):
return f'Style: {self.name}'
def __contains__(self, item):
return hasattr(self.obj, item)
def __setattr__(self, name, value):
if name != '_obj':
self.obj.setPropertyValue(name, value)
else:
super().__setattr__(name, value)
def __getattr__(self, name):
return self.obj.getPropertyValue(name)
@property
def obj(self):
return self._obj
@property
def name(self):
return self.obj.Name
@property
def is_in_use(self):
return self.obj.isInUse()
@property
def is_user_defined(self):
return self.obj.isUserDefined()
@property
def properties(self):
return get_properties(self.obj)
@properties.setter
def properties(self, values):
set_properties(self.obj, values)
class LOStyles(LOBaseStyles):
NAME = 'Styles'
def __init__(self, obj, type_style, create_instance):
super().__init__(obj)
self._type_style = type_style
self._create_instance = create_instance
def __str__(self):
return f'Styles: {self._type_style}'
def __setitem__(self, key, value):
if key in self:
style = self.obj.getByName(key)
else:
name = f'com.sun.star.style.{self._type_style[:-1]}'
style = self._create_instance(name)
self.obj.insertByName(key, style)
set_properties(style, value)
def __delitem__(self, key):
self.obj.removeByName(key)
class LOStyleFamilies(LOBaseStyles):
NAME = STYLE_FAMILIES
def __init__(self, obj, create_instance):
super().__init__(obj, create_instance)
def __str__(self):
return f'Style Families: {self.names}'
def _validate_name(self, name):
if not name.endswith('Styles'):
name = f'{name}Styles'
return name
def __contains__(self, item):
return self.obj.hasByName(self._validate_name(item))
def __getitem__(self, index):
if isinstance(index, int):
index = self.names[index]
else:
index = self._validate_name(index)
return super().__getitem__(index)

View File

@ -338,7 +338,7 @@ class LOInspect():
'STRUCT': '-Struct-',
}
def __init__(self, obj: Any, to_doc: bool=False):
def __init__(self, obj: Any, to_doc: bool=True):
"""Introspection objects pyUno
:param obj: Object to inspect
@ -381,7 +381,8 @@ class LOInspect():
sheet['A1'].data = self.interfaces
sheet = doc.insert('Services')
sheet['A1'].data = self.services
if self.services:
sheet['A1'].data = self.services
sheet = doc.insert('Listeners')
sheet['A1'].data = self.listeners

View File

@ -1,5 +1,10 @@
#!/usr/bin/env python3
import uno
import unohelper
from com.sun.star.io import IOException, XOutputStream
from .easymain import Paths, create_instance
# UNO Enum
class MessageBoxType():
@ -9,3 +14,103 @@ class MessageBoxType():
"""
from com.sun.star.awt.MessageBoxType \
import MESSAGEBOX, INFOBOX, WARNINGBOX, ERRORBOX, QUERYBOX
class LineStyle():
from com.sun.star.drawing.LineStyle import NONE, SOLID, DASH
class FillStyle():
from com.sun.star.drawing.FillStyle import NONE, SOLID, GRADIENT, HATCH, BITMAP
class BitmapMode():
from com.sun.star.drawing.BitmapMode import REPEAT, STRETCH, NO_REPEAT
class CellContentType():
from com.sun.star.table.CellContentType import EMPTY, VALUE, TEXT, FORMULA
class IOStream(object):
"""Classe for input/output stream"""
class OutputStream(unohelper.Base, XOutputStream):
def __init__(self):
self._buffer = b''
self.closed = 0
@property
def buffer(self):
return self._buffer
def closeOutput(self):
self.closed = 1
def writeBytes(self, seq):
if seq.value:
self._buffer = seq.value
def flush(self):
pass
@classmethod
def buffer(cls):
return io.BytesIO()
@classmethod
def input(cls, buffer):
service = 'com.sun.star.io.SequenceInputStream'
stream = create_instance(service, True)
stream.initialize((uno.ByteSequence(buffer.getvalue()),))
return stream
@classmethod
def output(cls):
return cls.OutputStream()
@classmethod
def to_bin(cls, stream):
buffer = cls.OutputStream()
_, data = stream.readBytes(buffer, stream.available())
return data.value
def get_input_stream(data):
stream = create_instance('com.sun.star.io.SequenceInputStream', True)
stream.initialize((data,))
return stream
class BaseObjectProperties():
def __init__(self, obj):
self._obj = obj
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
def __contains__(self, item):
return hasattr(self.obj, item)
def __setattr__(self, name, value):
if name == '_obj':
super().__setattr__(name, value)
else:
if name in self:
if name == 'FillBitmapURL':
value = Paths.to_url(value)
setattr(self.obj, name, value)
else:
object.__setattr__(self, name, value)
def __getattr__(self, name):
return self.obj.getPropertyValue(name)
@property
def obj(self):
return self._obj

View File

@ -1,11 +1,291 @@
#!/usr/bin/env python3
from typing import Any
from .easymain import log, BaseObject
from .easydoc import LODocument
from .easydrawpage import LODrawPage
from .easystyles import LOStyleFamilies
class LOTableRange(BaseObject):
def __init__(self, table, obj):
self._table = table
super().__init__(obj)
def __str__(self):
return f'TextTable: Range - {self.name}'
@property
def name(self):
if self.is_cell:
n = self.obj.CellName
else:
c1 = self.obj[0,0].CellName
c2 = self.obj[self.rows-1,self.columns-1].CellName
n = f'{c1}:{c2}'
return n
@property
def is_cell(self):
return hasattr(self.obj, 'CellName')
@property
def data(self):
return self.obj.getDataArray()
@data.setter
def data(self, values):
self.obj.setDataArray(values)
@property
def rows(self):
return len(self.data)
@property
def columns(self):
return len(self.data[0])
@property
def string(self):
return self.obj.String
@string.setter
def string(self, value):
self.obj.String = value
@property
def value(self):
return self.obj.Value
@value.setter
def value(self, value):
self.obj.Value = value
class LORow(BaseObject):
def __init__(self, rows, index):
self._rows = rows
self._index = index
super().__init__(rows[index])
def __str__(self):
return 'TextTable: Row'
@property
def height(self):
return self.obj.Height
@height.setter
def height(self, value):
self.obj.Height = value
def remove(self):
self._rows.removeByIndex(self._index, 1)
return
class LORows(BaseObject):
def __init__(self, obj):
super().__init__(obj)
def __str__(self):
return 'TextTable: Rows'
def __len__(self):
return self.obj.Count
def __getitem__(self, key):
return LORow(self.obj, key)
@property
def count(self):
return self.obj.Count
def remove(self, index, count=1):
self.obj.removeByIndex(index, count)
return
class LOTextTable(BaseObject):
def __init__(self, obj):
super().__init__(obj)
def __str__(self):
return f'Writer: TextTable - {self.name}'
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
def __iter__(self):
self._i = 0
return self
def __next__(self):
"""Interation cells"""
try:
name = self.obj.CellNames[self._i]
except IndexError:
raise StopIteration
self._i += 1
return self[name]
def __getitem__(self, key):
if isinstance(key, str):
if ':' in key:
rango = self.obj.getCellRangeByName(key)
else:
rango = self.obj.getCellByName(key)
elif isinstance(key, tuple):
if isinstance(key[0], slice):
rango = self.obj.getCellRangeByPosition(
key[1].start, key[0].start, key[1].stop-1, key[0].stop-1)
else:
rango = self.obj[key]
return LOTableRange(self.obj, rango)
@property
def name(self):
return self.obj.Name
@name.setter
def name(self, value):
self.obj.Name = value
@property
def rows(self):
return LORows(self.obj.Rows)
class LOTextTables(BaseObject):
def __init__(self, obj):
super().__init__(obj)
def __str__(self):
return 'Writer: TextTables'
def __getitem__(self, key):
return LOTextTable(self.obj[key])
class LOWriterTextPortion(BaseObject):
def __init__(self, obj):
super().__init__(obj)
def __str__(self):
return 'Writer: TextPortion'
@property
def string(self):
return self.obj.String
class LOWriterParagraph(BaseObject):
TEXT_PORTION = 'SwXTextPortion'
def __init__(self, obj):
super().__init__(obj)
def __str__(self):
return 'Writer: Paragraph'
def __iter__(self):
self._iter = iter(self.obj)
return self
def __next__(self):
obj = next(self._iter)
type_obj = obj.ImplementationName
if type_obj == self.TEXT_PORTION:
obj = LOWriterTextPortion(obj)
return obj
@property
def string(self):
return self.obj.String
@property
def cursor(self):
return self.obj.Text.createTextCursorByRange(self.obj)
class LOWriterTextRange(BaseObject):
PARAGRAPH = 'SwXParagraph'
def __init__(self, obj):
super().__init__(obj)
def __str__(self):
return 'Writer: TextRange'
def __getitem__(self, index):
for i, v in enumerate(self):
if index == i:
return v
if index > i:
raise IndexError
def __iter__(self):
self._enum = self.obj.createEnumeration()
return self
def __next__(self):
if self._enum.hasMoreElements():
obj = self._enum.nextElement()
type_obj = obj.ImplementationName
if type_obj == self.PARAGRAPH:
obj = LOWriterParagraph(obj)
else:
raise StopIteration
return obj
@property
def string(self):
return self.obj.String
@property
def cursor(self):
return self.obj.Text.createTextCursorByRange(self.obj)
class LOWriterTextRanges(BaseObject):
def __init__(self, obj):
super().__init__(obj)
# ~ self._doc = doc
# ~ self._paragraphs = [LOWriterTextRange(p, doc) for p in obj]
def __str__(self):
return 'Writer: TextRanges'
def __len__(self):
return self.obj.Count
def __getitem__(self, index):
return LOWriterTextRange(self.obj[index])
def __iter__(self):
self._index = 0
return self
def __next__(self):
try:
obj = LOWriterTextRange(self.obj[self._index])
except IndexError:
raise StopIteration
self._index += 1
return obj
class LOWriter(LODocument):
_type = 'writer'
TEXT_RANGES = 'SwXTextRanges'
_type = 'writer'
def __init__(self, obj):
super().__init__(obj)
@ -17,3 +297,61 @@ class LOWriter(LODocument):
@zoom.setter
def zoom(self, value):
self._view_settings.ZoomValue = value
@property
def selection(self):
"""Get current seleccion"""
sel = None
selection = self.obj.CurrentSelection
type_obj = selection.ImplementationName
if type_obj == self.TEXT_RANGES:
sel = LOWriterTextRanges(selection)
if len(sel) == 1:
sel = sel[0]
else:
log.debug(type_obj)
log.debug(selection)
sel = selection
return sel
@property
def string(self):
return self._obj.Text.String
@property
def styles(self):
ci = self.obj.createInstance
return LOStyleFamilies(self.obj.StyleFamilies, ci)
@property
def draw_page(self):
"""Get draw page"""
return LODrawPage(self.obj.DrawPage)
@property
def dp(self):
return self.draw_page
@property
def shapes(self):
return self.draw_page
@property
def cursor(self):
return self.obj.Text.createTextCursor()
@property
def view_cursor(self):
return self._cc.ViewCursor
@property
def tables(self):
return LOTextTables(self.obj.TextTables)
def select(self, rango: Any):
""""""
obj = rango
if hasattr(rango, 'obj'):
obj = rango.obj
self._cc.select(obj)
return

185
source/easymacro/utils.py Normal file
View File

@ -0,0 +1,185 @@
#!/usr/bin/env python
# ~ https://github.com/pyca/cryptography/blob/main/src/cryptography/fernet.py#L27
import base64
import binascii
import os
import time
import typing
_MAX_CLOCK_SKEW = 60
class InvalidSignature(Exception):
pass
class InvalidToken(Exception):
pass
class Fernet:
def __init__(
self,
key: bytes | str,
backend: typing.Any = None,
) -> None:
try:
key = base64.urlsafe_b64decode(key)
except binascii.Error as exc:
raise ValueError(
"Fernet key must be 32 url-safe base64-encoded bytes."
) from exc
if len(key) != 32:
raise ValueError(
"Fernet key must be 32 url-safe base64-encoded bytes."
)
self._signing_key = key[:16]
self._encryption_key = key[16:]
@classmethod
def generate_key(cls) -> bytes:
return base64.urlsafe_b64encode(os.urandom(32))
def encrypt(self, data: bytes) -> bytes:
return self.encrypt_at_time(data, int(time.time()))
def encrypt_at_time(self, data: bytes, current_time: int) -> bytes:
iv = os.urandom(16)
return self._encrypt_from_parts(data, current_time, iv)
def _encrypt_from_parts(
self, data: bytes, current_time: int, iv: bytes
) -> bytes:
utils._check_bytes("data", data)
padder = padding.PKCS7(algorithms.AES.block_size).padder()
padded_data = padder.update(data) + padder.finalize()
encryptor = Cipher(
algorithms.AES(self._encryption_key),
modes.CBC(iv),
).encryptor()
ciphertext = encryptor.update(padded_data) + encryptor.finalize()
basic_parts = (
b"\x80"
+ current_time.to_bytes(length=8, byteorder="big")
+ iv
+ ciphertext
)
h = HMAC(self._signing_key, hashes.SHA256())
h.update(basic_parts)
hmac = h.finalize()
return base64.urlsafe_b64encode(basic_parts + hmac)
def decrypt(self, token: bytes | str, ttl: int | None = None) -> bytes:
timestamp, data = Fernet._get_unverified_token_data(token)
if ttl is None:
time_info = None
else:
time_info = (ttl, int(time.time()))
return self._decrypt_data(data, timestamp, time_info)
def decrypt_at_time(
self, token: bytes | str, ttl: int, current_time: int
) -> bytes:
if ttl is None:
raise ValueError(
"decrypt_at_time() can only be used with a non-None ttl"
)
timestamp, data = Fernet._get_unverified_token_data(token)
return self._decrypt_data(data, timestamp, (ttl, current_time))
def extract_timestamp(self, token: bytes | str) -> int:
timestamp, data = Fernet._get_unverified_token_data(token)
# Verify the token was not tampered with.
self._verify_signature(data)
return timestamp
@staticmethod
def _get_unverified_token_data(token: bytes | str) -> tuple[int, bytes]:
if not isinstance(token, (str, bytes)):
raise TypeError("token must be bytes or str")
try:
data = base64.urlsafe_b64decode(token)
except (TypeError, binascii.Error):
raise InvalidToken
if not data or data[0] != 0x80:
raise InvalidToken
if len(data) < 9:
raise InvalidToken
timestamp = int.from_bytes(data[1:9], byteorder="big")
return timestamp, data
def _verify_signature(self, data: bytes) -> None:
h = HMAC(self._signing_key, hashes.SHA256())
h.update(data[:-32])
try:
h.verify(data[-32:])
except InvalidSignature:
raise InvalidToken
def _decrypt_data(
self,
data: bytes,
timestamp: int,
time_info: tuple[int, int] | None,
) -> bytes:
if time_info is not None:
ttl, current_time = time_info
if timestamp + ttl < current_time:
raise InvalidToken
if current_time + _MAX_CLOCK_SKEW < timestamp:
raise InvalidToken
self._verify_signature(data)
iv = data[9:25]
ciphertext = data[25:-32]
decryptor = Cipher(
algorithms.AES(self._encryption_key), modes.CBC(iv)
).decryptor()
plaintext_padded = decryptor.update(ciphertext)
try:
plaintext_padded += decryptor.finalize()
except ValueError:
raise InvalidToken
unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
unpadded = unpadder.update(plaintext_padded)
try:
unpadded += unpadder.finalize()
except ValueError:
raise InvalidToken
return unpadded

View File

@ -9,6 +9,6 @@ USER = 'elmau'
IS_WIN = False
IS_MAC = False
LIBO_VERSION = '7.4'
LIBO_VERSION = '7.6'
LANGUAGE = 'en-US'
LANG = 'en'