diff --git a/doc/content/es/application/_index.md b/doc/content/es/application/_index.md
new file mode 100644
index 0000000..fc08acb
--- /dev/null
+++ b/doc/content/es/application/_index.md
@@ -0,0 +1,10 @@
++++
+title = "LibreOffice"
+weight = 4
++++
+
+### create_instance
+
+
+```python
+```
diff --git a/doc/content/es/calc/_index.md b/doc/content/es/calc/_index.md
new file mode 100644
index 0000000..a9c1069
--- /dev/null
+++ b/doc/content/es/calc/_index.md
@@ -0,0 +1,12 @@
++++
+title = "Calc"
+weight = 5
++++
+
+#### Trabajar con Calc
+
+### active
+
+
+```python
+```
diff --git a/doc/content/es/dialog/_index.md b/doc/content/es/dialog/_index.md
new file mode 100644
index 0000000..7934e68
--- /dev/null
+++ b/doc/content/es/dialog/_index.md
@@ -0,0 +1,76 @@
++++
+title = "Cuadros de diálogo"
+weight = 6
++++
+
+#### Trabajar con cuadros de diálogo
+
+### create
+
+##### 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.
+
+```python
+def crear_cuadro_de_dialogo():
+ path = '/home/mau/Desktop/Dialog1.xdl'
+ propiedades = {'Path': path}
+
+ dialog = app.dialog.create(propiedades)
+ dialog.open()
+ return
+```
+
+
+##### Desde mis macros
+
+Si el cuadro de diálogo esta en la librería `Standard`.
+
+```python
+def crear_cuadro_de_dialogo():
+ propiedades = {
+ 'Location': 'user',
+ 'Name': 'Dialog1',
+ }
+ dialog = app.dialog.create(propiedades)
+ dialog.open()
+ return
+```
+
+Si el cuadro de diálogo esta en otra librería.
+
+```python
+def crear_cuadro_de_dialogo():
+ propiedades = {
+ 'Location': 'user',
+ 'Library': 'MiAplicacion',
+ 'Name': 'Dialog2',
+ }
+ dialog = app.dialog.create(propiedades)
+ dialog.open()
+ return
+```
+
+Si el cuadro de diálogo esta en el documento activo en la librería `Standard`.
+
+```python
+def crear_cuadro_de_dialogo():
+ propiedades = {
+ 'Location': 'document',
+ 'Name': 'miApp',
+ }
+ dialog = app.dialog.create(propiedades)
+ dialog.open()
+ return
+```
+
+##### Crear dinámicamente
+
+Crear un cuadro de diálogo vacío.
+
+```python
+def crear_cuadro_de_dialogo():
+ dialog = app.dialog.create()
+ dialog.open()
+ return
+```
diff --git a/doc/content/es/tools/_index.md b/doc/content/es/tools/_index.md
new file mode 100644
index 0000000..cd82b8d
--- /dev/null
+++ b/doc/content/es/tools/_index.md
@@ -0,0 +1,105 @@
++++
+title = "Herramientas"
+weight = 3
++++
+
+Recuerda, es necesario importar la librería.
+
+```python
+import easymacro as app
+```
+
+#### Información sobre la PC
+
+### OS
+
+Obtener el sistema operativo.
+
+```python
+app.msgbox(app.OS)
+```
+
+
+### DESKTOP
+
+Obtener el tipo de escritorio, solo en GNU/Linux.
+
+```python
+app.msgbox(app.DESKTOP)
+```
+
+
+### PC
+
+Obtener el nombre de la computadora.
+
+```python
+app.msgbox(app.PC)
+```
+
+
+### USER
+
+Obtener el nombre de usuario.
+
+```python
+app.msgbox(app.USER)
+```
+
+
+### IS_WIN
+
+Saber si estamos en Windows.
+
+```python
+app.msgbox(app.IS_WIN)
+```
+
+
+### IS_MAC
+
+Saber si estamos en OSX.
+
+```python
+app.msgbox(app.IS_MAC)
+```
+
+#### Información sobre LibreOffice
+
+### NAME
+
+Nombre de la aplicación.
+
+```python
+app.msgbox(app.NAME)
+```
+
+
+### VERSION
+
+Versión de la aplicación.
+
+```python
+app.msgbox(app.VERSION)
+```
+
+
+### LANG
+
+Lenguaje de la aplicación.
+
+```python
+app.msgbox(app.LANG)
+```
+
+
+### LANGUAGE
+
+Lenguaje con variante.
+
+```python
+app.msgbox(app.LANGUAGE)
+```
+
+
+[1]: https://git.cuates.net/elmau/easymacro/issues
diff --git a/doc/content/es/tools/dates_and_time/_index.md b/doc/content/es/tools/dates_and_time/_index.md
new file mode 100644
index 0000000..5d45bbc
--- /dev/null
+++ b/doc/content/es/tools/dates_and_time/_index.md
@@ -0,0 +1,153 @@
++++
+title = "Fechas y tiempo"
+weight = 2
++++
+
+
+{{% notice tip %}}
+La fecha inicial en Calc y en Python es diferente.
+{{% /notice %}}
+
+
+### today
+
+Obtener la fecha de hoy.
+
+```python
+d = app.dates
+app.msgbox(d.today)
+```
+
+
+### now
+
+Obtener la fecha y hora actuales.
+
+```python
+d = app.dates
+app.msgbox(d.now)
+```
+
+
+### time
+
+Obtener la hora actual.
+
+```python
+d = app.dates
+app.msgbox(d.now.time())
+```
+
+
+### epoch
+
+Obtener el [tiempo Unix][1]
+
+```python
+d = app.dates
+app.msgbox(d.epoch)
+```
+
+
+### date
+
+Devolver una fecha
+
+```python
+d = app.dates
+
+date = d.date(1974, 1, 15)
+app.msgbox(date)
+```
+
+
+### time
+
+Devolver un tiempo
+
+```python
+d = app.dates
+
+time = d.time(10, 20, 15)
+app.msgbox(time)
+```
+
+
+### datetime
+
+Devolver fecha y hora
+
+```python
+d = app.dates
+
+dt = d.datetime(1974, 1, 15, 10, 11, 12)
+app.msgbox(dt)
+```
+
+### str_to_date
+
+Convertir una cadena en fecha. Mira este [excelente recurso][2]
+
+```python
+d = app.dates
+
+cadena = '1974-01-15'
+plantilla = '%Y-%m-%d'
+fecha = d.str_to_date(cadena, plantilla)
+app.msgbox(fecha)
+app.msgbox(type(fecha))
+```
+
+Para obtener un valor válido para establecer en una celda de Calc.
+
+```python
+d = app.dates
+
+cadena = '1974-01-15'
+plantilla = '%Y-%m-%d'
+fecha = d.str_to_date(cadena, plantilla, True)
+app.msgbox(fecha)
+app.msgbox(type(fecha))
+```
+
+### calc_to_date
+
+Convierte el valor de una celda en una fecha Python, por ejemplo, la fecha inicial configurada en Calc.
+
+```python
+d = app.dates
+
+value_from_cell = 1
+fecha = d.calc_to_date(value_from_cell)
+app.msgbox(fecha)
+app.msgbox(type(fecha))
+```
+
+
+### start y end
+
+Medir tiempo en segundos.
+
+```python
+d = app.dates
+
+d.start()
+app.sleep(5)
+seconds = d.end()
+app.msgbox(seconds)
+```
+
+Regresar timedelta en vez de segundos.
+
+```python
+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
diff --git a/doc/content/es/tools/email/_index.md b/doc/content/es/tools/email/_index.md
new file mode 100644
index 0000000..e5dbdc8
--- /dev/null
+++ b/doc/content/es/tools/email/_index.md
@@ -0,0 +1,18 @@
++++
+title = "Correo electrónico"
+weight = 3
++++
+
+#### Enviar correo electrónico.
+
+{{% notice tip %}}
+Siempre guarda las contraseñas de la forma más segura posible.
+{{% /notice %}}
+
+### send
+
+Enviar un correo electrónico.
+
+```python
+
+```
diff --git a/doc/content/es/tools/messages/_index.md b/doc/content/es/tools/messages/_index.md
new file mode 100644
index 0000000..c4496f5
--- /dev/null
+++ b/doc/content/es/tools/messages/_index.md
@@ -0,0 +1,69 @@
++++
+title = "Mensajes"
+weight = 1
++++
+
+#### Cuadros de mensaje
+
+### msgbox
+
+Mostrar mensaje estandar.
+
+```python
+mensaje = 'Maldito Mundo'
+titulo = 'Mi Macro'
+app.msgbox(mensaje, titulo)
+```
+
+
+### warning
+
+Mostrar mensaje con icono de advertencia.
+
+```python
+mensaje = 'Cuidado, esta acción es peligrosa'
+titulo = 'Mi Macro'
+app.warning(mensaje, titulo)
+```
+
+
+### errorbox
+
+Mostrar mensaje con icono de error.
+
+```python
+mensaje = 'ERROR: contacte a soporte'
+titulo = 'Mi Macro'
+app.errorbox(mensaje, titulo)
+```
+
+
+### question
+
+Hacer una pregunta mostrando el icono de interrogación y mostrando los botones de comando `Si` y `No`. La respuesta siempre es verdadera (`True`) si el usuario selecciona `si` y falsa (`False`) en caso contrario.
+
+```python
+mensaje = '¿Es fácil Python?'
+titulo = 'Mi Macro'
+resultado = app.question(mensaje, titulo)
+app.msgbox(resultado)
+```
+
+### inputbox
+
+Muestra un mensaje al usuario, permitiendo capturar una respuesta.
+
+```python
+mensaje = 'Escribe tu nombre'
+nombre = app.inputbox(mensaje)
+app.msgbox(nombre)
+```
+
+Para ocultar solo en pantalla lo que captura el usuario, como contraseñas.
+
+```python
+mensaje = 'Captura la contraseña'
+echochar = '*'
+contraseña = app.inputbox(mensaje, echochar=echochar)
+app.msgbox(contraseña)
+```
diff --git a/doc/content/es/tools/paths/_index.md b/doc/content/es/tools/paths/_index.md
new file mode 100644
index 0000000..419ec49
--- /dev/null
+++ b/doc/content/es/tools/paths/_index.md
@@ -0,0 +1,569 @@
++++
+title = "Rutas y archivos"
+weight = 2
++++
+
+#### Trabajar con rutas y archivos.
+
+### path
+
+Obtener información de una ruta.
+
+```python
+ruta_archivo = '/home/mau/mi_archivo.ods'
+p = app.paths(ruta_archivo)
+
+app.debug(p.path)
+app.debug(p.file_name)
+app.debug(p.name)
+app.debug(p.ext)
+app.debug(p.size)
+app.debug(p.url)
+```
+
+```
+15/08/2022 14:40:08 - DEBUG - /home/mau
+15/08/2022 14:40:08 - DEBUG - mi_archivo.ods
+15/08/2022 14:40:08 - DEBUG - mi_archivo
+15/08/2022 14:40:08 - DEBUG - ods
+15/08/2022 14:40:08 - DEBUG - 7011
+15/08/2022 14:40:08 - DEBUG - file:///home/mau/mi_archivo.ods
+```
+
+Obtener la misma información como un tupla.
+
+```python
+ruta_archivo = '/home/mau/mi_archivo.ods'
+p = app.paths(ruta_archivo)
+app.debug(p.info)
+```
+
+```
+15/08/2022 14:43:01 - DEBUG - ('/home/mau', 'mi_archivo.ods', 'mi_archivo', 'ods', 7011, 'file:///home/mau/mi_archivo.ods')
+```
+
+O como diccionario.
+
+```python
+ruta_archivo = '/home/mau/mi_archivo.ods'
+p = app.paths(ruta_archivo)
+app.debug(p.dict)
+```
+
+```
+15/08/2022 14:43:01 - DEBUG - {'path': '/home/mau', 'file_name': 'mi_archivo.ods', 'name': 'mi_archivo', 'ext': 'ods', 'size': 7011, 'url': 'file:///home/mau/mi_archivo.ods'}
+```
+
+
+### home
+
+Obtener la carpeta de inicio del usuario.
+
+```python
+p = app.paths
+app.debug(p.home)
+```
+
+
+### documents
+
+Obtener la carpeta Documentos del usuario.
+
+```python
+p = app.paths
+app.debug(p.documents)
+```
+
+
+### user_profile
+
+Obtener la ruta del perfil de usuario.
+
+```python
+p = app.paths
+app.debug(p.user_profile)
+```
+
+
+### user_config
+
+Obtener la ruta de la carpeta `config` en el perfil de usuario.
+
+```python
+p = app.paths
+app.debug(p.user_config)
+```
+
+
+### python
+
+Obtener la ruta del ejecutable `python`
+
+```python
+p = app.paths
+app.debug(p.python)
+```
+
+
+### to_system
+
+Pasar una ruta en formato URL al formato del sistema de archivos del SO.
+
+```python
+p = app.paths
+ruta_url = 'file:///home/mau/mi_archivo.ods'
+ruta = p.to_system(ruta_url)
+app.debug(ruta)
+```
+
+
+### to_url
+
+Pasar una ruta del sistema de archivos del SO al formato URL.
+
+```python
+p = app.paths
+ruta = '/home/mau/mi_archivo.ods'
+ruta_url = p.to_url(ruta)
+app.debug(ruta_url)
+```
+
+### config
+
+Obtener rutas de la configuración de LibreOffice. Por default obtiene la ruta de `Documentos`, para otras ruta mire [Api XPathSettings][1]
+
+```python
+p = app.paths
+ruta = p.config()
+app.debug(ruta)
+
+ruta = p.config('UserConfig')
+app.debug(ruta)
+```
+
+{{% notice tip %}}
+Algunas rutas pueden ser más de una, separados por `;`, en este caso, el resultado es una lista con las rutas.
+{{% /notice %}}
+
+
+### join
+
+Concatenar rutas.
+
+```python
+p = app.paths
+ruta = p.join('/home/mau', 'pruebas', 'archivo.ods')
+app.debug(ruta)
+```
+
+
+### exists
+
+Verificar si una ruta existe.
+
+```python
+p = app.paths
+resultado = p.exists('/home/mau/test/archivo.ods')
+app.debug(resultado)
+```
+
+### exists_app
+
+Verificar si una aplicación existe.
+
+```python
+p = app.paths
+
+resultado = p.exists_app('noexiste')
+app.debug(resultado)
+
+resultado = p.exists_app('soffice')
+app.debug(resultado)
+```
+
+
+### is_dir
+
+Verificar si la ruta es un directorio.
+
+```python
+p = app.paths
+resultado = p.is_dir('/home/mau')
+app.debug(resultado)
+```
+
+
+### is_file
+
+Verificar si la ruta es un archivo.
+
+```python
+p = app.paths
+resultado = p.is_file('/home/mau/mi_archivo.ods')
+app.debug(resultado)
+```
+
+
+### temp_file
+
+Crear un archivo temporal, es borrado automáticamente al cerrarse.
+
+```python
+p = app.paths
+
+f = p.temp_file()
+f.write(app.INFO_DEBUG)
+f.close()
+```
+
+Usado en `contexto`, se cierra automáticamente.
+
+```python
+with p.temp_file() as f:
+ app.debug(f.name)
+ f.write('test')
+```
+
+### temp_dir
+
+Crear un directorio temporal, al salir del contexto, es eliminado junto con todo su contenido del sistema de archivos.
+
+```python
+p = app.paths
+
+with p.temp_dir() as d:
+ app.debug(p.exists(d))
+ app.debug(d)
+
+app.debug(p.exists(d))
+```
+
+### get
+
+Permitir al usuario seleccionar archivos, devuelve solo la ruta. De forma predeterminada abre en la carpeta `Documentos`.
+
+```python
+p = app.paths
+ruta = p.get()
+app.debug(ruta)
+```
+
+Establecer que abra en otro directorio.
+
+```python
+ruta = p.get('/tmp')
+app.debug(ruta)
+```
+
+Agregar un filtro.
+
+```python
+ruta = p.get(filters='ods')
+app.debug(ruta)
+```
+
+Agregar multiples filtros.
+
+```python
+ruta = p.get(filters='xml,txt')
+app.debug(ruta)
+```
+
+Permitir selecciones multiples.
+
+```python
+ruta = p.get(filters='xml,txt', multiple=True)
+app.debug(ruta)
+```
+
+
+### get_dir
+
+Permitir al usuario seleccionar un directorio, devuelve solo la ruta. De forma predeterminada abre en la carpeta `Documentos`.
+
+```python
+p = app.paths
+ruta = p.get_dir()
+app.debug(ruta)
+```
+
+Establecer que abra en otro directorio.
+
+```python
+ruta = p.get_dir('/tmp')
+app.debug(ruta)
+```
+
+### get_for_save
+
+Permitir seleccionar un directorio y capturar el nombre del archivo, devuelve la ruta completa.
+
+```python
+p = app.paths
+ruta = p.get_for_save()
+app.debug(ruta)
+```
+
+Establecer que inicie en otro directorio.
+
+```python
+ruta = p.get_for_save('/tmp')
+app.debug(ruta)
+```
+
+Si se establece un filtro, automáticamente se agrega la extensión.
+
+```python
+ruta = p.get_for_save(filters='txt')
+app.debug(ruta)
+```
+
+
+### files
+
+Obtener archivos de una ruta, no recursivamente. De forma predeterminada regresa todos, incluyendo archivos ocultos.
+
+```python
+ruta = '/home/mau'
+
+p = app.paths
+archivos = p.files(ruta)
+for a in archivos:
+ app.debug(a)
+```
+
+Filtrar por tipo de archivo.
+
+```python
+p = app.paths
+archivos = p.files(ruta, '*.pdf')
+```
+
+Obtener archivos de forma recursiva.
+
+```python
+p = app.paths
+archivos = p.files(ruta, '**/*.xml')
+```
+
+
+### walk
+
+Obtener archivos de forma recursiva.
+
+```python
+p = app.paths
+ruta = '/home/mau/Documents'
+archivos = p.walk(ruta)
+for a in archivos:
+ app.debug(a)
+```
+
+Con filtro.
+
+```python
+archivos = p.walk(ruta, 'ods')
+```
+
+Con filtros.
+
+```python
+archivos = p.walk(ruta, 'ods|odt')
+```
+
+
+### dirs
+
+Obtener los directorios de una ruta, no recursivamente.
+
+```python
+p = app.paths
+ruta = '/home/mau/Documents'
+folders = p.dirs(ruta)
+for f in folders:
+ app.debug(f)
+```
+
+
+### walk_dirs
+
+Obtener los directorios de una ruta, no recursivamente.
+
+```python
+p = app.paths
+ruta = '/home/mau/Documents'
+folders = p.walk_dirs(ruta)
+for f in folders:
+ app.debug(f)
+```
+
+Obtener información en una lista de tuplas: (ID_FOLDER, ID_PADRE, NOMBRE)
+
+```python
+folders = p.walk_dirs(ruta, True)
+```
+
+
+### extension
+
+Obtener la ruta de instalación de una extensión a partir de su ID.
+
+```python
+p = app.paths
+id_ext = 'net.elmau.zaz.talk'
+ruta = p.extension(id_ext)
+app.debug(ruta)
+```
+
+### replace_ext
+
+Reemplazar extensión de un archivo.
+
+```python
+p = app.paths
+ruta = '/home/mau/mi_archivo.ods'
+ruta_nueva = p.replace_ext(ruta, 'pdf')
+app.debug(ruta_nueva)
+```
+
+
+### open
+
+Abrir cualquier archivo con el programas predeterminado del SO.
+
+```python
+p = app.paths
+ruta = '/home/mau/archivo.pdf'
+p.open(ruta)
+
+ruta = '/home/mau/index.html'
+p.open(ruta)
+```
+
+### save y read
+
+Guardar y leer datos, el encoding predeterminado es UTF8.
+
+```python
+p = app.paths
+datos = """¿Quieres saber quién eres? No preguntes. Actúa.
+La acción te delineará y definirá.
+
+Thomas Jefferson
+"""
+
+ruta = '/home/mau/temp.txt'
+p.save(ruta, datos)
+
+datos = p.read(ruta)
+app.msgbox(datos)
+```
+
+Cambiar el encoding.
+
+```python
+app.paths.save(ruta, datos, 'iso-8859-1')
+```
+
+
+### save_bin y read_bin
+
+Guardar y leer datos binarios.
+
+```python
+p = app.paths
+datos = b'Datos binarios'
+
+ruta = '/home/mau/temp.bin'
+p.save_bin(ruta, datos)
+
+datos = p.read_bin(ruta)
+app.msgbox(datos)
+```
+
+
+### save_json y read_json
+
+Guardar y leer en formato json.
+
+```python
+p = app.paths
+
+ruta = '/home/mau/datos.json'
+datos = {
+ 'tipo': 'calc',
+ 'nombre': 'miarchivo.ods',
+}
+p.save_json(ruta, datos)
+
+datos = p.read_json(ruta)
+app.msgbox(datos)
+```
+
+
+### save_csv y read_csv
+
+Exportar e importar datos en formato CSV. Vea la documentación [CSV][2] para los argumentos que puede usar.
+
+```python
+p = app.paths
+
+ruta = '/home/mau/datos.csv'
+ahora = app.dates.now
+datos = (
+ (1, 'uno', ahora),
+ (2, 'dos', ahora),
+ (3, 'tres', ahora),
+)
+p.save_csv(ruta, datos)
+
+datos = p.read_csv(ruta)
+app.msgbox(datos)
+```
+
+
+### kill
+
+Borra archivos o directorios con todo su contenido. Devuelve verdadero (True) en caso de exito y falso (Falso) en cualquier otro caso.
+
+{{% notice warning %}}
+Este proceso es destructivo, es buena practica siempre solicitar confirmación al usuario.
+{{% /notice %}}
+
+```python
+p = app.paths
+
+ruta = '/home/mau/temp.bin'
+resultado = p.kill(ruta)
+app.msgbox(resultado)
+```
+
+
+### copy
+
+Copiar archivos. Devuelve la ruta completa final destino.
+
+De un origen a una carpeta destino
+
+```python
+p = app.paths
+
+ruta_origen = '/home/mau/temp.txt'
+ruta_destino = '/home/mau/Desktop'
+ruta = p.copy(ruta_origen, ruta_destino)
+app.debug(ruta)
+```
+
+Cambiar el nombre destino.
+
+```python
+ruta_origen = '/home/mau/temp.txt'
+ruta_destino = '/home/mau/Desktop'
+nuevo_nombre = 'datos.csv'
+ruta = p.copy(ruta_origen, ruta_destino, nuevo_nombre)
+app.debug(ruta)
+```
+
+
+[1]: http://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1util_1_1XPathSettings.html
+[2]: https://docs.python.org/3.7/library/csv.html
diff --git a/doc/content/es/tools/tools_01.png b/doc/content/es/tools/tools_01.png
new file mode 100644
index 0000000..6395bb4
Binary files /dev/null and b/doc/content/es/tools/tools_01.png differ
diff --git a/source/easymacro (copy).bk1 b/source/easymacro (copy).bk1
deleted file mode 100644
index fb1519f..0000000
--- a/source/easymacro (copy).bk1
+++ /dev/null
@@ -1,4332 +0,0 @@
-#!/usr/bin/env python3
-
-# == Rapid Develop Macros in LibreOffice ==
-
-# ~ https://git.cuates.net/elmau/easymacro
-
-# ~ easymacro 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.
-
-# ~ easymacro 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 easymacro. If not, see .
-
-
-import csv
-import datetime
-import getpass
-import hashlib
-import io
-import json
-import logging
-import os
-import platform
-import re
-import shlex
-import shutil
-import socket
-import ssl
-import subprocess
-import sys
-import tempfile
-import threading
-import time
-import traceback
-
-from functools import wraps
-from pathlib import Path
-from pprint import pprint
-from string import Template
-from typing import Any, Union
-
-from socket import timeout
-from urllib import parse
-from urllib.request import Request, urlopen
-from urllib.error import URLError, HTTPError
-
-import mailbox
-import smtplib
-from smtplib import SMTPException, SMTPAuthenticationError
-from email.mime.multipart import MIMEMultipart
-from email.mime.base import MIMEBase
-from email.mime.text import MIMEText
-from email.utils import formatdate
-from email import encoders
-
-import uno
-import unohelper
-from com.sun.star.awt import Rectangle, Size, Point
-from com.sun.star.awt import Key, KeyEvent, KeyModifier
-from com.sun.star.awt import MessageBoxButtons as MSG_BUTTONS
-from com.sun.star.awt.MessageBoxResults import YES
-from com.sun.star.beans import PropertyValue, NamedValue
-from com.sun.star.beans.PropertyConcept import ALL
-from com.sun.star.datatransfer import XTransferable, DataFlavor
-from com.sun.star.io import IOException, XOutputStream
-from com.sun.star.ui.dialogs import TemplateDescription
-
-from com.sun.star.sheet import XRangeSelectionListener
-from com.sun.star.lang import XEventListener
-
-from com.sun.star.container import NoSuchElementException
-
-# Global variables
-OS = platform.system()
-DESKTOP = os.environ.get('DESKTOP_SESSION', '')
-PC = platform.node()
-USER = getpass.getuser()
-IS_WIN = OS == 'Windows'
-IS_MAC = OS == 'Darwin'
-
-
-LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
-LOG_DATE = '%d/%m/%Y %H:%M:%S'
-if IS_WIN:
- logging.addLevelName(logging.ERROR, 'ERROR')
- logging.addLevelName(logging.DEBUG, 'DEBUG')
- logging.addLevelName(logging.INFO, 'INFO')
-else:
- logging.addLevelName(logging.ERROR, '\033[1;41mERROR\033[1;0m')
- logging.addLevelName(logging.DEBUG, '\x1b[33mDEBUG\033[1;0m')
- logging.addLevelName(logging.INFO, '\x1b[32mINFO\033[1;0m')
-logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT, datefmt=LOG_DATE)
-log = logging.getLogger(__name__)
-
-
-_info_debug = f"Python: {sys.version}\n\n{platform.platform()}\n\n" + '\n'.join(sys.path)
-
-TIMEOUT = 10
-SALT = b'00a1bfb05353bb3fd8e7aa7fe5efdccc'
-
-_EVENTS = {}
-PYTHON = 'python'
-if IS_WIN:
- PYTHON = 'python.exe'
-
-FILES = {
- 'CONFIG': 'zaz-{}.json',
-}
-DIRS = {}
-
-MESSAGES = {
- 'es': {
- 'OK': 'Aceptar',
- 'Cancel': 'Cancelar',
- 'Select path': 'Seleccionar ruta',
- 'Select directory': 'Seleccionar directorio',
- 'Select file': 'Seleccionar archivo',
- 'Incorrect user or password': 'Nombre de usuario o contraseña inválidos',
- 'Allow less secure apps in GMail': 'Activa: Permitir aplicaciones menos segura en GMail',
- }
-}
-
-
-CTX = uno.getComponentContext()
-SM = CTX.getServiceManager()
-
-
-# UNO Enum
-class MessageBoxType():
- """Class for import enum
-
- `See Api MessageBoxType `_
- """
- from com.sun.star.awt.MessageBoxType \
- import MESSAGEBOX, INFOBOX, WARNINGBOX, ERRORBOX, QUERYBOX
-MBT = MessageBoxType
-
-
-def create_instance(name: str, with_context: bool=False, argument: Any=None) -> Any:
- """Create a service instance
-
- :param name: Name of service
- :type name: str
- :param with_context: If used context
- :type with_context: bool
- :param argument: If needed some argument
- :type argument: Any
- :return: PyUno instance
- :rtype: PyUno Object
- """
-
- if with_context:
- instance = SM.createInstanceWithContext(name, CTX)
- elif argument:
- instance = SM.createInstanceWithArguments(name, (argument,))
- else:
- instance = SM.createInstance(name)
-
- return instance
-
-
-def get_app_config(node_name: str, key: str='') -> Any:
- """Get any key from any node from LibreOffice configuration.
-
- :param node_name: Name of node
- :type name: str
- :param key: Name of key
- :type key: str
- :return: Any value
- :rtype: Any
-
- `See Api ConfigurationProvider `_
- """
-
- name = 'com.sun.star.configuration.ConfigurationProvider'
- service = 'com.sun.star.configuration.ConfigurationAccess'
- cp = create_instance(name, True)
- node = PropertyValue(Name='nodepath', Value=node_name)
- value = ''
-
- try:
- value = cp.createInstanceWithArguments(service, (node,))
- if value and value.hasByName(key):
- value = value.getPropertyValue(key)
- except Exception as e:
- error(e)
- value = ''
-
- return value
-
-
-# Get info LibO
-NAME = TITLE = get_app_config('org.openoffice.Setup/Product', 'ooName')
-VERSION = get_app_config('org.openoffice.Setup/Product','ooSetupVersion')
-LANGUAGE = get_app_config('org.openoffice.Setup/L10N/', 'ooLocale')
-LANG = LANGUAGE.split('-')[0]
-
-INFO_DEBUG = f"{NAME} v{VERSION} {LANGUAGE}\n\n{_info_debug}"
-
-# Get start date from Calc configuration
-node = '/org.openoffice.Office.Calc/Calculate/Other/Date'
-year = get_app_config(node, 'YY')
-month = get_app_config(node, 'MM')
-day = get_app_config(node, 'DD')
-DATE_OFFSET = datetime.date(year, month, day).toordinal()
-
-
-def _(msg):
- if LANG == 'en':
- return msg
-
- if not LANG in MESSAGES:
- return msg
-
- return MESSAGES[LANG][msg]
-
-
-def set_app_config(node_name: str, key: str, new_value: Any) -> Any:
- """Update value for key in node name.
-
- :param node_name: Name of node
- :type name: str
- :param key: Name of key
- :type key: str
- :return: True if update sucesfully
- :rtype: bool
-
- `See Api ConfigurationUpdateAccess `_
- """
- result = True
- current_value = ''
- name = 'com.sun.star.configuration.ConfigurationProvider'
- service = 'com.sun.star.configuration.ConfigurationUpdateAccess'
- cp = create_instance(name, True)
- node = PropertyValue(Name='nodepath', Value=node_name)
- update = cp.createInstanceWithArguments(service, (node,))
-
- try:
- current_value = update.getPropertyValue(key)
- update.setPropertyValue(key, new_value)
- update.commitChanges()
- except Exception as e:
- error(e)
- if update.hasByName(key) and current_value:
- update.setPropertyValue(key, current_value)
- update.commitChanges()
- result = False
-
- return result
-
-
-def debug(*messages) -> None:
- """Show messages debug
-
- :param messages: List of messages to debug
- :type messages: list[Any]
- """
-
- data = [str(m) for m in messages]
- log.debug('\t'.join(data))
- return
-
-
-def error(message: Any) -> None:
- """Show message error
-
- :param message: The message error
- :type message: Any
- """
-
- log.error(message)
- return
-
-
-def info(*messages) -> None:
- """Show messages info
-
- :param messages: List of messages to debug
- :type messages: list[Any]
- """
-
- data = [str(m) for m in messages]
- log.info('\t'.join(data))
- return
-
-
-def save_log(path: str, data: Any) -> None:
- """Save data in file, data append to end and automatic add current time.
-
- :param path: Path to save log
- :type path: str
- :param data: Data to save in file log
- :type data: Any
- """
-
- with open(path, 'a') as f:
- f.write(f'{str(now())[:19]} - ')
- pprint(data, stream=f)
- return
-
-
-def mri(obj: Any) -> None:
- """Inspect object with MRI Extension
-
- :param obj: Any pyUno object
- :type obj: Any
-
- `See MRI `_
- """
-
- m = create_instance('mytools.Mri')
- if m is None:
- msg = 'Extension MRI not found'
- error(msg)
- return
-
- if hasattr(obj, 'obj'):
- obj = obj.obj
- m.inspect(obj)
- return
-
-
-def catch_exception(f):
- """Catch exception for any function
-
- :param f: Any Python function
- :type f: Function instance
- """
-
- @wraps(f)
- def func(*args, **kwargs):
- try:
- return f(*args, **kwargs)
- except Exception as e:
- name = f.__name__
- if IS_WIN:
- msgbox(traceback.format_exc())
- log.error(name, exc_info=True)
- return func
-
-
-def msgbox(message: Any, title: str=TITLE, buttons=MSG_BUTTONS.BUTTONS_OK, \
- type_message_box=MessageBoxType.INFOBOX) -> int:
- """Create message box
-
- :param message: Any type message, all is converted to string.
- :type message: Any
- :param title: The title for message box
- :type title: str
- :param buttons: A combination of `com::sun::star::awt::MessageBoxButtons `_
- :type buttons: long
- :param type_message_box: The `message box type `_
- :type type_message_box: enum
- :return: `MessageBoxResult `_
- :rtype: int
-
- `See Api XMessageBoxFactory `_
- """
-
- toolkit = create_instance('com.sun.star.awt.Toolkit')
- parent = toolkit.getDesktopWindow()
- box = toolkit.createMessageBox(parent, type_message_box, buttons, title, str(message))
- return box.execute()
-
-
-def question(message: str, title: str=TITLE) -> bool:
- """Create message box question, show buttons YES and NO
-
- :param message: Message question
- :type message: str
- :param title: The title for message box
- :type title: str
- :return: True if user click YES and False if click NO
- :rtype: bool
- """
-
- result = msgbox(message, title, MSG_BUTTONS.BUTTONS_YES_NO, MessageBoxType.QUERYBOX)
- return result == YES
-
-
-def warning(message: Any, title: str=TITLE) -> int:
- """Create message box with icon warning
-
- :param message: Any type message, all is converted to string.
- :type message: Any
- :param title: The title for message box
- :type title: str
- :return: MessageBoxResult
- :rtype: int
- """
- return msgbox(message, title, type_message_box=MessageBoxType.WARNINGBOX)
-
-
-def errorbox(message: Any, title: str=TITLE) -> int:
- """Create message box with icon error
-
- :param message: Any type message, all is converted to string.
- :type message: Any
- :param title: The title for message box
- :type title: str
- :return: MessageBoxResult
- :rtype: int
- """
- return msgbox(message, title, type_message_box=MessageBoxType.ERRORBOX)
-
-
-def sleep(seconds: int):
- """Sleep
- """
- time.sleep(seconds)
- return
-
-
-def run_in_thread(fn):
- """Run any function in thread
-
- :param fn: Any Python function (macro)
- :type fn: Function instance
- """
- def run(*k, **kw):
- t = threading.Thread(target=fn, args=k, kwargs=kw)
- t.start()
- return t
- return run
-
-
-def dict_to_property(values: dict, uno_any: bool=False):
- """Convert dictionary to array of PropertyValue
-
- :param values: Dictionary of values
- :type values: dict
- :param uno_any: If return like array uno.Any
- :type uno_any: bool
- :return: Tuple of PropertyValue or array uno.Any
- :rtype: tuples or uno.Any
- """
- ps = tuple([PropertyValue(Name=n, Value=v) for n, v in values.items()])
- if uno_any:
- ps = uno.Any('[]com.sun.star.beans.PropertyValue', ps)
- return ps
-
-
-def _property_to_dict(values):
- d = {v.Name: v.Value for v in values}
- return d
-
-
-def data_to_dict(data) -> dict:
- """Convert tuples, list, PropertyValue, NamedValue to dictionary
-
- :param data: Dictionary of values
- :type data: array of tuples, list, PropertyValue or NamedValue
- :return: Dictionary
- :rtype: dict
- """
- d = {}
- if not isinstance(data, (tuple, list)):
- return d
-
- if isinstance(data[0], (tuple, list)):
- d = {r[0]: r[1] for r in data}
- elif isinstance(data[0], (PropertyValue, NamedValue)):
- d = _property_to_dict(data)
-
- return d
-
-
-def render(template, data):
- s = Template(template)
- return s.safe_substitute(**data)
-
-
-def _set_properties(model, properties):
- # ~ if 'X' in properties:
- # ~ properties['PositionX'] = properties.pop('X')
- # ~ if 'Y' in properties:
- # ~ properties['PositionY'] = properties.pop('Y')
- keys = tuple(properties.keys())
- values = tuple(properties.values())
- model.setPropertyValues(keys, values)
- return
-
-
-# Classes
-
-class LOInspect():
- """Classe inspect
-
- Inspired by `MRI `_
- """
- TYPE_CLASSES = {
- 'INTERFACE': '-Interface-',
- 'SEQUENCE': '-Sequence-',
- 'STRUCT': '-Struct-',
- }
-
- def __init__(self, obj: Any, to_doc: bool=False):
- """Introspection objects pyUno
-
- :param obj: Object to inspect
- :type obj: Any pyUno
- :param to_doc: If show info in new doc Calc
- :type to_doc: bool
- """
- self._obj = obj
- if hasattr(obj, 'obj'):
- self._obj = obj.obj
- self._properties = ()
- self._methods = ()
- self._interfaces = ()
- self._services = ()
- self._listeners = ()
-
- introspection = create_instance('com.sun.star.beans.Introspection')
- result = introspection.inspect(self._obj)
- if result:
- self._properties = self._get_properties(result)
- self._methods = self._get_methods(result)
- self._interfaces = self._get_interfaces(result)
- self._services = self._get_services(self._obj)
- self._listeners = self._get_listeners(result)
- self._to_doc(to_doc)
-
- def _to_doc(self, to_doc: bool):
- if not to_doc:
- return
-
- doc = LODocuments().new()
- sheet = doc[0]
- sheet.name = 'Properties'
- sheet['A1'].data = self.properties
-
- sheet = doc.insert('Methods')
- sheet['A1'].data = self.methods
-
- sheet = doc.insert('Interfaces')
- sheet['A1'].data = self.interfaces
-
- sheet = doc.insert('Services')
- sheet['A1'].data = self.services
-
- sheet = doc.insert('Listeners')
- sheet['A1'].data = self.listeners
-
- return
-
- def _get_value(self, p: Any):
- type_class = p.Type.typeClass.value
- if type_class in self.TYPE_CLASSES:
- return self.TYPE_CLASSES[type_class]
-
- value = ''
- try:
- value = getattr(self._obj, p.Name)
- if type_class == 'ENUM' and value:
- value = value.value
- elif type_class == 'TYPE':
- value = value.typeName
- elif value is None:
- value = '-void-'
- except:
- value = '-error-'
-
- return str(value)
-
- def _get_attributes(self, a: Any):
- PA = {1 : 'Maybe Void', 16 : 'Read Only'}
- attr = ', '.join([PA.get(k, '') for k in PA.keys() if a & k])
- return attr
-
- def _get_property(self, p: Any):
- name = p.Name
- tipo = p.Type.typeName
- value = self._get_value(p)
- attr = self._get_attributes(p.Attributes)
- return name, tipo, value, attr
-
- def _get_properties(self, result: Any):
- properties = result.getProperties(ALL)
- data = [('Name', 'Type', 'Value', 'Attributes')]
- data += [self._get_property(p) for p in properties]
- return data
-
- def _get_arguments(self, m: Any):
- arguments = '( {} )'.format(', '.join(
- [' '.join((
- f'[{p.aMode.value.lower()}]',
- p.aName,
- p.aType.Name)) for p in m.ParameterInfos]
- ))
- return arguments
-
- def _get_method(self, m: Any):
- name = m.Name
- arguments = self._get_arguments(m)
- return_type = m.ReturnType.Name
- class_name = m.DeclaringClass.Name
- return name, arguments, return_type, class_name
-
- def _get_methods(self, result: Any):
- methods = result.getMethods(ALL)
- data = [('Name', 'Arguments', 'Return Type', 'Class')]
- data += [self._get_method(m) for m in methods]
- return data
-
- def _get_interfaces(self, result: Any):
- methods = result.getMethods(ALL)
- interfaces = {m.DeclaringClass.Name for m in methods}
- return tuple(zip(interfaces))
-
- def _get_services(self, obj: Any):
- try:
- data = [str(s) for s in obj.getSupportedServiceNames()]
- data = tuple(zip(data))
- except:
- data = ()
- return data
-
- def _get_listeners(self, result: Any):
- data = [l.typeName for l in result.getSupportedListeners()]
- return tuple(zip(data))
-
- @property
- def properties(self):
- return self._properties
-
- @property
- def methods(self):
- return self._methods
-
- @property
- def interfaces(self):
- return self._interfaces
-
- @property
- def services(self):
- return self._services
-
- @property
- def listeners(self):
- return self._listeners
-
-
-# ~ https://github.com/django/django/blob/main/django/utils/functional.py#L61
-class classproperty:
-
- def __init__(self, method=None):
- self.fget = method
-
- def __get__(self, instance, cls=None):
- return self.fget(cls)
-
- def getter(self, method):
- self.fget = method
- return self
-
-
-class Dates(object):
- """Class for datetimes
- """
- _start = None
-
- @classproperty
- def now(cls):
- """Current local date and time
-
- :return: Return the current local date and time
- :rtype: datetime
- """
- return datetime.datetime.now().replace(microsecond=0)
-
- @classproperty
- def today(cls):
- """Current local date
-
- :return: Return the current local date
- :rtype: date
- """
- return datetime.date.today()
-
- @classproperty
- def time(cls):
- """Current local time
-
- :return: Return the current local time
- :rtype: datetime.time
- """
- t = cls.now.time().replace(microsecond=0)
- return t
-
- @classproperty
- def epoch(cls):
- """Get unix time
-
- :return: Return unix time
- :rtype: int
-
- `See Unix Time `_
- """
- n = cls.now
- e = int(time.mktime(n.timetuple()))
- return e
-
- @classmethod
- def date(cls, year: int, month: int, day: int):
- """Get date from year, month, day
-
- :param year: Year of date
- :type year: int
- :param month: Month of date
- :type month: int
- :param day: Day of day
- :type day: int
- :return: Return the date
- :rtype: date
-
- `See Python date `_
- """
- d = datetime.date(year, month, day)
- return d
-
- @classmethod
- def str_to_date(cls, str_date: str, template: str, to_calc: bool=False):
- """Get date from string
-
- :param str_date: Date in string
- :type str_date: str
- :param template: Formato of date string
- :type template: str
- :param to_calc: If date is for used in Calc cell
- :type to_calc: bool
- :return: Return date or int if used in Calc
- :rtype: date or int
-
- `See Python strptime `_
- """
- d = datetime.datetime.strptime(str_date, template).date()
- if to_calc:
- d = d.toordinal() - DATE_OFFSET
- return d
-
- @classmethod
- def calc_to_date(cls, value: float):
- """Get date from calc value
-
- :param value: Float value from cell
- :type value: float
- :return: Return the current local date
- :rtype: date
-
- `See Python fromordinal `_
- """
- d = datetime.date.fromordinal(int(value) + DATE_OFFSET)
- return d
-
- @classmethod
- def start(cls):
- """Start counter
- """
- cls._start = cls.now
- info('Start: ', cls._start)
- return
-
- @classmethod
- def end(cls, get_seconds: bool=True):
- """End counter
-
- :param get_seconds: If return value in total seconds
- :type get_seconds: bool
- :return: Return the timedelta or total seconds
- :rtype: timedelta or int
- """
- e = cls.now
- td = e - cls._start
- result = str(td)
- if get_seconds:
- result = td.total_seconds()
- info('End: ', e)
- return result
-
-
-class Json(object):
- """Class for json data
- """
-
- @classmethod
- def dumps(cls, data: Any) -> str:
- """Dumps
-
- :param data: Any data
- :type data: Any
- :return: Return string json
- :rtype: str
- """
- return json.dumps(data, indent=4, sort_keys=True)
-
- @classmethod
- def loads(cls, data: str) -> Any:
- """Loads
-
- :param data: String data
- :type data: str
- :return: Return any object
- :rtype: Any
- """
- return json.loads(data)
-
-
-class Macro(object):
- """Class for call macro
-
- `See Scripting Framework `_
- """
- @classmethod
- def call(cls, args: dict, in_thread: bool=False):
- """Call any macro
-
- :param args: Dictionary with macro location
- :type args: dict
- :param in_thread: If execute in thread
- :type in_thread: bool
- :return: Return None or result of call macro
- :rtype: Any
- """
-
- result = None
- if in_thread:
- t = threading.Thread(target=cls._call, args=(args,))
- t.start()
- else:
- result = cls._call(args)
- return result
-
- @classmethod
- def get_url_script(cls, args: dict):
- library = args['library']
- name = args['name']
- language = args.get('language', 'Python')
- location = args.get('location', 'user')
- module = args.get('module', '.')
-
- if language == 'Python':
- module = '.py$'
- elif language == 'Basic':
- module = f".{module}."
- if location == 'user':
- location = 'application'
-
- url = 'vnd.sun.star.script'
- url = f'{url}:{library}{module}{name}?language={language}&location={location}'
- return url
-
- @classmethod
- def _call(cls, args: dict):
- url = cls.get_url_script(args)
- args = args.get('args', ())
-
- service = 'com.sun.star.script.provider.MasterScriptProviderFactory'
- factory = create_instance(service)
- script = factory.createScriptProvider('').getScript(url)
- result = script.invoke(args, None, None)[0]
-
- return result
-
-
-class Shell(object):
- """Class for subprocess
-
- `See Subprocess `_
- """
- @classmethod
- def run(cls, command, capture=False, split=False):
- """Execute commands
-
- :param command: Command to run
- :type command: str
- :param capture: If capture result of command
- :type capture: bool
- :param split: Some commands need split.
- :type split: bool
- :return: Result of command
- :rtype: Any
- """
- if split:
- cmd = shlex.split(command)
- result = subprocess.run(cmd, capture_output=capture, text=True, shell=IS_WIN)
- if capture:
- result = result.stdout
- else:
- result = result.returncode
- else:
- if capture:
- result = subprocess.check_output(command, shell=True).decode()
- else:
- result = subprocess.Popen(command)
- return result
-
- @classmethod
- def popen(cls, command):
- """Execute commands and return line by line
-
- :param command: Command to run
- :type command: str
- :return: Result of command
- :rtype: Any
- """
- try:
- proc = subprocess.Popen(shlex.split(command), shell=IS_WIN,
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- for line in proc.stdout:
- yield line.decode().rstrip()
- except Exception as e:
- error(e)
- yield (e.errno, e.strerror)
-
-
-class Timer(object):
- """Class for timer thread"""
-
- class TimerThread(threading.Thread):
-
- def __init__(self, event, seconds, macro):
- threading.Thread.__init__(self)
- self._event = event
- self._seconds = seconds
- self._macro = macro
-
- def run(self):
- while not self._event.wait(self._seconds):
- Macro.call(self._macro)
- info('\tTimer stopped... ')
- return
-
- @classmethod
- def exists(cls, name):
- """Validate in timer **name** exists
-
- :param name: Timer name, it must be unique
- :type name: str
- :return: True if exists timer name
- :rtype: bool
- """
- global _EVENTS
- return name in _EVENTS
-
- @classmethod
- def start(cls, name: str, seconds: float, macro: dict):
- """Start timer **name** every **seconds** and execute **macro**
-
- :param name: Timer name, it must be unique
- :type name: str
- :param seconds: Seconds for wait
- :type seconds: float
- :param macro: Macro for execute
- :type macro: dict
- """
- global _EVENTS
-
- _EVENTS[name] = threading.Event()
- info(f"Timer '{name}' started, execute macro: '{macro['name']}'")
- thread = cls.TimerThread(_EVENTS[name], seconds, macro)
- thread.start()
- return
-
- @classmethod
- def stop(cls, name: str):
- """Stop timer **name**
-
- :param name: Timer name
- :type name: str
- """
- global _EVENTS
- _EVENTS[name].set()
- del _EVENTS[name]
- return
-
- @classmethod
- def once(cls, name: str, seconds: float, macro: dict):
- """Start timer **name** only once in **seconds** and execute **macro**
-
- :param name: Timer name, it must be unique
- :type name: str
- :param seconds: Seconds for wait before execute macro
- :type seconds: float
- :param macro: Macro for execute
- :type macro: dict
- """
- global _EVENTS
-
- _EVENTS[name] = threading.Timer(seconds, Macro.call, (macro,))
- _EVENTS[name].start()
- info(f'Event: "{name}", started... execute in {seconds} seconds')
-
- return
-
- @classmethod
- def cancel(cls, name: str):
- """Cancel timer **name** only once events.
-
- :param name: Timer name, it must be unique
- :type name: str
- """
- global _EVENTS
-
- if name in _EVENTS:
- try:
- _EVENTS[name].cancel()
- del _EVENTS[name]
- info(f'Cancel event: "{name}", ok...')
- except Exception as e:
- error(e)
- else:
- debug(f'Cancel event: "{name}", not exists...')
- return
-
-
-class Hash(object):
- """Class for hash
- """
- @classmethod
- def digest(cls, method: str, data: str, in_hex: bool=True):
- """Get digest from data with method
-
- :param method: Digest method: md5, sha1, sha256, sha512, etc...
- :type method: str
- :param data: Data for get digest
- :type data: str
- :param in_hex: If True, get digest in hexadecimal, if False, get bytes
- :type in_hex: bool
- :return: bytes or hex digest
- :rtype: bytes or str
- """
-
- result = ''
- obj = getattr(hashlib, method)(data.encode())
- if in_hex:
- result = obj.hexdigest()
- else:
- result = obj.digest()
- return result
-
-
-class Paths(object):
- """Class for paths
- """
- FILE_PICKER = 'com.sun.star.ui.dialogs.FilePicker'
- FOLDER_PICKER = 'com.sun.star.ui.dialogs.FolderPicker'
- REMOTE_FILE_PICKER = 'com.sun.star.ui.dialogs.RemoteFilePicker'
- OFFICE_FILE_PICKER = 'com.sun.star.ui.dialogs.OfficeFilePicker'
-
- def __init__(self, path=''):
- if path.startswith('file://'):
- path = str(Path(uno.fileUrlToSystemPath(path)).resolve())
- self._path = Path(path)
-
- @property
- def path(self):
- """Get base path"""
- return str(self._path.parent)
-
- @property
- def file_name(self):
- """Get file name"""
- return self._path.name
-
- @property
- def name(self):
- """Get name"""
- return self._path.stem
-
- @property
- def ext(self):
- """Get extension"""
- return self._path.suffix[1:]
-
- @property
- def size(self):
- """Get size"""
- return self._path.stat().st_size
-
- @property
- def url(self):
- """Get like URL"""
- return self._path.as_uri()
-
- @property
- def info(self):
- """Get all info like tuple"""
- i = (self.path, self.file_name, self.name, self.ext, self.size, self.url)
- return i
-
- @property
- def dict(self):
- """Get all info like dict"""
- data = {
- 'path': self.path,
- 'file_name': self.file_name,
- 'name': self.name,
- 'ext': self.ext,
- 'size': self.size,
- 'url': self.url,
- }
- return data
-
- @classproperty
- def home(self):
- """Get user home"""
- return str(Path.home())
-
- @classproperty
- def documents(self):
- """Get user save documents"""
- return self.config()
-
- @classproperty
- def user_profile(self):
- """Get path user profile"""
- path = self.config('UserConfig')
- path = str(Path(path).parent)
- return path
-
- @classproperty
- def user_config(self):
- """Get path config in user profile"""
- path = self.config('UserConfig')
- return path
-
- @classproperty
- def python(self):
- """Get path executable python"""
- if IS_WIN:
- path = self.join(self.config('Module'), PYTHON)
- elif IS_MAC:
- path = self.join(self.config('Module'), '..', 'Resources', PYTHON)
- else:
- path = sys.executable
- return path
-
- @classmethod
- def to_system(cls, path:str) -> str:
- """Convert paths in URL to system
-
- :param path: Path to convert
- :type path: str
- :return: Path system format
- :rtype: str
- """
- if path.startswith('file://'):
- path = str(Path(uno.fileUrlToSystemPath(path)).resolve())
- return path
-
- @classmethod
- def to_url(cls, path: str) -> str:
- """Convert paths in format system to URL
-
- :param path: Path to convert
- :type path: str
- :return: Path in URL
- :rtype: str
- """
- if not path.startswith('file://'):
- path = Path(path).as_uri()
- return path
-
- @classmethod
- def config(cls, name: str='Work') -> Union[str, list]:
- """Return path from config
-
- :param name: Name in service PathSettings, default get path documents
- :type name: str
- :return: Path in config, if exists.
- :rtype: str or list
-
- `See Api XPathSettings `_
- """
- path = create_instance('com.sun.star.util.PathSettings')
- path = cls.to_system(getattr(path, name)).split(';')
- if len(path) == 1:
- path = path[0]
- return path
-
- @classmethod
- def join(cls, *paths: str) -> str:
- """Join paths
-
- :param paths: Paths to join
- :type paths: list
- :return: New path with joins
- :rtype: str
- """
- path = str(Path(paths[0]).joinpath(*paths[1:]))
- return path
-
- @classmethod
- def exists(cls, path: str) -> bool:
- """If exists path
-
- :param path: Path for validate
- :type path: str
- :return: True if path exists, False if not.
- :rtype: bool
- """
- path = cls.to_system(path)
- result = Path(path).exists()
- return result
-
- @classmethod
- def exists_app(cls, name_app: str) -> bool:
- """If exists app in system
-
- :param name_app: Name of application
- :type name_app: str
- :return: True if app exists, False if not.
- :rtype: bool
- """
- result = bool(shutil.which(name_app))
- return result
-
- @classmethod
- def is_dir(cls, path: str):
- """Validate if path is directory
-
- :param path: Path for validate
- :type path: str
- :return: True if path is directory, False if not.
- :rtype: bool
- """
- return Path(path).is_dir()
-
- @classmethod
- def is_file(cls, path: str):
- """Validate if path is a file
-
- :param path: Path for validate
- :type path: str
- :return: True if path is a file, False if not.
- :rtype: bool
- """
- return Path(path).is_file()
-
- @classmethod
- def temp_file(self):
- """Make temporary file"""
- return tempfile.NamedTemporaryFile(mode='w')
-
- @classmethod
- def temp_dir(self):
- """Make temporary directory"""
- return tempfile.TemporaryDirectory(ignore_cleanup_errors=True)
-
- @classmethod
- def get(cls, init_dir: str='', filters: str='') -> str:
- """Get path for save
-
- :param init_dir: Initial default path
- :type init_dir: str
- :param filters: Filter for show type files: 'xml' or 'txt,xml'
- :type filters: str
- :return: Selected path
- :rtype: str
-
- `See API `_
- """
- if not init_dir:
- init_dir = cls.documents
- init_dir = cls.to_url(init_dir)
- file_picker = create_instance(cls.FILE_PICKER)
- file_picker.setTitle(_('Select path'))
- file_picker.setDisplayDirectory(init_dir)
- file_picker.initialize((TemplateDescription.FILEOPEN_SIMPLE,))
- if filters:
- for f in filters.split(','):
- file_picker.appendFilter(f.upper(), f'*.{f.lower()}')
-
- path = ''
- if file_picker.execute():
- path = cls.to_system(file_picker.getSelectedFiles()[0])
- return path
-
- @classmethod
- def get_dir(cls, init_dir: str='') -> str:
- """Get path dir
-
- :param init_dir: Initial default path
- :type init_dir: str
- :return: Selected path
- :rtype: str
- """
- folder_picker = create_instance(cls.FOLDER_PICKER)
- if not init_dir:
- init_dir = cls.documents
- init_dir = cls.to_url(init_dir)
- folder_picker.setTitle(_('Select directory'))
- folder_picker.setDisplayDirectory(init_dir)
-
- path = ''
- if folder_picker.execute():
- path = cls.to_system(folder_picker.getDirectory())
- return path
-
- @classmethod
- def get_file(cls, init_dir: str='', filters: str='', multiple: bool=False):
- """Get path exists file
-
- :param init_dir: Initial default path
- :type init_dir: str
- :param filters: Filter for show type files: 'xml' or 'txt,xml'
- :type filters: str
- :param multiple: If user can selected multiple files
- :type multiple: bool
- :return: Selected path
- :rtype: str
- """
- if not init_dir:
- init_dir = cls.documents
- init_dir = cls.to_url(init_dir)
-
- file_picker = create_instance(cls.FILE_PICKER)
- file_picker.setTitle(_('Select file'))
- file_picker.setDisplayDirectory(init_dir)
- file_picker.setMultiSelectionMode(multiple)
-
- if filters:
- for f in filters.split(','):
- file_picker.appendFilter(f.upper(), f'*.{f.lower()}')
-
- path = ''
- if file_picker.execute():
- files = file_picker.getSelectedFiles()
- path = [cls.to_system(f) for f in files]
- if not multiple:
- path = path[0]
- return path
-
- @classmethod
- def files(cls, path: str, pattern: str='*'):
- """Get all files in path
-
- :param path: Path with files
- :type path: str
- :param pattern: For filter files, default get all.
- :type pattern: str
- :return: Files in path
- :rtype: list
- """
- files = [str(p) for p in Path(path).glob(pattern) if p.is_file()]
- return files
-
- @classmethod
- def walk(cls, path, filters=''):
- """Get all files in path recursively
-
- :param path: Path with files
- :type path: str
- :param filters: For filter files, default get all.
- :type filters: str
- :return: Files in path
- :rtype: list
- """
- paths = []
- for folder, _, files in os.walk(path):
- if filters:
- pattern = re.compile(r'\.(?:{})$'.format(filters), re.IGNORECASE)
- paths += [cls.join(folder, f) for f in files if pattern.search(f)]
- else:
- paths += [cls.join(folder, f) for f in files]
- return paths
-
- @classmethod
- def dirs(cls, path):
- """Get directories in path
-
- :param path: Path to scan
- :type path: str
- :return: Directories in path
- :rtype: list
- """
- dirs = [str(p) for p in Path(path).iterdir() if p.is_dir()]
- return dirs
-
- @classmethod
- def walk_dirs(cls, path, tree=False):
- """Get directories recursively
-
- :param path: Path to scan
- :type path: str
- :param tree: get info in a tuple (ID_FOLDER, ID_PARENT, NAME)
- :type tree: bool
- :return: Directories in path
- :rtype: list
- """
- folders = []
- if tree:
- i = 0
- parents = {path: 0}
- for root, dirs, _ in os.walk(path):
- for name in dirs:
- i += 1
- rn = cls.join(root, name)
- if not rn in parents:
- parents[rn] = i
- folders.append((i, parents[root], name))
- else:
- for root, dirs, _ in os.walk(path):
- folders += [cls.join(root, name) for name in dirs]
- return folders
-
- @classmethod
- def extension(cls, id_ext: str):
- """Get path extension install from id
-
- :param id_ext: ID extension
- :type id_ext: str
- :return: Path extension
- :rtype: str
- """
- pip = CTX.getValueByName('/singletons/com.sun.star.deployment.PackageInformationProvider')
- path = Paths.to_system(pip.getPackageLocation(id_ext))
- return path
-
- @classmethod
- def replace_ext(cls, path: str, new_ext: str):
- """Replace extension in file path
-
- :param path: Path to file
- :type path: str
- :param new_ext: New extension
- :type new_ext: str
- :return: Path with new extension
- :rtype: str
- """
- p = Paths(path)
- name = f'{p.name}.{new_ext}'
- path = cls.join(p.path, name)
- return path
-
- @classmethod
- def open(cls, path: str):
- """Open any file with default program in systema
-
- :param path: Path to file
- :type path: str
- :return: PID file, only Linux
- :rtype: int
- """
- pid = 0
- if IS_WIN:
- os.startfile(path)
- else:
- pid = subprocess.Popen(['xdg-open', path]).pid
- return pid
-
- # ~ Save/read data
-
- @classmethod
- def save(cls, path: str, data: str, encoding: str='utf-8') -> bool:
- """Save data in path with encoding
-
- :param path: Path to file save
- :type path: str
- :param data: Data to save
- :type data: str
- :param encoding: Encoding for save data, default utf-8
- :type encoding: str
- :return: True, if save corrrectly
- :rtype: bool
- """
- result = bool(Path(path).write_text(data, encoding=encoding))
- return result
-
- @classmethod
- def save_bin(cls, path: str, data: bytes) -> bool:
- """Save binary data in path
-
- :param path: Path to file save
- :type path: str
- :param data: Data to save
- :type data: bytes
- :return: True, if save corrrectly
- :rtype: bool
- """
- result = bool(Path(path).write_bytes(data))
- return result
-
- @classmethod
- def read(cls, path: str, get_lines: bool=False, encoding: str='utf-8') -> Union[str, list]:
- """Read data in path
-
- :param path: Path to file read
- :type path: str
- :param get_lines: If read file line by line
- :type get_lines: bool
- :return: File content
- :rtype: str or list
- """
- if get_lines:
- with Path(path).open(encoding=encoding) as f:
- data = f.readlines()
- else:
- data = Path(path).read_text(encoding=encoding)
- return data
-
- @classmethod
- def read_bin(cls, path: str) -> bytes:
- """Read binary data in path
-
- :param path: Path to file read
- :type path: str
- :return: File content
- :rtype: bytes
- """
- data = Path(path).read_bytes()
- return data
-
- # ~ Import/export data
-
- @classmethod
- def from_json(cls, path: str) -> Any:
- """Read path file and load json data
-
- :param path: Path to file
- :type path: str
- :return: Any data
- :rtype: Any
- """
- data = json.loads(cls.read(path))
- return data
-
- @classmethod
- def to_json(cls, path: str, data: str):
- """Save data in path file like json
-
- :param path: Path to file
- :type path: str
- :return: True if save correctly
- :rtype: bool
- """
- data = json.dumps(data, indent=4, ensure_ascii=False, sort_keys=True)
- return cls.save(path, data)
-
- @classmethod
- def from_csv(cls, path: str, args: dict={}) -> tuple:
- """Read CSV
-
- :param path: Path to file csv
- :type path: str
- :param args: Any argument support for Python library
- :type args: dict
- :return: Data csv like tuple
- :rtype: tuple
-
- `See CSV Reader `_
- """
- with open(path) as f:
- rows = tuple(csv.reader(f, **args))
- return rows
-
- @classmethod
- def to_csv(cls, path: str, data: Any, args: dict={}):
- """Write CSV
-
- :param path: Path to file write csv
- :type path: str
- :param data: Data to write
- :type data: Iterable
- :param args: Any argument support for Python library
- :type args: dict
-
- `See CSV Writer `_
- """
- with open(path, 'w') as f:
- writer = csv.writer(f, **args)
- writer.writerows(data)
- return
-
- @classmethod
- def zip(cls, source: Union[str, tuple, list], target='') -> str:
- path_zip = target
- if not isinstance(source, (tuple, list)):
- path, _, name, _ = _P(source).info
- start = len(path) + 1
- if not target:
- path_zip = f'{path}/{name}.zip'
-
- if isinstance(source, (tuple, list)):
- files = [(f, f[len(_P(f).path)+1:]) for f in source]
- elif _P.is_file(source):
- files = ((source, source[start:]),)
- else:
- files = [(f, f[start:]) for f in _P.walk(source)]
-
- compression = zipfile.ZIP_DEFLATED
- with zipfile.ZipFile(path_zip, 'w', compression=compression) as z:
- for f in files:
- z.write(f[0], f[1])
- return path_zip
-
- @classmethod
- def unzip(cls, source, target='', members=None, pwd=None):
- path = target
- if not target:
- path = _P(source).path
- with zipfile.ZipFile(source) as z:
- if not pwd is None:
- pwd = pwd.encode()
- if isinstance(members, str):
- members = (members,)
- z.extractall(path, members=members, pwd=pwd)
- return
-
- @classmethod
- def zip_content(cls, path: str):
- with zipfile.ZipFile(path) as z:
- names = z.namelist()
- return names
-
- @classmethod
- def merge_zip(cls, target, zips):
- try:
- with zipfile.ZipFile(target, 'w', compression=zipfile.ZIP_DEFLATED) as t:
- for path in zips:
- with zipfile.ZipFile(path, compression=zipfile.ZIP_DEFLATED) as s:
- for name in s.namelist():
- t.writestr(name, s.open(name).read())
- except Exception as e:
- error(e)
- return False
-
- return True
-
- @classmethod
- def kill(cls, path: str):
- """Delete path
-
- :param path: Path to file or directory
- :type path: str
- :return: True if delete correctly
- :rtype: bool
- """
- p = Path(path)
- try:
- if p.is_file():
- p.unlink()
- elif p.is_dir():
- shutil.rmtree(path)
- result = True
- except OSError as e:
- log.error(e)
- result = False
-
- return result
-
- @classmethod
- def copy(cls, source: str, target: str='', name: str=''):
- p, f, n, e, _, _ = Paths(source).info
- if target:
- p = target
- e = f'.{e}'
- if name:
- e = ''
- n = name
- path_new = cls.join(p, f'{n}{e}')
- shutil.copy(source, path_new)
- return path_new
-
-
-class Config(object):
- """Class for set and get configurations
- """
- @classmethod
- def set(cls, prefix: str, value: Any, key: str='') -> bool:
- """Save data config in user config like json
-
- :param prefix: Unique prefix for this data
- :type prefix: str
- :param value: Value for save
- :type value: Any
- :param key: Key for value
- :type key: str
- :return: True if save correctly
- :rtype: bool
- """
- name_file = FILES['CONFIG'].format(prefix)
- path = Paths.join(Paths.user_config, name_file)
- data = value
- if key:
- data = cls.get(prefix)
- data[key] = value
- result = Paths.to_json(path, data)
- return result
-
- @classmethod
- def get(cls, prefix: str, key: str='', default: Any={}) -> Any:
- """Get data config from user config like json
-
- :param prefix: Unique prefix for this data
- :type prefix: str
- :param key: Key for value
- :type key: str
- :param default: Get if not exists key
- :type default: Any
- :return: data
- :rtype: Any
- """
- data = {}
- name_file = FILES['CONFIG'].format(prefix)
- path = Paths.join(Paths.user_config, name_file)
- if not Paths.exists(path):
- return data
-
- data = Paths.from_json(path)
- if key:
- data = data.get(key, default)
-
- return data
-
-
-class Url(object):
- """Class for simple url open
- """
- @classmethod
- def _open(cls, url: str, data: Any=None, headers: dict={}, verify: bool=True, \
- json: bool=False, timeout: int=TIMEOUT, method: str='GET') -> tuple:
- """URL Open"""
-
- debug(url)
- result = None
- context = None
- rheaders = {}
- err = ''
-
- if verify:
- if not data is None:
- if isinstance(data, str):
- data = data.encode()
- elif isinstance(data, dict):
- data = parse.urlencode(data).encode('ascii')
- else:
- context = ssl._create_unverified_context()
-
- try:
- req = Request(url, data=data, headers=headers, method=method)
- response = urlopen(req, timeout=timeout, context=context)
- except HTTPError as e:
- error(e)
- err = str(e)
- except URLError as e:
- error(e.reason)
- err = str(e.reason)
- # ToDo
- # ~ except timeout:
- # ~ err = 'timeout'
- # ~ error(err)
- else:
- rheaders = dict(response.info())
- result = response.read().decode()
- if json:
- result = Json.loads(result)
-
- return result, rheaders, err
-
- @classmethod
- def get(cls, url: str, data: Any=None, headers: dict={}, verify: bool=True, \
- json: bool=False, timeout: int=TIMEOUT) -> tuple:
- """Method GET
-
- :param url: Url to open
- :type url: str
- :return: result, headers and error
- :rtype: tuple
- """
- return cls._open(url, data, headers, verify, json, timeout)
-
- # ToDo
- @classmethod
- def post(cls, url: str, data: Any=None, headers: dict={}, verify: bool=True, \
- json: bool=False, timeout: int=TIMEOUT) -> tuple:
- """Method POST
- """
- data = parse.urlencode(data).encode('ascii')
- return cls._open(url, data, headers, verify, json, timeout, 'POST')
-
-
-class Email(object):
- """Class for send email
- """
- class _SmtpServer(object):
-
- def __init__(self, config):
- self._server = None
- self._error = ''
- self._sender = ''
- self._is_connect = self._login(config)
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- self.close()
-
- @property
- def is_connect(self):
- return self._is_connect
-
- @property
- def error(self):
- return self._error
-
- def _login(self, config):
- name = config['server']
- port = config['port']
- is_ssl = config['ssl']
- starttls = config.get('starttls', False)
- self._sender = config['user']
- try:
- if starttls:
- self._server = smtplib.SMTP(name, port, timeout=TIMEOUT)
- self._server.ehlo()
- self._server.starttls()
- self._server.ehlo()
- elif is_ssl:
- self._server = smtplib.SMTP_SSL(name, port, timeout=TIMEOUT)
- self._server.ehlo()
- else:
- self._server = smtplib.SMTP(name, port, timeout=TIMEOUT)
-
- self._server.login(self._sender, config['password'])
- msg = 'Connect to: {}'.format(name)
- debug(msg)
- return True
- except smtplib.SMTPAuthenticationError as e:
- if '535' in str(e):
- self._error = _('Incorrect user or password')
- return False
- if '534' in str(e) and 'gmail' in name:
- self._error = _('Allow less secure apps in GMail')
- return False
- except smtplib.SMTPException as e:
- self._error = str(e)
- return False
- except Exception as e:
- self._error = str(e)
- return False
- return False
-
- def _body(self, msg):
- body = msg.replace('\n', '
')
- return body
-
- def send(self, message):
- # ~ file_name = 'attachment; filename={}'
- email = MIMEMultipart()
- email['From'] = self._sender
- email['To'] = message['to']
- email['Cc'] = message.get('cc', '')
- email['Subject'] = message['subject']
- email['Date'] = formatdate(localtime=True)
- if message.get('confirm', False):
- email['Disposition-Notification-To'] = email['From']
- email.attach(MIMEText(self._body(message['body']), 'html'))
-
- paths = message.get('files', ())
- if isinstance(paths, str):
- paths = (paths,)
- for path in paths:
- fn = _P(path).file_name
- # ~ print('NAME', fn)
- part = MIMEBase('application', 'octet-stream')
- part.set_payload(_P.read_bin(path))
- encoders.encode_base64(part)
- part.add_header('Content-Disposition', f'attachment; filename="{fn}"')
- email.attach(part)
-
- receivers = (
- email['To'].split(',') +
- email['CC'].split(',') +
- message.get('bcc', '').split(','))
- try:
- self._server.sendmail(self._sender, receivers, email.as_string())
- msg = 'Email sent...'
- debug(msg)
- if message.get('path', ''):
- self.save_message(email, message['path'])
- return True
- except Exception as e:
- self._error = str(e)
- return False
- return False
-
- def save_message(self, email, path):
- mbox = mailbox.mbox(path, create=True)
- mbox.lock()
- try:
- msg = mailbox.mboxMessage(email)
- mbox.add(msg)
- mbox.flush()
- finally:
- mbox.unlock()
- return
-
- def close(self):
- try:
- self._server.quit()
- msg = 'Close connection...'
- debug(msg)
- except:
- pass
- return
-
- @classmethod
- def _send_email(cls, server, messages):
- with cls._SmtpServer(server) as server:
- if server.is_connect:
- for msg in messages:
- server.send(msg)
- else:
- error(server.error)
- return server.error
-
- @classmethod
- def send(cls, server: dict, messages: Union[dict, tuple, list]):
- """Send email with config server, emails send in thread.
-
- :param server: Configuration for send emails
- :type server: dict
- :param server: Dictionary con message or list of messages
- :type server: dict or iterator
- """
- if isinstance(messages, dict):
- messages = (messages,)
- t = threading.Thread(target=cls._send_email, args=(server, messages))
- t.start()
- return
-
-
-class Color(object):
- """Class for colors
-
- `See Web Colors `_
- """
- COLORS = {
- 'aliceblue': 15792383,
- 'antiquewhite': 16444375,
- 'aqua': 65535,
- 'aquamarine': 8388564,
- 'azure': 15794175,
- 'beige': 16119260,
- 'bisque': 16770244,
- 'black': 0,
- 'blanchedalmond': 16772045,
- 'blue': 255,
- 'blueviolet': 9055202,
- 'brown': 10824234,
- 'burlywood': 14596231,
- 'cadetblue': 6266528,
- 'chartreuse': 8388352,
- 'chocolate': 13789470,
- 'coral': 16744272,
- 'cornflowerblue': 6591981,
- 'cornsilk': 16775388,
- 'crimson': 14423100,
- 'cyan': 65535,
- 'darkblue': 139,
- 'darkcyan': 35723,
- 'darkgoldenrod': 12092939,
- 'darkgray': 11119017,
- 'darkgreen': 25600,
- 'darkgrey': 11119017,
- 'darkkhaki': 12433259,
- 'darkmagenta': 9109643,
- 'darkolivegreen': 5597999,
- 'darkorange': 16747520,
- 'darkorchid': 10040012,
- 'darkred': 9109504,
- 'darksalmon': 15308410,
- 'darkseagreen': 9419919,
- 'darkslateblue': 4734347,
- 'darkslategray': 3100495,
- 'darkslategrey': 3100495,
- 'darkturquoise': 52945,
- 'darkviolet': 9699539,
- 'deeppink': 16716947,
- 'deepskyblue': 49151,
- 'dimgray': 6908265,
- 'dimgrey': 6908265,
- 'dodgerblue': 2003199,
- 'firebrick': 11674146,
- 'floralwhite': 16775920,
- 'forestgreen': 2263842,
- 'fuchsia': 16711935,
- 'gainsboro': 14474460,
- 'ghostwhite': 16316671,
- 'gold': 16766720,
- 'goldenrod': 14329120,
- 'gray': 8421504,
- 'grey': 8421504,
- 'green': 32768,
- 'greenyellow': 11403055,
- 'honeydew': 15794160,
- 'hotpink': 16738740,
- 'indianred': 13458524,
- 'indigo': 4915330,
- 'ivory': 16777200,
- 'khaki': 15787660,
- 'lavender': 15132410,
- 'lavenderblush': 16773365,
- 'lawngreen': 8190976,
- 'lemonchiffon': 16775885,
- 'lightblue': 11393254,
- 'lightcoral': 15761536,
- 'lightcyan': 14745599,
- 'lightgoldenrodyellow': 16448210,
- 'lightgray': 13882323,
- 'lightgreen': 9498256,
- 'lightgrey': 13882323,
- 'lightpink': 16758465,
- 'lightsalmon': 16752762,
- 'lightseagreen': 2142890,
- 'lightskyblue': 8900346,
- 'lightslategray': 7833753,
- 'lightslategrey': 7833753,
- 'lightsteelblue': 11584734,
- 'lightyellow': 16777184,
- 'lime': 65280,
- 'limegreen': 3329330,
- 'linen': 16445670,
- 'magenta': 16711935,
- 'maroon': 8388608,
- 'mediumaquamarine': 6737322,
- 'mediumblue': 205,
- 'mediumorchid': 12211667,
- 'mediumpurple': 9662683,
- 'mediumseagreen': 3978097,
- 'mediumslateblue': 8087790,
- 'mediumspringgreen': 64154,
- 'mediumturquoise': 4772300,
- 'mediumvioletred': 13047173,
- 'midnightblue': 1644912,
- 'mintcream': 16121850,
- 'mistyrose': 16770273,
- 'moccasin': 16770229,
- 'navajowhite': 16768685,
- 'navy': 128,
- 'oldlace': 16643558,
- 'olive': 8421376,
- 'olivedrab': 7048739,
- 'orange': 16753920,
- 'orangered': 16729344,
- 'orchid': 14315734,
- 'palegoldenrod': 15657130,
- 'palegreen': 10025880,
- 'paleturquoise': 11529966,
- 'palevioletred': 14381203,
- 'papayawhip': 16773077,
- 'peachpuff': 16767673,
- 'peru': 13468991,
- 'pink': 16761035,
- 'plum': 14524637,
- 'powderblue': 11591910,
- 'purple': 8388736,
- 'red': 16711680,
- 'rosybrown': 12357519,
- 'royalblue': 4286945,
- 'saddlebrown': 9127187,
- 'salmon': 16416882,
- 'sandybrown': 16032864,
- 'seagreen': 3050327,
- 'seashell': 16774638,
- 'sienna': 10506797,
- 'silver': 12632256,
- 'skyblue': 8900331,
- 'slateblue': 6970061,
- 'slategray': 7372944,
- 'slategrey': 7372944,
- 'snow': 16775930,
- 'springgreen': 65407,
- 'steelblue': 4620980,
- 'tan': 13808780,
- 'teal': 32896,
- 'thistle': 14204888,
- 'tomato': 16737095,
- 'turquoise': 4251856,
- 'violet': 15631086,
- 'wheat': 16113331,
- 'white': 16777215,
- 'whitesmoke': 16119285,
- 'yellow': 16776960,
- 'yellowgreen': 10145074,
- }
-
- def _get_color(self, index):
- if isinstance(index, tuple):
- color = (index[0] << 16) + (index[1] << 8) + index[2]
- else:
- if index[0] == '#':
- r, g, b = bytes.fromhex(index[1:])
- color = (r << 16) + (g << 8) + b
- else:
- color = self.COLORS.get(index.lower(), -1)
- return color
-
- def __call__(self, index):
- return self._get_color(index)
-
- def __getitem__(self, index):
- return self._get_color(index)
-
-
-COLOR_ON_FOCUS = Color()('LightYellow')
-
-
-class ClipBoard(object):
- SERVICE = 'com.sun.star.datatransfer.clipboard.SystemClipboard'
- CLIPBOARD_FORMAT_TEXT = 'text/plain;charset=utf-16'
-
- class TextTransferable(unohelper.Base, XTransferable):
-
- def __init__(self, text):
- df = DataFlavor()
- df.MimeType = ClipBoard.CLIPBOARD_FORMAT_TEXT
- df.HumanPresentableName = 'encoded text utf-16'
- self.flavors = (df,)
- self._data = text
-
- def getTransferData(self, flavor):
- return self._data
-
- def getTransferDataFlavors(self):
- return self.flavors
-
- @classmethod
- def set(cls, value):
- ts = cls.TextTransferable(value)
- sc = create_instance(cls.SERVICE)
- sc.setContents(ts, None)
- return
-
- @classproperty
- def contents(cls):
- df = None
- text = ''
- sc = create_instance(cls.SERVICE)
- transferable = sc.getContents()
- data = transferable.getTransferDataFlavors()
- for df in data:
- if df.MimeType == cls.CLIPBOARD_FORMAT_TEXT:
- break
- if df:
- text = transferable.getTransferData(df)
- return text
-
-
-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()
-
-
-class EventsListenerBase(unohelper.Base, XEventListener):
-
- def __init__(self, controller, name, window=None):
- self._controller = controller
- self._name = name
- self._window = window
-
- @property
- def name(self):
- return self._name
-
- def disposing(self, event):
- self._controller = None
- if not self._window is None:
- self._window.setMenuBar(None)
-
-
-class EventsRangeSelectionListener(EventsListenerBase, XRangeSelectionListener):
-
- def __init__(self, controller):
- super().__init__(controller, '')
-
- def done(self, event):
- range_selection = event.RangeDescriptor
- event_name = 'range_selection_done'
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(range_selection)
- 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)()
- return
-
-
-class LOShapes(object):
- _type = 'ShapeCollection'
-
- def __init__(self, obj):
- self._obj = obj
-
- def __len__(self):
- return self.obj.Count
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- pass
-
- def __iter__(self):
- self._index = 0
- return self
-
- def __next__(self):
- try:
- s = self.obj[self._index]
- shape = LOShape(s)
- except IndexError:
- raise StopIteration
-
- self._index += 1
- return shape
-
- def __str__(self):
- return 'Shapes'
-
- @property
- def obj(self):
- return self._obj
-
-
-class LOShape(object):
- IMAGE = 'com.sun.star.drawing.GraphicObjectShape'
-
- def __init__(self, obj):
- self._obj = obj
-
- def __str__(self):
- return f'Shape: {self.name}'
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def properties(self):
- # ~ properties = self.obj.PropertySetInfo.Properties
- # ~ data = {p.Name: getattr(self.obj, p.Name) for p in properties}
- data = self.obj.PropertySetInfo.Properties
- keys = [p.Name for p in data]
- values = self.obj.getPropertyValues(keys)
- data = dict(zip(keys, values))
- return data
- @properties.setter
- def properties(self, values):
- _set_properties(self.obj, values)
-
- @property
- def shape_type(self):
- return self.obj.ShapeType
-
- @property
- def name(self):
- return self.obj.Name
- @name.setter
- def name(self, value):
- self.obj.Name = value
-
- @property
- def is_image(self):
- return self.shape_type == self.IMAGE
-
- @property
- def is_shape(self):
- return self.shape_type != self.IMAGE
-
- @property
- def size(self):
- s = self.obj.Size
- return s
- @size.setter
- def size(self, value):
- self.obj.Size = value
-
- @property
- def width(self):
- s = self.obj.Size
- return s.Width
- @width.setter
- def width(self, value):
- s = self.size
- s.Width = value
- self.size = s
-
- @property
- def height(self):
- s = self.obj.Size
- return s.Height
- @height.setter
- def height(self, value):
- s = self.size
- s.Height = value
- self.size = s
-
- @property
- def position(self):
- return self.obj.Position
- @property
- def x(self):
- return self.position.X
- @property
- def y(self):
- return self.position.Y
-
- @property
- def string(self):
- return self.obj.String
- @string.setter
- def string(self, value):
- self.obj.String = value
-
- @property
- def title(self):
- return self.obj.Title
- @title.setter
- def title(self, value):
- self.obj.Title = value
-
- @property
- def description(self):
- return self.obj.Description
- @description.setter
- def description(self, value):
- self.obj.Description = value
-
-
-
-class LOShortCuts(object):
- """Classe for manager shortcuts"""
- KEYS = {getattr(Key, k): k for k in dir(Key)}
- MODIFIERS = {
- 'shift': KeyModifier.SHIFT,
- 'ctrl': KeyModifier.MOD1,
- 'alt': KeyModifier.MOD2,
- 'ctrlmac': KeyModifier.MOD3,
- }
- COMBINATIONS = {
- 0: '',
- 1: 'shift',
- 2: 'ctrl',
- 4: 'alt',
- 8: 'ctrlmac',
- 3: 'shift+ctrl',
- 5: 'shift+alt',
- 9: 'shift+ctrlmac',
- 6: 'ctrl+alt',
- 10: 'ctrl+ctrlmac',
- 12: 'alt+ctrlmac',
- 7: 'shift+ctrl+alt',
- 11: 'shift+ctrl+ctrlmac',
- 13: 'shift+alt+ctrlmac',
- 14: 'ctrl+alt+ctrlmac',
- 15: 'shift+ctrl+alt+ctrlmac',
- }
-
- def __init__(self, app: str=''):
- self._app = app
- service = 'com.sun.star.ui.GlobalAcceleratorConfiguration'
- if app:
- service = 'com.sun.star.ui.ModuleUIConfigurationManagerSupplier'
- type_app = LODocuments.TYPES[app]
- manager = create_instance(service, True)
- uicm = manager.getUIConfigurationManager(type_app)
- self._config = uicm.ShortCutManager
- else:
- self._config = create_instance(service)
-
- def __getitem__(self, index):
- return LOShortCuts(index)
-
- def __contains__(self, item):
- cmd = self.get_by_shortcut(item)
- return bool(cmd)
-
- def __iter__(self):
- self._i = -1
- return self
-
- def __next__(self):
- self._i += 1
- try:
- event = self._config.AllKeyEvents[self._i]
- event = self._get_info(event)
- except IndexError:
- raise StopIteration
-
- return event
-
- @classmethod
- def to_key_event(cls, shortcut: str):
- """Convert from string shortcut (Shift+Ctrl+Alt+LETTER) to KeyEvent"""
- key_event = KeyEvent()
- keys = shortcut.split('+')
- try:
- for m in keys[:-1]:
- key_event.Modifiers += cls.MODIFIERS[m.lower()]
- key_event.KeyCode = getattr(Key, keys[-1].upper())
- except Exception as e:
- error(e)
- key_event = None
- return key_event
-
- @classmethod
- def get_url_script(cls, command: Union[str, dict]) -> str:
- """Get uno command or url for macro"""
- url = command
- if isinstance(url, str) and not url.startswith('.uno:'):
- url = f'.uno:{command}'
- elif isinstance(url, dict):
- url = Macro.get_url_script(command)
- return url
-
- def _get_shortcut(self, k):
- """Get shortcut for key event"""
- # ~ print(k.KeyCode, str(k.KeyChar), k.KeyFunc, k.Modifiers)
- shortcut = f'{self.COMBINATIONS[k.Modifiers]}+{self.KEYS[k.KeyCode]}'
- return shortcut
-
- def _get_info(self, key):
- """Get shortcut and command"""
- cmd = self._config.getCommandByKeyEvent(key)
- shortcut = self._get_shortcut(key)
- return shortcut, cmd
-
- def get_all(self):
- """Get all events key"""
- events = [(self._get_info(k)) for k in self._config.AllKeyEvents]
- return events
-
- def get_by_command(self, command: Union[str, dict]):
- """Get shortcuts by command"""
- url = LOShortCuts.get_url_script(command)
- key_events = self._config.getKeyEventsByCommand(url)
- shortcuts = [self._get_shortcut(k) for k in key_events]
- return shortcuts
-
- def get_by_shortcut(self, shortcut: str):
- """Get command by shortcut"""
- command = ''
- key_event = LOShortCuts.to_key_event(shortcut)
- if key_event:
- command = self._config.getCommandByKeyEvent(key_event)
- return command
-
- def set(self, shortcut: str, command: Union[str, dict]) -> bool:
- """Set shortcut to command
-
- :param shortcut: Shortcut like Shift+Ctrl+Alt+LETTER
- :type shortcut: str
- :param command: Command tu assign, 'UNOCOMMAND' or dict with macro info
- :type command: str or dict
- :return: True if set sucesfully
- :rtype: bool
- """
- result = True
- url = LOShortCuts.get_url_script(command)
- key_event = LOShortCuts.to_key_event(shortcut)
- try:
- self._config.setKeyEvent(key_event, url)
- self._config.store()
- except Exception as e:
- error(e)
- result = False
-
- return result
-
- def remove_by_shortcut(self, shortcut: str):
- """Remove by shortcut"""
- key_event = LOShortCuts.to_key_event(shortcut)
- try:
- self._config.removeKeyEvent(key_event)
- result = True
- except NoSuchElementException:
- debug(f'No exists: {shortcut}')
- result = False
- return result
-
- def remove_by_command(self, command: Union[str, dict]):
- """Remove by shortcut"""
- url = LOShortCuts.get_url_script(command)
- self._config.removeCommandFromAllKeyEvents(url)
- return
-
- def reset(self):
- """Reset configuration"""
- self._config.reset()
- self._config.store()
- return
-
-
-class LOMenuDebug():
- """Classe for debug info menu"""
-
- @classmethod
- def _get_info(cls, menu, index):
- """Get every option menu"""
- line = f"({index}) {menu.get('CommandURL', '----------')}"
- submenu = menu.get('ItemDescriptorContainer', None)
- if not submenu is None:
- line += cls._get_submenus(submenu)
- return line
-
- @classmethod
- def _get_submenus(cls, menu, level=1):
- """Get submenus"""
- line = ''
- for i, v in enumerate(menu):
- data = data_to_dict(v)
- cmd = data.get('CommandURL', '----------')
- line += f'\n{" " * level}├─ ({i}) {cmd}'
- submenu = data.get('ItemDescriptorContainer', None)
- if not submenu is None:
- line += cls._get_submenus(submenu, level + 1)
- return line
-
- def __call__(cls, menu):
- for i, m in enumerate(menu):
- data = data_to_dict(m)
- print(cls._get_info(data, i))
- return
-
-
-class LOMenuBase():
- """Classe base for menus"""
- NODE = 'private:resource/menubar/menubar'
- config = None
- menus = None
- app = ''
-
- @classmethod
- def _get_index(cls, parent: Any, name: Union[int, str]=''):
- """Get index menu from name
-
- :param parent: Menu parent
- :type parent: pyUno
- :param name: Menu name for search if is str
- :type name: int or str
- :return: Index of menu
- :rtype: int
- """
- index = None
- if isinstance(name, str) and name:
- for i, m in enumerate(parent):
- menu = data_to_dict(m)
- if menu.get('CommandURL', '') == name:
- index = i
- break
- elif isinstance(name, str):
- index = len(parent) - 1
- elif isinstance(name, int):
- index = name
- return index
-
- @classmethod
- def _get_command_url(cls, menu: dict):
- """Get url from command and set shortcut
-
- :param menu: Menu data
- :type menu: dict
- :return: URL command
- :rtype: str
- """
- shortcut = menu.pop('ShortCut', '')
- command = menu['CommandURL']
- url = LOShortCuts.get_url_script(command)
- if shortcut:
- LOShortCuts(cls.app).set(shortcut, command)
- return url
-
- @classmethod
- def _save(cls, parent: Any, menu: dict, index: int):
- """Insert menu
-
- :param parent: Menu parent
- :type parent: pyUno
- :param menu: New menu data
- :type menu: dict
- :param index: Position to insert
- :type index: int
- """
- # ~ Some day
- # ~ self._menus.insertByIndex(index, new_menu)
- properties = dict_to_property(menu, True)
- uno.invoke(parent, 'insertByIndex', (index, properties))
- cls.config.replaceSettings(cls.NODE, cls.menus)
- return
-
- @classmethod
- def _insert_submenu(cls, parent: Any, menus: list):
- """Insert submenus recursively
-
- :param parent: Menu parent
- :type parent: pyUno
- :param menus: List of menus
- :type menus: list
- """
- for i, menu in enumerate(menus):
- submenu = menu.pop('Submenu', False)
- if submenu:
- idc = cls.config.createSettings()
- menu['ItemDescriptorContainer'] = idc
- menu['Type'] = 0
- if menu['Label'][0] == '-':
- menu['Type'] = 1
- else:
- menu['CommandURL'] = cls._get_command_url(menu)
- cls._save(parent, menu, i)
- if submenu:
- cls._insert_submenu(idc, submenu)
- return
-
- @classmethod
- def _get_first_command(cls, command):
- url = command
- if isinstance(command, dict):
- url = Macro.get_url_script(command)
- return url
-
- @classmethod
- def insert(cls, parent: Any, menu: dict, after: Union[int, str]=''):
- """Insert new menu
-
- :param parent: Menu parent
- :type parent: pyUno
- :param menu: New menu data
- :type menu: dict
- :param after: After menu insert
- :type after: int or str
- """
- index = cls._get_index(parent, after) + 1
- submenu = menu.pop('Submenu', False)
- menu['Type'] = 0
- idc = cls.config.createSettings()
- menu['ItemDescriptorContainer'] = idc
- menu['CommandURL'] = cls._get_first_command(menu['CommandURL'])
- cls._save(parent, menu, index)
- if submenu:
- cls._insert_submenu(idc, submenu)
- return
-
- @classmethod
- def remove(cls, parent: Any, name: Union[str, dict]):
- """Remove name in parent
-
- :param parent: Menu parent
- :type parent: pyUno
- :param menu: Menu name
- :type menu: str
- """
- if isinstance(name, dict):
- name = Macro.get_url_script(name)
- index = cls._get_index(parent, name)
- if index is None:
- debug(f'Not found: {name}')
- return
- uno.invoke(parent, 'removeByIndex', (index,))
- cls.config.replaceSettings(cls.NODE, cls.menus)
- cls.config.store()
- return
-
-
-class LOMenu(object):
- """Classe for individual menu"""
-
- def __init__(self, config: Any, menus: Any, app: str, menu: Any):
- """
- :param config: Configuration Mananer
- :type config: pyUno
- :param menus: Menu bar main
- :type menus: pyUno
- :param app: Name LibreOffice module
- :type app: str
- :para menu: Particular menu
- :type menu: pyUno
- """
- self._config = config
- self._menus = menus
- self._app = app
- self._parent = menu
-
- def __contains__(self, name):
- """If exists name in menu"""
- exists = False
- for m in self._parent:
- menu = data_to_dict(m)
- cmd = menu.get('CommandURL', '')
- if name == cmd:
- exists = True
- break
- return exists
-
- def __getitem__(self, index):
- """Index access"""
- if isinstance(index, int):
- menu = data_to_dict(self._parent[index])
- else:
- for m in self._parent:
- menu = data_to_dict(m)
- cmd = menu.get('CommandURL', '')
- if cmd == index:
- break
-
- obj = LOMenu(self._config, self._menus, self._app,
- menu['ItemDescriptorContainer'])
- return obj
-
- def debug(self):
- """Debug menu"""
- LOMenuDebug()(self._parent)
- return
-
- def insert(self, menu: dict, after: Union[int, str]='', save: bool=True):
- """Insert new menu
-
- :param menu: New menu data
- :type menu: dict
- :param after: Insert in after menu
- :type after: int or str
- :param save: For persistente save
- :type save: bool
- """
- LOMenuBase.config = self._config
- LOMenuBase.menus = self._menus
- LOMenuBase.app = self._app
- LOMenuBase.insert(self._parent, menu, after)
- if save:
- self._config.store()
- return
-
- def remove(self, menu: str):
- """Remove menu
-
- :param menu: Menu name
- :type menu: str
- """
- LOMenuBase.config = self._config
- LOMenuBase.menus = self._menus
- LOMenuBase.remove(self._parent, menu)
- return
-
-
-class LOMenuApp(object):
- """Classe for manager menu by LibreOffice module"""
- NODE = 'private:resource/menubar/menubar'
- MENUS = {
- 'file': '.uno:PickList',
- 'picklist': '.uno:PickList',
- 'tools': '.uno:ToolsMenu',
- 'help': '.uno:HelpMenu',
- 'window': '.uno:WindowList',
- 'edit': '.uno:EditMenu',
- 'view': '.uno:ViewMenu',
- 'insert': '.uno:InsertMenu',
- 'format': '.uno:FormatMenu',
- 'styles': '.uno:FormatStylesMenu',
- 'formatstyles': '.uno:FormatStylesMenu',
- 'sheet': '.uno:SheetMenu',
- 'data': '.uno:DataMenu',
- 'table': '.uno:TableMenu',
- 'formatform': '.uno:FormatFormMenu',
- 'page': '.uno:PageMenu',
- 'shape': '.uno:ShapeMenu',
- 'slide': '.uno:SlideMenu',
- 'slideshow': '.uno:SlideShowMenu',
- }
-
- def __init__(self, app: str):
- """
- :param app: LibreOffice Module: calc, writer, draw, impress, math, main
- :type app: str
- """
- self._app = app
- self._config = self._get_config()
- self._menus = self._config.getSettings(self.NODE, True)
-
- def _get_config(self):
- """Get config manager"""
- service = 'com.sun.star.ui.ModuleUIConfigurationManagerSupplier'
- type_app = LODocuments.TYPES[self._app]
- manager = create_instance(service, True)
- config = manager.getUIConfigurationManager(type_app)
- return config
-
- def debug(self):
- """Debug menu"""
- LOMenuDebug()(self._menus)
- return
-
- def __contains__(self, name):
- """If exists name in menu"""
- exists = False
- for m in self._menus:
- menu = data_to_dict(m)
- cmd = menu.get('CommandURL', '')
- if name == cmd:
- exists = True
- break
- return exists
-
- def __getitem__(self, index):
- """Index access"""
- if isinstance(index, int):
- menu = data_to_dict(self._menus[index])
- else:
- for m in self._menus:
- menu = data_to_dict(m)
- cmd = menu.get('CommandURL', '')
- if cmd == index or cmd == self.MENUS[index.lower()]:
- break
-
- obj = LOMenu(self._config, self._menus, self._app,
- menu['ItemDescriptorContainer'])
- return obj
-
- def insert(self, menu: dict, after: Union[int, str]='', save: bool=True):
- """Insert new menu
-
- :param menu: New menu data
- :type menu: dict
- :param after: Insert in after menu
- :type after: int or str
- :param save: For persistente save
- :type save: bool
- """
- LOMenuBase.config = self._config
- LOMenuBase.menus = self._menus
- LOMenuBase.app = self._app
- LOMenuBase.insert(self._menus, menu, after)
- if save:
- self._config.store()
- return
-
- def remove(self, menu: str):
- """Remove menu
-
- :param menu: Menu name
- :type menu: str
- """
- LOMenuBase.config = self._config
- LOMenuBase.menus = self._menus
- LOMenuBase.remove(self._menus, menu)
- return
-
-
-class LOMenus(object):
- """Classe for manager menus"""
-
- def __getitem__(self, index):
- """Index access"""
- return LOMenuApp(index)
-
-
-class LOEvents():
-
- def __init__(self, obj):
- self._obj = obj
-
- def __contains__(self, item):
- return self.obj.hasByName(item)
-
- def __getitem__(self, index):
- """Index access"""
- return self.obj.getByName(index)
-
- def __setitem__(self, name: str, macro: dict):
- """Set macro to event
-
- :param name: Event name
- :type name: str
- :param macro: Macro execute in event
- :type name: dict
- """
- pv = '[]com.sun.star.beans.PropertyValue'
- args = ()
- if macro:
- url = Macro.get_url_script(macro)
- args = dict_to_property(dict(EventType='Script', Script=url))
- uno.invoke(self.obj, 'replaceByName', (name, uno.Any(pv, args)))
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def names(self):
- return self.obj.ElementNames
-
- def remove(self, name):
- pv = '[]com.sun.star.beans.PropertyValue'
- uno.invoke(self.obj, 'replaceByName', (name, uno.Any(pv, ())))
- return
-
-
-class LOMain():
- """Classe for LibreOffice"""
-
- class commands():
- """Class for disable and enable commands
-
- `See DispatchCommands `_
- """
- @classmethod
- def _set_app_command(cls, command: str, disable: bool):
- """Disable or enabled UNO command
-
- :param command: UNO command to disable or enabled
- :type command: str
- :param disable: True if disable, False if active
- :type disable: bool
- :return: True if correctly update, False if not.
- :rtype: bool
- """
- NEW_NODE_NAME = f'zaz_disable_command_{command.lower()}'
- name = 'com.sun.star.configuration.ConfigurationProvider'
- service = 'com.sun.star.configuration.ConfigurationUpdateAccess'
- node_name = '/org.openoffice.Office.Commands/Execute/Disabled'
-
- cp = create_instance(name, True)
- node = PropertyValue(Name='nodepath', Value=node_name)
- update = cp.createInstanceWithArguments(service, (node,))
-
- result = True
- try:
- if disable:
- new_node = update.createInstanceWithArguments(())
- new_node.setPropertyValue('Command', command)
- update.insertByName(NEW_NODE_NAME, new_node)
- else:
- update.removeByName(NEW_NODE_NAME)
- update.commitChanges()
- except Exception as e:
- result = False
-
- return result
-
- @classmethod
- def disable(cls, command: str):
- """Disable UNO command
-
- :param command: UNO command to disable
- :type command: str
- :return: True if correctly disable, False if not.
- :rtype: bool
- """
- return cls._set_app_command(command, True)
-
- @classmethod
- def enabled(cls, command):
- """Enabled UNO command
-
- :param command: UNO command to enabled
- :type command: str
- :return: True if correctly disable, False if not.
- :rtype: bool
- """
- return cls._set_app_command(command, False)
-
- @classproperty
- def cmd(cls):
- """Disable or enable commands"""
- return cls.commands
-
- @classproperty
- def desktop(cls):
- """Create desktop instance
-
- :return: Desktop instance
- :rtype: pyUno
- """
- obj = create_instance('com.sun.star.frame.Desktop', True)
- return obj
-
- @classmethod
- def dispatch(cls, frame: Any, command: str, args: dict={}) -> None:
- """Call dispatch, used only if not exists directly in API
-
- :param frame: doc or frame instance
- :type frame: pyUno
- :param command: Command to execute
- :type command: str
- :param args: Extra argument for command
- :type args: dict
-
- `See DispatchCommands <`See DispatchCommands `_>`_
- """
- dispatch = create_instance('com.sun.star.frame.DispatchHelper')
- if hasattr(frame, 'frame'):
- frame = frame.frame
- url = command
- if not command.startswith('.uno:'):
- url = f'.uno:{command}'
- opt = dict_to_property(args)
- dispatch.executeDispatch(frame, url, '', 0, opt)
- return
-
- @classmethod
- def fonts(cls):
- """Get all font visibles in LibreOffice
-
- :return: tuple of FontDescriptors
- :rtype: tuple
-
- `See API FontDescriptor `_
- """
- toolkit = create_instance('com.sun.star.awt.Toolkit')
- device = toolkit.createScreenCompatibleDevice(0, 0)
- return device.FontDescriptors
-
- @classmethod
- def filters(cls):
- """Get all support filters
-
- `See Help ConvertFilters `_
- `See API FilterFactory `_
- """
- factory = create_instance('com.sun.star.document.FilterFactory')
- rows = [data_to_dict(factory[name]) for name in factory]
- for row in rows:
- row['UINames'] = data_to_dict(row['UINames'])
- return rows
-
-
-class LODocument():
-
- def __init__(self, obj):
- self._obj = obj
- self._cc = obj.getCurrentController()
- self._undo = True
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- self.close()
-
- @property
- def obj(self):
- """Return original pyUno object"""
- return self._obj
-
- @property
- def type(self):
- """Get type document"""
- return self._type
-
- @property
- def frame(self):
- """Get frame document"""
- return self._cc.getFrame()
-
- @property
- def title(self):
- """Get title document"""
- return self.obj.getTitle()
- @title.setter
- def title(self, value):
- self.obj.setTitle(value)
-
- @property
- def uid(self):
- """Get Runtime UID"""
- return self.obj.RuntimeUID
-
- @property
- def is_saved(self):
- """Get is saved"""
- return self.obj.hasLocation()
-
- @property
- def is_modified(self):
- """Get is modified"""
- return self.obj.isModified()
-
- @property
- def is_read_only(self):
- """Get is read only"""
- return self.obj.isReadonly()
-
- @property
- def path(self):
- """Get path in system files"""
- return Paths.to_system(self.obj.URL)
-
- @property
- def dir(self):
- """Get directory from path"""
- return Paths(self.path).path
-
- @property
- def file_name(self):
- """Get only file name"""
- return Paths(self.path).file_name
-
- @property
- def name(self):
- """Get name without extension"""
- return Paths(self.path).name
-
- @property
- def visible(self):
- """Get windows visible"""
- w = self.frame.ContainerWindow
- return w.isVisible()
- @visible.setter
- def visible(self, value):
- w = self.frame.ContainerWindow
- w.setVisible(value)
-
- @property
- def zoom(self):
- """Get current zoom value"""
- return self._cc.ZoomValue
- @zoom.setter
- def zoom(self, value):
- self._cc.ZoomValue = value
-
- @property
- def status_bar(self):
- """Get status bar"""
- bar = self._cc.getStatusIndicator()
- return bar
-
- @property
- def selection(self):
- """Get current selecction"""
- sel = self.obj.CurrentSelection
- return sel
-
- @property
- def table_auto_formats(self):
- taf = create_instance('com.sun.star.sheet.TableAutoFormats')
- return taf.ElementNames
-
- def save(self, path: str='', args: dict={}) -> bool:
- """Save document
-
- :param path: Path to save document
- :type path: str
- :param args: Optional: Extra argument for save
- :type args: dict
- :return: True if save correctly, False if not
- :rtype: bool
- """
- if not path:
- self.obj.store()
- return True
-
- path_save = Paths.to_url(path)
- opt = dict_to_property(args)
-
- try:
- self.obj.storeAsURL(path_save, opt)
- except Exception as e:
- error(e)
- return False
-
- return True
-
- def close(self):
- """Close document"""
- self.obj.close(True)
- return
-
- def to_pdf(self, path: str='', args: dict={}):
- """Export to PDF
-
- :param path: Path to export document
- :type path: str
- :param args: Optional: Extra argument for export
- :type args: dict
- :return: None if path or stream in memory
- :rtype: bytes or None
-
- `See PDF Export `_
- """
- stream = None
- path_pdf = 'private:stream'
-
- filter_name = f'{self.type}_pdf_Export'
- filter_data = dict_to_property(args, True)
- filters = {
- 'FilterName': filter_name,
- 'FilterData': filter_data,
- }
- if path:
- path_pdf = Paths.to_url(path)
- else:
- stream = IOStream.output()
- filters['OutputStream'] = stream
-
- opt = dict_to_property(filters)
- try:
- self.obj.storeToURL(path_pdf, opt)
- except Exception as e:
- error(e)
-
- if not stream is None:
- stream = stream.buffer
-
- return stream
-
- def export(self, path: str='', filter_name: str='', args: dict={}):
- """Export to others formats
-
- :param path: Path to export document
- :type path: str
- :param filter_name: Filter name to export
- :type filter_name: str
- :param args: Optional: Extra argument for export
- :type args: dict
- :return: None if path or stream in memory
- :rtype: bytes or None
- """
- FILTERS = {
- 'xlsx': 'Calc MS Excel 2007 XML',
- 'xls': 'MS Excel 97',
- 'docx': 'MS Word 2007 XML',
- 'doc': 'MS Word 97',
- 'rtf': 'Rich Text Format',
- }
-
- stream = None
- path_target = 'private:stream'
-
- filter_name = FILTERS.get(filter_name, filter_name)
- filter_data = dict_to_property(args, True)
- filters = {
- 'FilterName': filter_name,
- 'FilterData': filter_data,
- }
- if path:
- path_target = Paths.to_url(path)
- else:
- stream = IOStream.output()
- filters['OutputStream'] = stream
-
- opt = dict_to_property(filters)
- try:
- self.obj.storeToURL(path_target, opt)
- except Exception as e:
- error(e)
-
- if not stream is None:
- stream = stream.buffer
-
- return stream
-
- def _create_instance(self, name):
- obj = self.obj.createInstance(name)
- return obj
-
- def set_focus(self):
- """Send focus to windows"""
- w = self.frame.ComponentWindow
- w.setFocus()
- return
-
- def copy(self):
- """Copy current selection"""
- LOMain.dispatch(self.frame, 'Copy')
- return
-
- def paste(self):
- """Paste current content in clipboard"""
- sc = create_instance('com.sun.star.datatransfer.clipboard.SystemClipboard')
- transferable = sc.getContents()
- self._cc.insertTransferable(transferable)
- return
-
- def paste_special(self):
- """Insert contents, show dialog box Paste Special"""
- LOMain.dispatch(self.frame, 'InsertContents')
- return
-
- def paste_values(self):
- """Paste only values"""
- args = {
- 'Flags': 'SVDT',
- # ~ 'FormulaCommand': 0,
- # ~ 'SkipEmptyCells': False,
- # ~ 'Transpose': False,
- # ~ 'AsLink': False,
- # ~ 'MoveMode': 4,
- }
- LOMain.dispatch(self.frame, 'InsertContents', args)
- return
-
- def clear_undo(self):
- """Clear history undo"""
- self.obj.getUndoManager().clear()
- return
-
-
-class LODocMain():
- """Classe for start module"""
- _type = 'main'
-
- def __init__(self, obj):
- self._obj = obj
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def type(self):
- return self._type
-
-
-class LOCellStyle():
-
- def __init__(self, obj):
- self._obj = obj
-
- def __str__(self):
- return f'CellStyle: {self.name}'
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def name(self):
- return self.obj.Name
-
- @property
- def properties(self):
- properties = self.obj.PropertySetInfo.Properties
- data = {p.Name: getattr(self.obj, p.Name) for p in properties}
- return data
- @properties.setter
- def properties(self, values):
- _set_properties(self.obj, values)
-
-
-class LOCellStyles():
-
- def __init__(self, obj, doc):
- self._obj = obj
- self._doc = doc
-
- def __len__(self):
- return len(self.obj)
-
- def __getitem__(self, index):
- return LOCellStyle(self.obj[index])
-
- def __setitem__(self, key, value):
- self.obj[key] = value
-
- def __delitem__(self, key):
- if not isinstance(key, str):
- key = key.Name
- del self.obj[key]
-
- def __contains__(self, item):
- return item in self.obj
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def names(self):
- return self.obj.ElementNames
-
- def new(self, name: str):
- obj = self._doc.create_instance('com.sun.star.style.CellStyle')
- self.obj[name] = obj
- return LOCellStyle(obj)
-
-
-class LODocCalc(LODocument):
- """Classe for Calc module"""
- TYPE_RANGES = ('ScCellObj', 'ScCellRangeObj')
- RANGES = 'ScCellRangesObj'
- SHAPE = 'com.sun.star.drawing.SvxShapeCollection'
- _type = 'calc'
-
- def __init__(self, obj):
- super().__init__(obj)
- self._sheets = obj.Sheets
- self._listener_range_selection = None
-
- def __getitem__(self, index):
- """Index access"""
- return LOCalcSheet(self._sheets[index])
-
- def __setitem__(self, key: str, value: Any):
- """Insert new sheet"""
- self._sheets[key] = value
-
- def __len__(self):
- return self._sheets.Count
-
- def __contains__(self, item):
- return item in self._sheets
-
- def __iter__(self):
- self._i = 0
- return self
-
- def __next__(self):
- try:
- sheet = LOCalcSheet(self._sheets[self._i])
- except Exception as e:
- raise StopIteration
- self._i += 1
- return sheet
-
- def __str__(self):
- return f'Calc: {self.title}'
-
- @property
- def selection(self):
- sel = self.obj.CurrentSelection
- type_obj = sel.ImplementationName
- if type_obj in self.TYPE_RANGES:
- sel = LOCalcRange(sel)
- elif type_obj == self.RANGES:
- sel = LOCalcRanges(sel)
- elif type_obj == self.SHAPE:
- if len(sel) == 1:
- sel = LOShape(sel[0])
- else:
- sel = LOShapes(sel)
- else:
- debug(type_obj)
- return sel
-
- @property
- def headers(self):
- """Get true if is visible columns/rows headers"""
- return self._cc.ColumnRowHeaders
- @headers.setter
- def headers(self, value):
- """Set visible columns/rows headers"""
- self._cc.ColumnRowHeaders = value
-
- @property
- def tabs(self):
- """Get true if is visible tab sheets"""
- return self._cc.SheetTabs
- @tabs.setter
- def tabs(self, value):
- """Set visible tab sheets"""
- self._cc.SheetTabs = value
-
- @property
- def names(self):
- """Get all sheet names"""
- names = self.obj.Sheets.ElementNames
- return names
-
- @property
- def active(self):
- """Get active sheet"""
- return LOCalcSheet(self._cc.ActiveSheet)
-
- @property
- def new_sheet(self):
- sheet = self._create_instance('com.sun.star.sheet.Spreadsheet')
- return sheet
-
- @property
- def events(self):
- return LOEvents(self.obj.Events)
-
- @property
- def cs(self):
- return self.cell_styles
- @property
- def cell_styles(self):
- obj = self.obj.StyleFamilies['CellStyles']
- return LOCellStyles(obj, self)
-
- def activate(self, sheet: Any):
- """Activate sheet
-
- :param sheet: Sheet to activate
- :type sheet: str, pyUno or LOCalcSheet
- """
- obj = sheet
- if isinstance(sheet, LOCalcSheet):
- obj = sheet.obj
- elif isinstance(sheet, str):
- obj = self._sheets[sheet]
- self._cc.setActiveSheet(obj)
- return
-
- def insert(self, name: Union[str, list, tuple]):
- """Insert new sheet
-
- :param name: Name new sheet, or iterable with names.
- :type name: str, list or tuple
- :return: New last instance sheet.
- :rtype: LOCalcSheet
- """
- names = name
- if isinstance(name, str):
- names = (name,)
- for n in names:
- self._sheets[n] = self._create_instance('com.sun.star.sheet.Spreadsheet')
- return LOCalcSheet(self._sheets[n])
-
- def remove(self, name: str):
- """Remove sheet by name
-
- :param name: Name sheet will remove
- :type name: str
- """
- if isinstance(name, LOCalcSheet):
- name = name.name
- self._sheets.removeByName(name)
- return
-
- def move(self, name:str, pos: int=-1):
- """Move sheet name to position
-
- :param name: Name sheet to move
- :type name: str
- :param pos: New position, if pos=-1 move to end
- :type pos: int
- """
- index = pos
- if pos < 0:
- index = len(self)
- if isinstance(name, LOCalcSheet):
- name = name.name
- self._sheets.moveByName(name, index)
- return
-
- def _get_new_name_sheet(self, name):
- i = 1
- new_name = f'{name}_{i}'
- while new_name in self:
- i += 1
- new_name = f'{name}_{i}'
- return new_name
-
- def copy_sheet(self, name: Any, new_name: str='', pos: int=-1):
- """Copy sheet by name
-
- """
- if isinstance(name, LOCalcSheet):
- name = name.name
- index = pos
- if pos < 0:
- index = len(self)
- if not new_name:
- new_name = self._get_new_name_sheet(name)
- self._sheets.copyByName(name, new_name, index)
- return LOCalcSheet(self._sheets[new_name])
-
- def copy_from(self, doc: Any, source: Any=None, target: Any=None, pos: int=-1):
- """Copy sheet from document
-
- """
- index = pos
- if pos < 0:
- index = len(self)
-
- names = source
- if not source:
- names = doc.names
- elif isinstance(source, str):
- names = (source,)
- elif isinstance(source, LOCalcSheet):
- names = (source.name,)
-
- new_names = target
- if not target:
- new_names = names
- elif isinstance(target, str):
- new_names = (target,)
-
- for i, name in enumerate(names):
- self._sheets.importSheet(doc.obj, name, index + i)
- self[index + i].name = new_names[i]
-
- return LOCalcSheet(self._sheets[index])
-
- def sort(self, reverse=False):
- """Sort sheets by name
-
- :param reverse: For order in reverse
- :type reverse: bool
- """
- names = sorted(self.names, reverse=reverse)
- for i, n in enumerate(names):
- self.move(n, i)
- return
-
- @run_in_thread
- def start_range_selection(self, controllers: Any, args: dict={}):
- """Start select range selection by user
-
- `See Api RangeSelectionArguments `_
- """
- if args:
- args['CloseOnMouseRelease'] = args.get('CloseOnMouseRelease', True)
- else:
- args = dict(
- Title = 'Please select a range',
- CloseOnMouseRelease = True)
- properties = dict_to_property(args)
-
- self._listener_range_selection = EventsRangeSelectionListener(controllers(self))
- self._cc.addRangeSelectionListener(self._listener_range_selection)
- self._cc.startRangeSelection(properties)
- return
-
- def remove_range_selection_listener(self):
- if not self._listener_range_selection is None:
- self._cc.removeRangeSelectionListener(self._listener_range_selection)
- return
-
- def select(self, rango: Any):
- obj = rango
- if hasattr(rango, 'obj'):
- obj = rango.obj
- self._cc.select(obj)
- return
-
- @property
- def ranges(self):
- obj = self._create_instance('com.sun.star.sheet.SheetCellRanges')
- return LOCalcRanges(obj)
-
- def get_ranges(self, address: str):
- ranges = self.ranges
- ranges.add([sheet[address] for sheet in self])
- return ranges
-
-
-class LOCalcSheet(object):
-
- def __init__(self, obj):
- self._obj = obj
-
- def __getitem__(self, index):
- return LOCalcRange(self.obj[index])
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- pass
-
- def __str__(self):
- return f'Sheet: {self.name}'
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def doc(self):
- return LODocCalc(self.obj.DrawPage.Forms.Parent)
-
- @property
- def name(self):
- return self._obj.Name
- @name.setter
- def name(self, value):
- self._obj.Name = value
-
- @property
- def code_name(self):
- return self._obj.CodeName
- @code_name.setter
- def code_name(self, value):
- self._obj.CodeName = value
-
- @property
- def visible(self):
- return self._obj.IsVisible
- @visible.setter
- def visible(self, value):
- self._obj.IsVisible = value
-
- @property
- def color(self):
- return self._obj.TabColor
- @color.setter
- def color(self, value):
- self._obj.TabColor = Color()(value)
-
- @property
- def events(self):
- return LOEvents(self.obj.Events)
-
- @property
- def used_area(self):
- cursor = self.create_cursor()
- cursor.gotoEndOfUsedArea(True)
- return self[cursor.AbsoluteName]
-
- @property
- def is_protected(self):
- return self._obj.isProtected()
-
- @property
- def password(self):
- return ''
- @password.setter
- def password(self, value):
- self.obj.protect(value)
-
- def unprotect(self, value):
- try:
- self.obj.unprotect(value)
- return True
- except:
- pass
- return False
-
- def move(self, pos: int=-1):
- index = pos
- if pos < 0:
- index = len(self.doc)
- self.doc.move(self.name, index)
- return
-
- def remove(self):
- self.doc.remove(self.name)
- return
-
- def copy(self, new_name: str='', pos: int=-1):
- index = pos
- if pos < 0:
- index = len(self.doc)
- new_sheet = self.doc.copy_sheet(self.name, new_name, index)
- return new_sheet
-
- def copy_to(self, doc: Any, target: str='', pos: int=-1):
- index = pos
- if pos < 0:
- index = len(doc)
-
- new_name = target or self.name
- sheet = doc.copy_from(self.doc, self.name, new_name, index)
- return sheet
-
- def activate(self):
- self.doc.activate(self.obj)
- return
-
- def create_cursor(self, rango: Any=None):
- if rango is None:
- cursor = self.obj.createCursor()
- else:
- obj = rango
- if hasattr(rango, 'obj'):
- obj = rango.obj
- cursor = self.obj.createCursorByRange(obj)
- return cursor
-
-
-class LOCalcRanges(object):
-
- def __init__(self, obj):
- self._obj = obj
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- pass
-
- def __len__(self):
- return self._obj.Count
-
- def __iter__(self):
- self._index = 0
- return self
-
- def __next__(self):
- try:
- r = self.obj[self._index]
- rango = LOCalcRange(r)
- except IndexError:
- raise StopIteration
-
- self._index += 1
- return rango
-
- def __contains__(self, item):
- return self._obj.hasByName(item.name)
-
- def __getitem__(self, index):
- r = self.obj[index]
- rango = LOCalcRange(r)
- return rango
-
- def __str__(self):
- s = f'Ranges: {self.names}'
- return s
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def names(self):
- return self.obj.ElementNames
-
- @property
- def data(self):
- rows = [r.data for r in self]
- return rows
- @data.setter
- def data(self, values):
- for i, data in enumerate(values):
- self[i].data = data
-
- @property
- def style(self):
- return ''
- @style.setter
- def style(self, value):
- for r in self:
- r.style = value
-
- def add(self, rangos: Any):
- if isinstance(rangos, LOCalcRange):
- rangos = (rangos,)
- for r in rangos:
- self.obj.addRangeAddress(r.range_address, False)
- return
-
- def remove(self, rangos: Any):
- if isinstance(rangos, LOCalcRange):
- rangos = (rangos,)
- for r in rangos:
- self.obj.removeRangeAddress(r.range_address)
- return
-
-
-class LOCalcRange(object):
- CELL = 'ScCellObj'
-
- def __init__(self, obj):
- self._obj = obj
- self._is_cell = obj.ImplementationName == self.CELL
-
- def __getitem__(self, index):
- return LOCalcRange(self.obj[index])
-
- def __iter__(self):
- self._r = 0
- self._c = 0
- return self
-
- def __next__(self):
- try:
- rango = self[self._r, self._c]
- except Exception as e:
- raise StopIteration
- self._c += 1
- if self._c == self.obj.Columns.Count:
- self._c = 0
- self._r +=1
- return rango
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- pass
-
- def __len__(self):
- ra = self.range_address
- rows = ra.EndRow - ra.StartRow + 1
- cols = ra.EndColumn - ra.StartColumn + 1
- return rows, cols
-
- def __str__(self):
- s = f'Range: {self.name}'
- if self.is_cell:
- s = f'Cell: {self.name}'
- return s
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def is_cell(self):
- return self._is_cell
-
- @property
- def name(self):
- return self.obj.AbsoluteName
-
- @property
- def address(self):
- return self.obj.CellAddress
-
- @property
- def range_address(self):
- return self.obj.RangeAddress
-
- @property
- def sheet(self):
- return LOCalcSheet(self.obj.Spreadsheet)
-
- @property
- def doc(self):
- doc = self.obj.Spreadsheet.DrawPage.Forms.Parent
- return LODocCalc(doc)
-
- @property
- def cursor(self):
- cursor = self.obj.Spreadsheet.createCursorByRange(self.obj)
- return cursor
-
- def offset(self, rows=0, cols=1):
- ra = self.range_address
- col = ra.EndColumn + cols
- row = ra.EndRow + rows
- return LOCalcRange(self.sheet[row, col].obj)
-
- @property
- def style(self):
- return self.obj.CellStyle
- @style.setter
- def style(self, value):
- self.obj.CellStyle = value
-
- @property
- def string(self):
- return self.obj.String
- @string.setter
- def string(self, value):
- self.obj.setString(value)
-
- @property
- def data(self):
- return self.obj.getDataArray()
- @data.setter
- def data(self, values):
- if self._is_cell:
- self.to_size(len(values[0]), len(values)).data = values
- else:
- self.obj.setDataArray(values)
-
- @property
- def current_region(self):
- cursor = self.cursor
- cursor.collapseToCurrentRegion()
- rango = self.obj.Spreadsheet[cursor.AbsoluteName]
- return LOCalcRange(rango)
-
- def to_size(self, cols: int, rows: int):
- cursor = self.cursor
- cursor.collapseToSize(cols, rows)
- rango = self.obj.Spreadsheet[cursor.AbsoluteName]
- return LOCalcRange(rango)
-
-
-class LOWriterTextRange(object):
-
- def __init__(self, obj, doc):
- self._obj = obj
- self._doc = doc
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def text(self):
- return self.obj.Text
-
- @property
- def cursor(self):
- return self.text.createTextCursorByRange(self.obj)
-
- def insert_comment(self, content: str, author: str='', dt: Any=None):
- # ~ range.Text.insertTextContent(cursor, comment, False)
- comment = self._doc._create_instance('com.sun.star.text.textfield.Annotation')
- comment.Content = content
- comment.Author = author
- comment.attach(self.cursor.End)
- return
-
-
-class LODocWriter(LODocument):
- _type = 'writer'
- TEXT_RANGES = 'SwXTextRanges'
-
- def __init__(self, obj):
- super().__init__(obj)
- self._view_settings = self._cc.ViewSettings
-
- @property
- def selection(self):
- sel = self.obj.CurrentSelection
- type_obj = sel.ImplementationName
- if type_obj == self.TEXT_RANGES:
- if len(sel) == 1:
- sel = LOWriterTextRange(sel[0], self)
-
- return sel
-
- @property
- def zoom(self):
- return self._view_settings.ZoomValue
- @zoom.setter
- def zoom(self, value):
- self._view_settings.ZoomValue = value
-
-
-class LODocDrawImpress(LODocument):
-
- def __init__(self, obj):
- super().__init__(obj)
-
-
-class LODocDraw(LODocDrawImpress):
- _type = 'draw'
-
- def __init__(self, obj):
- super().__init__(obj)
-
-
-class LODocImpress(LODocDrawImpress):
- _type = 'impress'
-
- def __init__(self, obj):
- super().__init__(obj)
-
-
-class LODocMath(LODocDrawImpress):
- _type = 'math'
-
- def __init__(self, obj):
- super().__init__(obj)
-
-
-class LODocBase(LODocument):
- _type = 'base'
-
- def __init__(self, obj):
- super().__init__(obj)
-
-
-class LODocIDE(LODocument):
- _type = 'basicide'
-
- def __init__(self, obj):
- super().__init__(obj)
-
-
-class LODocuments():
- """Classe for documents
- """
- TYPES = {
- 'calc': 'com.sun.star.sheet.SpreadsheetDocument',
- 'writerr': 'com.sun.star.text.TextDocument',
- 'draw': 'com.sun.star.drawing.DrawingDocument',
- 'impress': 'com.sun.star.presentation.PresentationDocument',
- 'math': 'com.sun.star.formula.FormulaProperties',
- 'ide': 'com.sun.star.script.BasicIDE',
- 'base': 'com.sun.star.sdb.OfficeDatabaseDocument',
- 'main': 'com.sun.star.frame.StartModule',
- }
- _classes = {
- 'com.sun.star.sheet.SpreadsheetDocument': LODocCalc,
- 'com.sun.star.text.TextDocument': LODocWriter,
- 'com.sun.star.drawing.DrawingDocument': LODocDraw,
- 'com.sun.star.presentation.PresentationDocument': LODocImpress,
- 'com.sun.star.formula.FormulaProperties': LODocMath,
- 'com.sun.star.script.BasicIDE': LODocIDE,
- 'com.sun.star.sdb.OfficeDatabaseDocument': LODocBase,
- 'com.sun.star.frame.StartModule': LODocMain
- }
- # ~ BASE: 'com.sun.star.sdb.DocumentDataSource',
-
- def __init__(self):
- self._desktop = LOMain.desktop
-
- def __len__(self):
- # ~ len(self._desktop.Components)
- for i, _ in enumerate(self._desktop.Components):
- pass
- return i + 1
-
- def __getitem__(self, index):
- # ~ self._desktop.Components[index]
- obj = None
-
- for i, doc in enumerate(self._desktop.Components):
- if isinstance(index, int) and i == index:
- obj = self._get_class_doc(doc)
- break
- elif isinstance(index, str) and doc.Title == index:
- obj = self._get_class_doc(doc)
- break
-
- return obj
-
- def __contains__(self, item):
- doc = self[item]
- return not doc is None
-
- def __iter__(self):
- self._i = -1
- return self
-
- def __next__(self):
- self._i += 1
- doc = self[self._i]
- if doc is None:
- raise StopIteration
- else:
- return doc
-
- def _get_class_doc(self, doc):
- """Identify type doc"""
- main = 'com.sun.star.frame.StartModule'
- if doc.supportsService(main):
- return self._classes[main](doc)
-
- mm = create_instance('com.sun.star.frame.ModuleManager')
- type_module = mm.identify(doc)
- return self._classes[type_module](doc)
-
- @property
- def active(self):
- """Get active doc"""
- doc = self._desktop.getCurrentComponent()
- obj = self._get_class_doc(doc)
- return obj
-
- def new(self, type_doc: str='calc', args: dict={}):
- """Create new document
-
- :param type_doc: The type doc to create, default is Calc
- :type type_doc: str
- :param args: Extra argument
- :type args: dict
- :return: New document
- :rtype: Custom classe
- """
- url = f'private:factory/s{type_doc}'
- opt = dict_to_property(args)
- doc = self._desktop.loadComponentFromURL(url, '_default', 0, opt)
- obj = self._get_class_doc(doc)
- return obj
-
- def open(self, path: str, args: dict={}):
- """ Open document from path
-
- :param path: Path to document
- :type path: str
- :param args: Extra argument
- Usually options:
- Hidden: True or False
- AsTemplate: True or False
- ReadOnly: True or False
- Password: super_secret
- MacroExecutionMode: 4 = Activate macros
- Preview: True or False
- :type args: dict
-
- `See API XComponentLoader `_
- `See API MediaDescriptor `_
- """
- url = Paths.to_url(path)
- opt = dict_to_property(args)
- doc = self._desktop.loadComponentFromURL(url, '_default', 0, opt)
- if doc is None:
- return
-
- obj = self._get_class_doc(doc)
- return obj
-
-
-def __getattr__(name):
- classes = {
- 'inspect': LOInspect,
- 'dates': Dates,
- 'json': Json,
- 'macro': Macro,
- 'shell': Shell,
- 'timer': Timer,
- 'hash': Hash,
- 'path': Paths,
- 'config': Config,
- 'url': Url,
- 'email': Email,
- 'color': Color(),
- 'io': IOStream,
- 'clipboard': ClipBoard,
- 'shortcuts': LOShortCuts(),
- 'menus': LOMenus(),
- 'lo': LOMain,
- 'command': LOMain.cmd,
- 'docs': LODocuments(),
- 'active': LODocuments().active,
- }
- if name in classes:
- return classes[name]
-
- raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
-
-
-class LOServer(object):
- """Started LibeOffice like server
- """
- HOST = 'localhost'
- PORT = '8100'
- ARG = f'socket,host={HOST},port={PORT};urp;StarOffice.ComponentContext'
- CMD = ['soffice',
- '-env:SingleAppInstance=false',
- '-env:UserInstallation=file:///tmp/LO_Process8100',
- '--headless', '--norestore', '--invisible',
- f'--accept={ARG}']
-
- def __init__(self):
- self._server = None
- self._ctx = None
- self._sm = None
- self._start_server()
- self._init_values()
-
- def _init_values(self):
- global CTX
- global SM
-
- if not self.is_running:
- return
-
- ctx = uno.getComponentContext()
- service = 'com.sun.star.bridge.UnoUrlResolver'
- resolver = ctx.ServiceManager.createInstanceWithContext(service, ctx)
- self._ctx = resolver.resolve('uno:{}'.format(self.ARG))
- self._sm = self._ctx.getServiceManager()
- CTX = self._ctx
- SM = self._sm
- return
-
- @property
- def is_running(self):
- try:
- s = socket.create_connection((self.HOST, self.PORT), 5.0)
- s.close()
- debug('LibreOffice is running...')
- return True
- except ConnectionRefusedError:
- return False
-
- def _start_server(self):
- if self.is_running:
- return
-
- for i in range(3):
- self._server = subprocess.Popen(self.CMD,
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- time.sleep(3)
- if self.is_running:
- break
- return
-
- def stop(self):
- """Stop server
- """
- if self._server is None:
- print('Search pgrep soffice')
- else:
- self._server.terminate()
- debug('LibreOffice is stop...')
- return
-
- def _create_instance(self, name, with_context=True):
- if with_context:
- instance = self._sm.createInstanceWithContext(name, self._ctx)
- else:
- instance = self._sm.createInstance(name)
- return instance
diff --git a/source/easymacro (copy).bk2 b/source/easymacro (copy).bk2
deleted file mode 100644
index 0d78e21..0000000
--- a/source/easymacro (copy).bk2
+++ /dev/null
@@ -1,4671 +0,0 @@
-#!/usr/bin/env python3
-
-# == Rapid Develop Macros in LibreOffice ==
-
-import base64
-import ctypes
-import gettext
-import zipfile
-
-from collections import OrderedDict
-from collections.abc import MutableMapping
-from decimal import Decimal
-from enum import IntEnum
-
-import imaplib
-
-from com.sun.star.awt.PosSize import POSSIZE, SIZE
-
-from com.sun.star.sheet import TableFilterField
-from com.sun.star.table.CellContentType import EMPTY, VALUE, TEXT, FORMULA
-from com.sun.star.util import Time, Date, DateTime
-
-from com.sun.star.text.ControlCharacter import PARAGRAPH_BREAK
-
-from com.sun.star.lang import Locale
-from com.sun.star.awt import XActionListener
-from com.sun.star.awt import XMenuListener
-from com.sun.star.awt import XMouseListener
-from com.sun.star.awt import XMouseMotionListener
-from com.sun.star.awt import XFocusListener
-from com.sun.star.awt import XKeyListener
-from com.sun.star.awt import XItemListener
-from com.sun.star.awt import XTabListener
-from com.sun.star.awt import XSpinListener
-from com.sun.star.awt import XWindowListener
-from com.sun.star.awt import XTopWindowListener
-from com.sun.star.awt.grid import XGridDataListener
-from com.sun.star.awt.grid import XGridSelectionListener
-from com.sun.star.script import ScriptEventDescriptor
-
-
-# ~ https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1awt_1_1FontUnderline.html
-from com.sun.star.awt import FontUnderline
-from com.sun.star.style.VerticalAlignment import TOP, MIDDLE, BOTTOM
-
-from com.sun.star.view.SelectionType import SINGLE, MULTI, RANGE
-
-from com.sun.star.sdb.CommandType import TABLE, QUERY, COMMAND
-
-try:
- from peewee import Database, DateTimeField, DateField, TimeField, \
- __exception_wrapper__
-except ImportError as e:
- Database = DateField = TimeField = DateTimeField = object
- print('You need install peewee, only if you will develop with Base')
-
-
-LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
-LOG_DATE = '%d/%m/%Y %H:%M:%S'
-logging.addLevelName(logging.ERROR, '\033[1;41mERROR\033[1;0m')
-logging.addLevelName(logging.DEBUG, '\x1b[33mDEBUG\033[1;0m')
-logging.addLevelName(logging.INFO, '\x1b[32mINFO\033[1;0m')
-logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT, datefmt=LOG_DATE)
-log = logging.getLogger(__name__)
-
-
-# ~ You can get custom salt
-# ~ codecs.encode(os.urandom(16), 'hex')
-# ~ but, not modify this file, modify in import file
-SALT = b'c9548699d4e432dfd2b46adddafbb06d'
-
-LOG_NAME = 'ZAZ'
-
-LEFT = 0
-CENTER = 1
-RIGHT = 2
-
-CALC = 'calc'
-WRITER = 'writer'
-DRAW = 'draw'
-IMPRESS = 'impress'
-BASE = 'base'
-MATH = 'math'
-BASIC = 'basic'
-MAIN = 'main'
-TYPE_DOC = {
- CALC: 'com.sun.star.sheet.SpreadsheetDocument',
- WRITER: 'com.sun.star.text.TextDocument',
- DRAW: 'com.sun.star.drawing.DrawingDocument',
- IMPRESS: 'com.sun.star.presentation.PresentationDocument',
- MATH: 'com.sun.star.formula.FormulaProperties',
- BASE: 'com.sun.star.sdb.DocumentDataSource',
- BASIC: 'com.sun.star.script.BasicIDE',
- MAIN: 'com.sun.star.frame.StartModule',
-}
-
-OBJ_SHAPE = 'com.sun.star.comp.sc.ScShapeObj'
-OBJ_GRAPHIC = 'SwXTextGraphicObject'
-
-OBJ_TEXTS = 'SwXTextRanges'
-OBJ_TEXT = 'SwXTextRange'
-
-CLSID = {
- 'FORMULA': '078B7ABA-54FC-457F-8551-6147e776a997',
-}
-
-SERVICES = {
- 'TEXT_EMBEDDED': 'com.sun.star.text.TextEmbeddedObject',
- 'TEXT_TABLE': 'com.sun.star.text.TextTable',
- 'GRAPHIC': 'com.sun.star.text.GraphicObject',
-}
-
-
-# ~ from com.sun.star.sheet.FilterOperator import EMPTY, NO_EMPTY, EQUAL, NOT_EQUAL
-class FilterOperator(IntEnum):
- EMPTY = 0
- NO_EMPTY = 1
- EQUAL = 2
- NOT_EQUAL = 3
-
-# ~ https://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1awt_1_1UnoControlEditModel.html#a54d3ff280d892218d71e667f81ce99d4
-class Border(IntEnum):
- NO_BORDER = 0
- BORDER = 1
- SIMPLE = 2
-
-
-# ~ https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1sheet.html#aa5aa6dbecaeb5e18a476b0a58279c57a
-class ValidationType():
- from com.sun.star.sheet.ValidationType \
- import ANY, WHOLE, DECIMAL, DATE, TIME, TEXT_LEN, LIST, CUSTOM
-VT = ValidationType
-
-
-# ~ https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1sheet.html#aecf58149730f4c8c5c18c70f3c7c5db7
-class ValidationAlertStyle():
- from com.sun.star.sheet.ValidationAlertStyle \
- import STOP, WARNING, INFO, MACRO
-VAS = ValidationAlertStyle
-
-
-# ~ https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1sheet_1_1ConditionOperator2.html
-class ConditionOperator():
- from com.sun.star.sheet.ConditionOperator2 \
- import NONE, EQUAL, NOT_EQUAL, GREATER, GREATER_EQUAL, LESS, \
- LESS_EQUAL, BETWEEN, NOT_BETWEEN, FORMULA, DUPLICATE, NOT_DUPLICATE
-CO = ConditionOperator
-
-
-class DataPilotFieldOrientation():
- from com.sun.star.sheet.DataPilotFieldOrientation \
- import HIDDEN, COLUMN, ROW, PAGE, DATA
-DPFO = DataPilotFieldOrientation
-
-
-class CellInsertMode():
- from com.sun.star.sheet.CellInsertMode import DOWN, RIGHT, ROWS, COLUMNS
-CIM = CellInsertMode
-
-
-class CellDeleteMode():
- from com.sun.star.sheet.CellDeleteMode import UP, LEFT, ROWS, COLUMNS
-CDM = CellDeleteMode
-
-
-class FormButtonType():
- from com.sun.star.form.FormButtonType import PUSH, SUBMIT, RESET, URL
-FBT = FormButtonType
-
-
-class TextContentAnchorType():
- from com.sun.star.text.TextContentAnchorType \
- import AT_PARAGRAPH, AS_CHARACTER, AT_PAGE, AT_FRAME, AT_CHARACTER
-TCAT = TextContentAnchorType
-
-
-SECONDS_DAY = 60 * 60 * 24
-DIR = {
- 'images': 'images',
- 'locales': 'locales',
-}
-
-KEY = {
- 'enter': 1280,
-}
-
-DEFAULT_MIME_TYPE = 'png'
-MIME_TYPE = {
- 'png': 'image/png',
- 'jpg': 'image/jpeg',
-}
-
-
-try:
- COUNTRY = LANGUAGE.split('-')[1]
-except:
- COUNTRY = ''
-LOCALE = Locale(LANG, COUNTRY, '')
-
-
-def inspect(obj: Any, to_sheet: bool=True) -> None:
- if hasattr(obj, 'obj'):
- obj = obj.obj
-
- if to_sheet:
- _inspect_to_sheet(obj)
- else:
- zaz = create_instance('net.elmau.zaz.inspect')
- zaz.inspect(obj)
- return
-
-
-def get_type_doc(obj: Any) -> str:
- for k, v in TYPE_DOC.items():
- if obj.supportsService(v):
- return k
- return ''
-
-
-def _get_class_doc(obj: Any) -> Any:
- classes = {
- CALC: LOCalc,
- WRITER: LOWriter,
- DRAW: LODraw,
- IMPRESS: LOImpress,
- BASE: LOBase,
- MATH: LOMath,
- BASIC: LOBasic,
- }
- type_doc = get_type_doc(obj)
- return classes[type_doc](obj)
-
-
-def _date_to_struct(value):
- if isinstance(value, datetime.datetime):
- d = DateTime()
- d.Year = value.year
- d.Month = value.month
- d.Day = value.day
- d.Hours = value.hour
- d.Minutes = value.minute
- d.Seconds = value.second
- elif isinstance(value, datetime.date):
- d = Date()
- d.Day = value.day
- d.Month = value.month
- d.Year = value.year
- elif isinstance(value, datetime.time):
- d = Time()
- d.Hours = value.hour
- d.Minutes = value.minute
- d.Seconds = value.second
- return d
-
-
-def _struct_to_date(value):
- d = None
- if isinstance(value, Time):
- d = datetime.time(value.Hours, value.Minutes, value.Seconds)
- elif isinstance(value, Date):
- if value != Date():
- d = datetime.date(value.Year, value.Month, value.Day)
- elif isinstance(value, DateTime):
- if value.Year > 0:
- d = datetime.datetime(
- value.Year, value.Month, value.Day,
- value.Hours, value.Minutes, value.Seconds)
- return d
-
-
-def install_locales(path: str, domain: str='base', dir_locales=DIR['locales']):
- path_locales = _P.join(_P(path).path, dir_locales)
- try:
- lang = gettext.translation(domain, path_locales, languages=[LANG])
- lang.install()
- _ = lang.gettext
- except Exception as e:
- from gettext import gettext as _
- error(e)
- return _
-
-
-def _export_image(obj, args):
- name = 'com.sun.star.drawing.GraphicExportFilter'
- exporter = create_instance(name)
- path = _P.to_system(args['URL'])
- args = dict_to_property(args)
- exporter.setSourceDocument(obj)
- exporter.filter(args)
- return _P.exists(path)
-
-
-def get_size_screen():
- res = ''
- if IS_WIN:
- user32 = ctypes.windll.user32
- res = f'{user32.GetSystemMetrics(0)}x{user32.GetSystemMetrics(1)}'
- else:
- try:
- args = 'xrandr | grep "*" | cut -d " " -f4'
- res = run(args, split=False)
- except Exception as e:
- error(e)
- return res.strip()
-
-
-def _get_key(password):
- from cryptography.hazmat.primitives import hashes
- from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
-
- kdf = PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt=SALT,
- iterations=100000)
- key = base64.urlsafe_b64encode(kdf.derive(password.encode()))
- return key
-
-
-def encrypt(data, password):
- from cryptography.fernet import Fernet
-
- f = Fernet(_get_key(password))
- if isinstance(data, str):
- data = data.encode()
- token = f.encrypt(data).decode()
- return token
-
-
-def decrypt(token, password):
- from cryptography.fernet import Fernet, InvalidToken
-
- data = ''
- f = Fernet(_get_key(password))
- try:
- data = f.decrypt(token.encode()).decode()
- except InvalidToken as e:
- error('Invalid Token')
- return data
-
-
-def switch_design_mode(doc):
- call_dispatch(doc.frame, '.uno:SwitchControlDesignMode')
- return
-
-
-class ImapServer(object):
-
- def __init__(self, config):
- self._server = None
- self._error = ''
- self._is_connect = self._login(config)
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- self.close()
-
- @property
- def is_connect(self):
- return self._is_connect
-
- @property
- def error(self):
- return self._error
-
- def _login(self, config):
- try:
- # ~ hosts = 'gmail' in config['server']
- if config['ssl']:
- self._server = imaplib.IMAP4_SSL(config['server'], config['port'])
- else:
- self._server = imaplib.IMAP4(config['server'], config['port'])
- self._server.login(config['user'], config['password'])
- self._server.select()
- return True
- except imaplib.IMAP4.error as e:
- self._error = str(e)
- return False
- except Exception as e:
- self._error = str(e)
- return False
- return False
-
- def get_folders(self, exclude=()):
- folders = {}
- result, subdir = self._server.list()
- for s in subdir:
- print(s.decode('utf-8'))
- return folders
-
- def close(self):
- try:
- self._server.close()
- self._server.logout()
- msg = 'Close connection...'
- debug(msg)
- except:
- pass
- return
-
-
-# ~ Classes
-
-class LOBaseObject(object):
-
- def __init__(self, obj):
- self._obj = obj
-
- def __setattr__(self, name, value):
- exists = hasattr(self, name)
- if not exists and not name in ('_obj', '_index', '_view'):
- setattr(self._obj, name, value)
- else:
- super().__setattr__(name, value)
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- pass
-
- @property
- def obj(self):
- return self._obj
-
-
-class LOCalc(LODocument):
-
- @property
- def db_ranges(self):
- # ~ return LOCalcDataBaseRanges(self.obj.DataBaseRanges)
- return self.obj.DatabaseRanges
-
- def render(self, data, sheet=None, clean=True):
- if sheet is None:
- sheet = self.active
- return sheet.render(data, clean=clean)
-
-
-class LOChart(object):
-
- def __init__(self, name, obj, draw_page):
- self._name = name
- self._obj = obj
- self._eobj = self._obj.EmbeddedObject
- self._type = 'Column'
- self._cell = None
- self._shape = self._get_shape(draw_page)
- self._pos = self._shape.Position
-
- def __getitem__(self, index):
- return LOBaseObject(self.diagram.getDataRowProperties(index))
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- pass
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def name(self):
- return self._name
-
- @property
- def diagram(self):
- return self._eobj.Diagram
-
- @property
- def type(self):
- return self._type
- @type.setter
- def type(self, value):
- self._type = value
- if value == 'Bar':
- self.diagram.Vertical = True
- return
- type_chart = f'com.sun.star.chart.{value}Diagram'
- self._eobj.setDiagram(self._eobj.createInstance(type_chart))
-
- @property
- def cell(self):
- return self._cell
- @cell.setter
- def cell(self, value):
- self._cell = value
- self._shape.Anchor = value.obj
-
- @property
- def position(self):
- return self._pos
- @position.setter
- def position(self, value):
- self._pos = value
- self._shape.Position = value
-
- def _get_shape(self, draw_page):
- for shape in draw_page:
- if shape.PersistName == self.name:
- break
- return shape
-
-
-class LOSheetCharts(object):
-
- def __init__(self, obj, sheet):
- self._obj = obj
- self._sheet = sheet
-
- def __getitem__(self, index):
- return LOChart(index, self.obj[index], self._sheet.draw_page)
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- pass
-
- def __contains__(self, item):
- return item in self.obj
-
- def __len__(self):
- return len(self.obj)
-
- @property
- def obj(self):
- return self._obj
-
- def new(self, name, pos_size, data):
- self.obj.addNewByName(name, pos_size, data, True, True)
- return LOChart(name, self.obj[name], self._sheet.draw_page)
-
-
-class LOSheetTableField(object):
-
- def __init__(self, obj):
- self._obj = obj
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- pass
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def name(self):
- return self.obj.Name
-
- @property
- def orientation(self):
- return self.obj.Orientation
- @orientation.setter
- def orientation(self, value):
- self.obj.Orientation = value
-
-
-# ~ com.sun.star.sheet.DataPilotFieldOrientation.ROW
-class LOSheetTable(object):
-
- def __init__(self, obj):
- self._obj = obj
- self._source = None
-
- def __getitem__(self, index):
- field = self.obj.DataPilotFields[index]
- return LOSheetTableField(field)
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- pass
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def filter(self):
- return self.obj.ShowFilterButton
- @filter.setter
- def filter(self, value):
- self.obj.ShowFilterButton = value
-
- @property
- def source(self):
- return self._source
- @source.setter
- def source(self, value):
- self._source = value
- self.obj.SourceRange = value.range_address
-
- @property
- def rows(self):
- return self.obj.RowFields
- @rows.setter
- def rows(self, values):
- if not isinstance(values, tuple):
- values = (values,)
- for v in values:
- with self[v] as f:
- f.orientation = DPFO.ROW
- @property
- def columns(self):
- return self.obj.ColumnFields
- @columns.setter
- def columns(self, values):
- if not isinstance(values, tuple):
- values = (values,)
- for v in values:
- with self[v] as f:
- f.orientation = DPFO.COLUMN
-
- @property
- def data(self):
- return self.obj.DataFields
- @data.setter
- def data(self, values):
- if not isinstance(values, tuple):
- values = (values,)
- for v in values:
- with self[v] as f:
- f.orientation = DPFO.DATA
-
-
-class LOSheetTables(object):
-
- def __init__(self, obj, sheet):
- self._obj = obj
- self._sheet = sheet
-
- def __getitem__(self, index):
- return LOSheetTable(self.obj[index])
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- pass
-
- def __contains__(self, item):
- return item in self.obj
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def count(self):
- return self.obj.Count
-
- @property
- def names(self):
- return self.obj.ElementNames
-
- def new(self, name, target):
- table = self.obj.createDataPilotDescriptor()
- self.obj.insertNewByName(name, target.address, table)
- return LOSheetTable(self.obj[name])
-
- def remove(self, name):
- self.obj.removeByName(name)
- return
-
-
-# ~ class LOFormControl(LOBaseObject):
-class LOFormControl():
- EVENTS = {
- 'action': 'actionPerformed',
- 'click': 'mousePressed',
- }
- TYPES = {
- 'actionPerformed': 'XActionListener',
- 'mousePressed': 'XMouseListener',
- }
-
- def __init__(self, obj, view, form):
- self._obj = obj
- self._view = view
- self._form = form
- self._m = view.Model
- self._index = -1
-
- # ~ def __setattr__(self, name, value):
- # ~ if name in ('_form', '_view', '_m', '_index'):
- # ~ self.__dict__[name] = value
- # ~ else:
- # ~ super().__setattr__(name, value)
-
- def __str__(self):
- return f'{self.name} ({self.type}) {[self.index]}'
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def form(self):
- return self._form
-
- @property
- def doc(self):
- return self.obj.Parent.Forms.Parent
-
- @property
- def name(self):
- return self._m.Name
- @name.setter
- def name(self, value):
- self._m.Name = value
-
- @property
- def tag(self):
- return self._m.Tag
- @tag.setter
- def tag(self, value):
- self._m.Tag = value
-
- @property
- def index(self):
- return self._index
- @index.setter
- def index(self, value):
- self._index = value
-
- @property
- def enabled(self):
- return self._m.Enabled
- @enabled.setter
- def enabled(self, value):
- self._m.Enabled = value
-
- @property
- def anchor(self):
- return self.obj.Anchor
- @anchor.setter
- def anchor(self, value):
- size = None
- if hasattr(value, 'obj'):
- size = getattr(value, 'size', None)
- value = value.obj
- self.obj.Anchor = value
- if not size is None:
- self.size = size
- try:
- self.obj.ResizeWithCell = True
- except:
- pass
-
- @property
- def size(self):
- return self.obj.Size
- @size.setter
- def size(self, value):
- self.obj.Size = value
-
- @property
- def events(self):
- return self.form.getScriptEvents(self.index)
- def add_event(self, name, macro):
- if not 'name' in macro:
- macro['name'] = '{}_{}'.format(self.name, name)
-
- event = ScriptEventDescriptor()
- event.AddListenerParam = ''
- event.EventMethod = self.EVENTS[name]
- event.ListenerType = self.TYPES[event.EventMethod]
- event.ScriptCode = _get_url_script(macro)
- event.ScriptType = 'Script'
-
- for ev in self.events:
- if ev.EventMethod == event.EventMethod and \
- ev.ListenerType == event.ListenerType:
- self.form.revokeScriptEvent(self.index,
- event.ListenerType, event.EventMethod, event.AddListenerParam)
- break
-
- self.form.registerScriptEvent(self.index, event)
- return
-
- def set_focus(self):
- self._view.setFocus()
- return
-
-
-class LOFormControlLabel(LOFormControl):
-
- def __init__(self, obj, view, form):
- super().__init__(obj, view, form)
-
- @property
- def type(self):
- return 'label'
-
- @property
- def value(self):
- return self._m.Label
- @value.setter
- def value(self, value):
- self._m.Label = value
-
-
-class LOFormControlText(LOFormControl):
-
- def __init__(self, obj, view, form):
- super().__init__(obj, view, form)
-
- @property
- def type(self):
- return 'text'
-
- @property
- def value(self):
- return self._m.Text
- @value.setter
- def value(self, value):
- self._m.Text = value
-
-
-class LOFormControlButton(LOFormControl):
-
- def __init__(self, obj, view, form):
- super().__init__(obj, view, form)
-
- @property
- def type(self):
- return 'button'
-
- @property
- def value(self):
- return self._m.Label
- @value.setter
- def value(self, value):
- self._m.Text = Label
-
- @property
- def url(self):
- return self._m.TargetURL
- @url.setter
- def url(self, value):
- self._m.TargetURL = value
- self._m.ButtonType = FormButtonType.URL
-
-
-FORM_CONTROL_CLASS = {
- 'label': LOFormControlLabel,
- 'text': LOFormControlText,
- 'button': LOFormControlButton,
-}
-
-
-class LOForm(object):
- MODELS = {
- 'label': 'com.sun.star.form.component.FixedText',
- 'text': 'com.sun.star.form.component.TextField',
- 'button': 'com.sun.star.form.component.CommandButton',
- }
-
- def __init__(self, obj, draw_page):
- self._obj = obj
- self._dp = draw_page
- self._controls = {}
- self._init_controls()
-
- def __getitem__(self, index):
- control = self.obj[index]
- return self._controls[control.Name]
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- pass
-
- def __contains__(self, item):
- return item in self.obj
-
- def __len__(self):
- return len(self.obj)
-
- def __str__(self):
- return f'Form: {self.name}'
-
- def _init_controls(self):
- types = {
- 'com.sun.star.form.OFixedTextModel': 'label',
- 'com.sun.star.form.OEditModel': 'text',
- 'com.sun.star.form.OButtonModel': 'button',
- }
- for i, control in enumerate(self.obj):
- name = control.Name
- tipo = types[control.ImplementationName]
- view = self.doc.CurrentController.getControl(control)
- control = FORM_CONTROL_CLASS[tipo](control, view, self._obj)
- control.index = i
- setattr(self, name, control)
- self._controls[name] = control
- return
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def name(self):
- return self.obj.Name
- @name.setter
- def name(self, value):
- self.obj.Name = value
-
- @property
- def source(self):
- return self.obj.DataSourceName
- @source.setter
- def source(self, value):
- self.obj.DataSourceName = value
-
- @property
- def type(self):
- return self.obj.CommandType
- @type.setter
- def type(self, value):
- self.obj.CommandType = value
-
- @property
- def command(self):
- return self.obj.Command
- @command.setter
- def command(self, value):
- self.obj.Command = value
-
- @property
- def doc(self):
- return self.obj.Parent.Parent
-
- def _special_properties(self, tipo, args):
- if tipo == 'button':
- # ~ if 'ImageURL' in args:
- # ~ args['ImageURL'] = self._set_image_url(args['ImageURL'])
- args['FocusOnClick'] = args.get('FocusOnClick', False)
- return args
- return args
-
- def add(self, args):
- name = args['Name']
- tipo = args.pop('Type').lower()
- w = args.pop('Width', 1000)
- h = args.pop('Height', 200)
- x = args.pop('X', 0)
- y = args.pop('Y', 0)
- control = self.doc.createInstance('com.sun.star.drawing.ControlShape')
- control.setSize(Size(w, h))
- control.setPosition(Point(x, y))
- model = self.doc.createInstance(self.MODELS[tipo])
- args = self._special_properties(tipo, args)
- _set_properties(model, args)
- control.Control = model
- index = len(self)
- self.obj.insertByIndex(index, model)
- self._dp.add(control)
- view = self.doc.CurrentController.getControl(self.obj.getByName(name))
- control = FORM_CONTROL_CLASS[tipo](control, view, self.obj)
- control.index = index
- setattr(self, name, control)
- self._controls[name] = control
- return control
-
-
-class LOSheetForms(object):
-
- def __init__(self, draw_page):
- self._dp = draw_page
- self._obj = draw_page.Forms
-
- def __getitem__(self, index):
- return LOForm(self.obj[index], self._dp)
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- pass
-
- def __contains__(self, item):
- return item in self.obj
-
- def __len__(self):
- return len(self.obj)
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def doc(self):
- return self.obj.Parent
-
- @property
- def count(self):
- return len(self)
-
- @property
- def names(self):
- return self.obj.ElementNames
-
- def insert(self, name=''):
- if not name:
- name = f'form{self.count + 1}'
- form = self.doc.createInstance('com.sun.star.form.component.Form')
- self.obj.insertByName(name, form)
- return LOForm(form, self._dp)
-
- def remove(self, index):
- if isinstance(index, int):
- self.obj.removeByIndex(index)
- else:
- self.obj.removeByName(index)
- return
-
-
-# ~ IsFiltered,
-# ~ IsManualPageBreak,
-# ~ IsStartOfNewPage
-class LOSheetRows(object):
-
- def __init__(self, sheet, obj):
- self._sheet = sheet
- self._obj = obj
-
- def __getitem__(self, index):
- if isinstance(index, int):
- rows = LOSheetRows(self._sheet, self.obj[index])
- else:
- rango = self._sheet[index.start:index.stop,0:]
- rows = LOSheetRows(self._sheet, rango.obj.Rows)
- return rows
-
- def __len__(self):
- return self.obj.Count
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def visible(self):
- return self._obj.IsVisible
- @visible.setter
- def visible(self, value):
- self._obj.IsVisible = value
-
- @property
- def color(self):
- return self.obj.CellBackColor
- @color.setter
- def color(self, value):
- self.obj.CellBackColor = value
-
- @property
- def is_transparent(self):
- return self.obj.IsCellBackgroundTransparent
- @is_transparent.setter
- def is_transparent(self, value):
- self.obj.IsCellBackgroundTransparent = value
-
- @property
- def height(self):
- return self.obj.Height
- @height.setter
- def height(self, value):
- self.obj.Height = value
-
- def optimal(self):
- self.obj.OptimalHeight = True
- return
-
- def insert(self, index, count):
- self.obj.insertByIndex(index, count)
- return
-
- def remove(self, index, count):
- self.obj.removeByIndex(index, count)
- return
-
-
-# ~ IsManualPageBreak,
-# ~ IsStartOfNewPage
-class LOSheetColumns(object):
-
- def __init__(self, sheet, obj):
- self._sheet = sheet
- self._obj = obj
-
- def __getitem__(self, index):
- if isinstance(index, (int, str)):
- rows = LOSheetColumns(self._sheet, self.obj[index])
- else:
- rango = self._sheet[0,index.start:index.stop]
- rows = LOSheetColumns(self._sheet, rango.obj.Columns)
- return rows
-
- def __len__(self):
- return self.obj.Count
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def visible(self):
- return self._obj.IsVisible
- @visible.setter
- def visible(self, value):
- self._obj.IsVisible = value
-
- @property
- def width(self):
- return self.obj.Width
- @width.setter
- def width(self, value):
- self.obj.Width = value
-
- def optimal(self):
- self.obj.OptimalWidth = True
- return
-
- def insert(self, index, count):
- self.obj.insertByIndex(index, count)
- return
-
- def remove(self, index, count):
- self.obj.removeByIndex(index, count)
- return
-
-
-class LOCalcSheet(object):
-
- @property
- def draw_page(self):
- return LODrawPage(self.obj.DrawPage)
- @property
- def dp(self):
- return self.draw_page
-
- @property
- def shapes(self):
- return self.draw_page
-
- @property
- def charts(self):
- return LOSheetCharts(self.obj.Charts, self)
-
- @property
- def tables(self):
- return LOSheetTables(self.obj.DataPilotTables, self)
-
- @property
- def rows(self):
- return LOSheetRows(self, self.obj.Rows)
-
- @property
- def columns(self):
- return LOSheetColumns(self, self.obj.Columns)
-
- @property
- def forms(self):
- return LOSheetForms(self.obj.DrawPage)
-
- @property
- def search_descriptor(self):
- return self.obj.createSearchDescriptor()
-
- @property
- def replace_descriptor(self):
- return self.obj.createReplaceDescriptor()
-
- def render(self, data, rango=None, clean=True):
- if rango is None:
- rango = self.used_area
- return rango.render(data, clean)
-
- def find(self, search_string, rango=None):
- if rango is None:
- rango = self.used_area
- return rango.find(search_string)
-
-
-class LOCalcRange(object):
-
- def __contains__(self, item):
- return item.in_range(self)
-
- @property
- def back_color(self):
- return self._obj.CellBackColor
- @back_color.setter
- def back_color(self, value):
- self._obj.CellBackColor = get_color(value)
-
- @property
- def columns(self):
- return self.obj.Columns.Count
-
- @property
- def column(self):
- c1 = self.address.Column
- c2 = c1 + 1
- ra = self.current_region.range_address
- r1 = ra.StartRow
- r2 = ra.EndRow + 1
- return LOCalcRange(self.sheet[r1:r2, c1:c2].obj)
-
- @property
- def rows(self):
- return LOSheetRows(self.sheet, self.obj.Rows)
-
- @property
- def row(self):
- r1 = self.address.Row
- r2 = r1 + 1
- ra = self.current_region.range_address
- c1 = ra.StartColumn
- c2 = ra.EndColumn + 1
- return LOCalcRange(self.sheet[r1:r2, c1:c2].obj)
-
- @property
- def type(self):
- return self.obj.Type
-
- @property
- def error(self):
- return self.obj.getError()
-
- # ~ https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1sheet_1_1CellFlags.html
- @property
- def value(self):
- v = None
- if self.type == VALUE:
- v = self.obj.getValue()
- elif self.type == TEXT:
- v = self.obj.getString()
- elif self.type == FORMULA:
- v = self.obj.getFormula()
- return v
- @value.setter
- def value(self, data):
- if isinstance(data, str):
- if data[0] in '=':
- self.obj.setFormula(data)
- else:
- self.obj.setString(data)
- elif isinstance(data, Decimal):
- self.obj.setValue(float(data))
- elif isinstance(data, (int, float, bool)):
- self.obj.setValue(data)
- elif isinstance(data, datetime.datetime):
- d = data.toordinal()
- t = (data - datetime.datetime.fromordinal(d)).seconds / SECONDS_DAY
- self.obj.setValue(d - DATE_OFFSET + t)
- elif isinstance(data, datetime.date):
- d = data.toordinal()
- self.obj.setValue(d - DATE_OFFSET)
- elif isinstance(data, datetime.time):
- d = (data.hour * 3600 + data.minute * 60 + data.second) / SECONDS_DAY
- self.obj.setValue(d)
-
- @property
- def date(self):
- value = int(self.obj.Value)
- date = datetime.date.fromordinal(value + DATE_OFFSET)
- return date
-
- @property
- def time(self):
- seconds = self.obj.Value * SECONDS_DAY
- time_delta = datetime.timedelta(seconds=seconds)
- time = (datetime.datetime.min + time_delta).time()
- return time
-
- @property
- def datetime(self):
- return datetime.datetime.combine(self.date, self.time)
-
- @property
- def dict(self):
- rows = self.data
- k = rows[0]
- data = [dict(zip(k, r)) for r in rows[1:]]
- return data
- @dict.setter
- def dict(self, values):
- data = [tuple(values[0].keys())]
- data += [tuple(d.values()) for d in values]
- self.data = data
-
- @property
- def formula(self):
- return self.obj.getFormulaArray()
- @formula.setter
- def formula(self, values):
- self.obj.setFormulaArray(values)
-
- @property
- def array_formula(self):
- return self.obj.ArrayFormula
- @array_formula.setter
- def array_formula(self, value):
- self.obj.ArrayFormula = value
-
- @property
- def next_cell(self):
- a = self.current_region.range_address
- col = a.StartColumn
- row = a.EndRow + 1
- return LOCalcRange(self.sheet[row, col].obj)
-
- @property
- def position(self):
- return self.obj.Position
-
- @property
- def size(self):
- return self.obj.Size
-
- @property
- def possize(self):
- data = {
- 'Width': self.size.Width,
- 'Height': self.size.Height,
- 'X': self.position.X,
- 'Y': self.position.Y,
- }
- return data
-
- @property
- def visible(self):
- cursor = self.cursor
- rangos = cursor.queryVisibleCells()
- rangos = LOCalcRanges(rangos)
- return rangos
-
- @property
- def merged_area(self):
- cursor = self.cursor
- cursor.collapseToMergedArea()
- rango = LOCalcRange(self.sheet[cursor.AbsoluteName].obj)
- return rango
-
- @property
- def empty(self):
- cursor = self.cursor
- rangos = cursor.queryEmptyCells()
- rangos = [LOCalcRange(self.sheet[r.AbsoluteName].obj) for r in rangos]
- return tuple(rangos)
-
- def query_content(self, type_content=1023):
- cursor = self.cursor
- rangos = cursor.queryContentCells(type_content)
- rangos = [LOCalcRange(self.sheet[r.AbsoluteName].obj) for r in rangos]
- return tuple(rangos)
-
- @property
- def merge(self):
- return self.obj.IsMerged
- @merge.setter
- def merge(self, value):
- self.obj.merge(value)
-
- @property
- def auto_format(self):
- return ''
- @auto_format.setter
- def auto_format(self, value):
- self.obj.autoFormat(value)
-
- @property
- def validation(self):
- return self.obj.Validation
- @validation.setter
- def validation(self, values):
- current = self.validation
- if not values:
- current.Type = ValidationType.ANY
- current.ShowInputMessage = False
- else:
- is_list = False
- for k, v in values.items():
- if k == 'Type' and v == VT.LIST:
- is_list = True
- if k == 'Formula1' and is_list:
- if isinstance(v, (tuple, list)):
- v = ';'.join(['"{}"'.format(i) for i in v])
- setattr(current, k, v)
- self.obj.Validation = current
-
- def select(self):
- self.doc._cc.select(self.obj)
- return
-
- def search(self, options, find_all=True):
- rangos = None
-
- descriptor = self.sheet.search_descriptor
- descriptor.setSearchString(options['Search'])
- descriptor.SearchCaseSensitive = options.get('CaseSensitive', False)
- descriptor.SearchWords = options.get('Words', False)
- if hasattr(descriptor, 'SearchRegularExpression'):
- descriptor.SearchRegularExpression = options.get('RegularExpression', False)
- if hasattr(descriptor, 'SearchType') and 'Type' in options:
- descriptor.SearchType = options['Type']
-
- if find_all:
- found = self.obj.findAll(descriptor)
- else:
- found = self.obj.findFirst(descriptor)
-
- if found:
- if found.ImplementationName == OBJ_CELL:
- rangos = LOCalcRange(found)
- else:
- rangos = [LOCalcRange(f) for f in found]
-
- return rangos
-
- def replace(self, options):
- descriptor = self.sheet.replace_descriptor
- descriptor.setSearchString(options['Search'])
- descriptor.setReplaceString(options['Replace'])
- descriptor.SearchCaseSensitive = options.get('CaseSensitive', False)
- descriptor.SearchWords = options.get('Words', False)
- if hasattr(descriptor, 'SearchRegularExpression'):
- descriptor.SearchRegularExpression = options.get('RegularExpression', False)
- if hasattr(descriptor, 'SearchType') and 'Type' in options:
- descriptor.SearchType = options['Type']
- count = self.obj.replaceAll(descriptor)
- return count
-
- def in_range(self, rango):
- if isinstance(rango, LOCalcRange):
- address = rango.range_address
- else:
- address = rango.RangeAddress
- result = self.cursor.queryIntersection(address)
- return bool(result.Count)
-
- def move(self, target):
- sheet = self.sheet.obj
- sheet.moveRange(target.address, self.range_address)
- return
-
- def insert(self, insert_mode=CIM.DOWN):
- sheet = self.sheet.obj
- sheet.insertCells(self.range_address, insert_mode)
- return
-
- def delete(self, delete_mode=CDM.UP):
- sheet = self.sheet.obj
- sheet.removeRange(self.range_address, delete_mode)
- return
-
- def copy_from(self, source):
- self.sheet.obj.copyRange(self.address, source.range_address)
- return
-
- def copy_to(self, target):
- self.sheet.obj.copyRange(target.address, self.range_address)
- return
-
- # ~ def copy_to(self, cell, formula=False):
- # ~ rango = cell.to_size(self.rows, self.columns)
- # ~ if formula:
- # ~ rango.formula = self.formula
- # ~ else:
- # ~ rango.data = self.data
- # ~ return
-
- # ~ def copy_from(self, rango, formula=False):
- # ~ data = rango
- # ~ if isinstance(rango, LOCalcRange):
- # ~ if formula:
- # ~ data = rango.formula
- # ~ else:
- # ~ data = rango.data
- # ~ rows = len(data)
- # ~ cols = len(data[0])
- # ~ if formula:
- # ~ self.to_size(rows, cols).formula = data
- # ~ else:
- # ~ self.to_size(rows, cols).data = data
- # ~ return
-
- def optimal_width(self):
- self.obj.Columns.OptimalWidth = True
- return
-
- def clean_render(self, template='\{(\w.+)\}'):
- self._sd.SearchRegularExpression = True
- self._sd.setSearchString(template)
- self.obj.replaceAll(self._sd)
- return
-
- def render(self, data, clean=True):
- self._sd = self.sheet.obj.createSearchDescriptor()
- self._sd.SearchCaseSensitive = False
- for k, v in data.items():
- cell = self._render_value(k, v)
- return cell
-
- def _render_value(self, key, value, parent=''):
- cell = None
- if isinstance(value, dict):
- for k, v in value.items():
- # ~ print(1, 'RENDER', k, v)
- cell = self._render_value(k, v, key)
- return cell
- elif isinstance(value, (list, tuple)):
- self._render_list(key, value)
- return
-
- search = f'{{{key}}}'
- if parent:
- search = f'{{{parent}.{key}}}'
- ranges = self.find_all(search)
-
- if ranges is None:
- return
-
- # ~ for cell in ranges or range(0):
- for cell in ranges:
- self._set_new_value(cell, search, value)
- return LOCalcRange(cell)
-
- def _set_new_value(self, cell, search, value):
- if not cell.ImplementationName == 'ScCellObj':
- return
-
- if isinstance(value, str):
- pattern = re.compile(search, re.IGNORECASE)
- new_value = pattern.sub(value, cell.String)
- cell.String = new_value
- else:
- LOCalcRange(cell).value = value
- return
-
- def _render_list(self, key, rows):
- for row in rows:
- for k, v in row.items():
- self._render_value(k, v)
- return
-
- def find(self, search_string):
- if self._sd is None:
- self._sd = self.sheet.obj.createSearchDescriptor()
- self._sd.SearchCaseSensitive = False
-
- self._sd.setSearchString(search_string)
- cell = self.obj.findFirst(self._sd)
- if cell:
- cell = LOCalcRange(cell)
- return cell
-
- def find_all(self, search_string):
- if self._sd is None:
- self._sd = self.sheet.obj.createSearchDescriptor()
- self._sd.SearchCaseSensitive = False
-
- self._sd.setSearchString(search_string)
- ranges = self.obj.findAll(self._sd)
- return ranges
-
- def filter(self, args, with_headers=True):
- ff = TableFilterField()
- ff.Field = args['Field']
- ff.Operator = args['Operator']
- if isinstance(args['Value'], str):
- ff.IsNumeric = False
- ff.StringValue = args['Value']
- else:
- ff.IsNumeric = True
- ff.NumericValue = args['Value']
-
- fd = self.obj.createFilterDescriptor(True)
- fd.ContainsHeader = with_headers
- fd.FilterFields = ((ff,))
- # ~ self.obj.AutoFilter = True
- self.obj.filter(fd)
- return
-
- def copy_format_from(self, rango):
- rango.select()
- self.doc.copy()
- self.select()
- args = {
- 'Flags': 'T',
- 'MoveMode': 4,
- }
- url = '.uno:InsertContents'
- call_dispatch(self.doc.frame, url, args)
- return
-
- def to_image(self):
- self.select()
- self.doc.copy()
- args = {'SelectedFormat': 141}
- url = '.uno:ClipboardFormatItems'
- call_dispatch(self.doc.frame, url, args)
- return self.sheet.shapes[-1]
-
- def insert_image(self, path, options={}):
- args = options.copy()
- ps = self.possize
- args['Width'] = args.get('Width', ps['Width'])
- args['Height'] = args.get('Height', ps['Height'])
- args['X'] = args.get('X', ps['X'])
- args['Y'] = args.get('Y', ps['Y'])
- # ~ img.ResizeWithCell = True
- img = self.sheet.dp.insert_image(path, args)
- img.anchor = self.obj
- args.clear()
- return img
-
- def insert_shape(self, tipo, args={}):
- ps = self.possize
- args['Width'] = args.get('Width', ps['Width'])
- args['Height'] = args.get('Height', ps['Height'])
- args['X'] = args.get('X', ps['X'])
- args['Y'] = args.get('Y', ps['Y'])
-
- shape = self.sheet.dp.add(tipo, args)
- shape.anchor = self.obj
- args.clear()
- return
-
- def filter_by_color(self, cell):
- rangos = cell.column[1:,:].visible
- for r in rangos:
- for c in r:
- if c.back_color != cell.back_color:
- c.rows.visible = False
- return
-
- def clear(self, what=1023):
- # ~ http://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1sheet_1_1CellFlags.html
- self.obj.clearContents(what)
- return
-
- def transpose(self):
- # ~ 'Flags': 'A',
- # ~ 'FormulaCommand': 0,
- # ~ 'SkipEmptyCells': False,
- # ~ 'AsLink': False,
- # ~ 'MoveMode': 4,
- self.select()
- self.doc.copy()
- self.clear(1023)
- self[0,0].select()
- self.doc.insert_contents({'Transpose': True})
- _CB.set('')
- return
-
- def transpose_data(self, formula=False):
- data = self.data
- if formula:
- data = self.formula
- data = tuple(zip(*data))
- self.clear(1023)
- self[0,0].copy_from(data, formula=formula)
- return
-
- def merge_by_row(self):
- for r in range(len(self.rows)):
- self[r].merge = True
- return
-
- def fill(self, source=1):
- self.obj.fillAuto(0, source)
- return
-
- def _cast(self, t, v):
- if not t:
- return v
-
- if t == datetime.date:
- nv = datetime.date.fromordinal(int(v) + DATE_OFFSET)
- else:
- nv = t(v)
- return nv
-
- def get_data(self, types):
- values = [
- [self._cast(types[i], v) for i, v in enumerate(row)]
- for row in self.data
- ]
- return values
-
-
-class LOWriterStyles(object):
-
- def __init__(self, styles):
- self._styles = styles
-
- @property
- def names(self):
- return {s.DisplayName: s.Name for s in self._styles}
-
- def __str__(self):
- return '\n'.join(tuple(self.names.values()))
-
-
-class LOWriterStylesFamilies(object):
-
- def __init__(self, styles):
- self._styles = styles
-
- def __getitem__(self, index):
- styles = {
- 'Character': 'CharacterStyles',
- 'Paragraph': 'ParagraphStyles',
- 'Page': 'PageStyles',
- 'Frame': 'FrameStyles',
- 'Numbering': 'NumberingStyles',
- 'Table': 'TableStyles',
- 'Cell': 'CellStyles',
- }
- name = styles.get(index, index)
- return LOWriterStyles(self._styles[name])
-
- def __iter__(self):
- self._index = 0
- return self
-
- def __next__(self):
- obj = LOWriterStyles(self._styles[self._index])
- self._index += 1
- return obj
- # ~ raise StopIteration
-
- @property
- def names(self):
- return self._styles.ElementNames
-
- def __str__(self):
- return '\n'.join(self.names)
-
-
-class LOWriterPageStyle(LOBaseObject):
-
- def __init__(self, obj):
- super().__init__(obj)
-
- def __str__(self):
- return f'Page Style: {self.name}'
-
- @property
- def name(self):
- return self._obj.Name
-
-
-class LOWriterPageStyles(object):
-
- def __init__(self, styles):
- self._styles = styles
-
- def __getitem__(self, index):
- return LOWriterPageStyle(self._styles[index])
-
- @property
- def names(self):
- return self._styles.ElementNames
-
- def __str__(self):
- return '\n'.join(self.names)
-
-
-class LOWriterTextRange(object):
-
- def __init__(self, obj, doc):
- self._obj = obj
- self._doc = doc
- self._is_paragraph = self.obj.ImplementationName == 'SwXParagraph'
- self._is_table = self.obj.ImplementationName == 'SwXTextTable'
- self._is_text = self.obj.ImplementationName == 'SwXTextPortion'
- self._is_section = not self.obj.TextSection is None
- self._parts = []
- if self._is_paragraph:
- self._parts = [LOWriterTextRange(p, doc) for p in obj]
-
- def __iter__(self):
- self._index = 0
- return self
-
- def __next__(self):
- try:
- obj = self._parts[self._index]
- except IndexError:
- raise StopIteration
-
- self._index += 1
- return obj
-
- @property
- def string(self):
- s = ''
- if not self._is_table:
- s = self.obj.String
- return s
- @string.setter
- def string(self, value):
- self.obj.String = value
-
- @property
- def value(self):
- return self.string
- @value.setter
- def value(self, value):
- self.string = value
-
- @property
- def style(self):
- s = ''
- if self.is_paragraph:
- s = self.obj.ParaStyleName
- elif self.is_text:
- s = self.obj.CharStyleName
- return s
- @style.setter
- def style(self, value):
- if self.is_paragraph:
- self.obj.ParaStyleName = value
- elif self.is_text:
- self.obj.CharStyleName = value
-
- @property
- def is_paragraph(self):
- return self._is_paragraph
-
- @property
- def is_table(self):
- return self._is_table
-
- @property
- def is_text(self):
- return self._is_text
-
- @property
- def is_section(self):
- return self._is_section
-
- @property
- def text_cursor(self):
- return self.text.createTextCursor()
-
- @property
- def dp(self):
- return self._doc.dp
-
- @property
- def paragraph(self):
- cursor = self.cursor
- cursor.gotoStartOfParagraph(False)
- cursor.gotoNextParagraph(True)
- return LOWriterTextRange(cursor, self._doc)
-
- def goto_start(self):
- if self.is_section:
- rango = self.obj.TextSection.Anchor.Start
- else:
- rango = self.obj.Start
- return LOWriterTextRange(rango, self._doc)
-
- def goto_end(self):
- if self.is_section:
- rango = self.obj.TextSection.Anchor.End
- else:
- rango = self.obj.End
- return LOWriterTextRange(rango, self._doc)
-
- def goto_previous(self, expand=True):
- cursor = self.cursor
- cursor.gotoPreviousParagraph(expand)
- return LOWriterTextRange(cursor, self._doc)
-
- def goto_next(self, expand=True):
- cursor = self.cursor
- cursor.gotoNextParagraph(expand)
- return LOWriterTextRange(cursor, self._doc)
-
- def go_left(self, from_self=True, count=1, expand=False):
- cursor = self.cursor
- if not from_self:
- cursor = self.text_cursor
- cursor.gotoRange(self.obj, False)
- cursor.goLeft(count, expand)
- return LOWriterTextRange(cursor, self._doc)
-
- def go_right(self, from_self=True, count=1, expand=False):
- cursor = self.cursor
- if not from_self:
- cursor = self.text_cursor
- cursor.gotoRange(self.obj, False)
- cursor.goRight(count, expand)
- return LOWriterTextRange(cursor, self._doc)
-
- def delete(self):
- self.value = ''
- return
-
- def offset(self):
- cursor = self.cursor.getEnd()
- return LOWriterTextRange(cursor, self._doc)
-
- def insert_content(self, data, cursor=None, replace=False):
- if cursor is None:
- cursor = self.cursor
- self.text.insertTextContent(cursor, data, replace)
- return
-
- def insert_math(self, formula,
- anchor_type=TextContentAnchorType.AS_CHARACTER,
- cursor=None, replace=False):
-
- math = self._doc.create_instance(SERVICES['TEXT_EMBEDDED'])
- math.CLSID = CLSID['FORMULA']
- math.AnchorType = anchor_type
- self.insert_content(math, cursor, replace)
- math.EmbeddedObject.Component.Formula = formula
- return math
-
- def new_line(self, count=1):
- cursor = self.cursor
- for i in range(count):
- self.text.insertControlCharacter(cursor, PARAGRAPH_BREAK, False)
- return LOWriterTextRange(cursor, self._doc)
-
- def insert_table(self, data):
- table = self._doc.create_instance(SERVICES['TEXT_TABLE'])
- rows = len(data)
- cols = len(data[0])
- table.initialize(rows, cols)
- self.insert_content(table)
- table.DataArray = data
- name = table.Name
- table = LOWriterTextTable(self._doc.tables[name], self._doc)
- return table
-
- def insert_shape(self, tipo, args={}):
- # ~ args['Width'] = args.get('Width', 1000)
- # ~ args['Height'] = args.get('Height', 1000)
- # ~ args['X'] = args.get('X', 0)
- # ~ args['Y'] = args.get('Y', 0)
- shape = self._doc.dp.add(tipo, args)
- # ~ shape.anchor = self.obj
- return shape
-
- def insert_image(self, path, args={}):
- w = args.get('Width', 1000)
- h = args.get('Height', 1000)
-
- image = self._doc.create_instance(SERVICES['GRAPHIC'])
- image.GraphicURL = _P.to_url(path)
- image.AnchorType = TextContentAnchorType.AS_CHARACTER
- image.Width = w
- image.Height = h
- self.insert_content(image)
- return self._doc.dp.last
-
-
-class LOWriterTextRanges(object):
-
- def __init__(self, obj, doc):
- self._obj = obj
- self._doc = doc
- self._paragraphs = [LOWriterTextRange(p, doc) for p in obj]
-
- def __len__(self):
- return len(self._paragraphs)
-
- def __getitem__(self, index):
- return self._paragraphs[index]
-
- def __iter__(self):
- self._index = 0
- return self
-
- def __next__(self):
- try:
- obj = self._paragraphs[self._index]
- except IndexError:
- raise StopIteration
-
- self._index += 1
- return obj
-
- @property
- def obj(self):
- return self._obj
-
-
-class LOWriterTextTable(object):
-
- def __init__(self, obj, doc):
- self._obj = obj
- self._doc = doc
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def name(self):
- return self._obj.Name
-
- @property
- def data(self):
- return self.obj.DataArray
- @data.setter
- def data(self, values):
- self.obj.DataArray = values
-
- @property
- def style(self):
- return self.obj.TableTemplateName
- @style.setter
- def style(self, value):
- self.obj.autoFormat(value)
-
-
-class LOWriterTextTables(object):
-
- def __init__(self, doc):
- self._doc = doc
- self._obj = doc.obj.TextTables
-
- def __getitem__(self, key):
- return LOWriterTextTable(self._obj[key], self._doc)
-
- def __len__(self):
- return self._obj.Count
-
- def insert(self, data, text_range=None):
- if text_range is None:
- text_range = self._doc.selection
- text_range.insert_table(data)
- return
-
-
-class LOWriter(LODocument):
-
- def __init__(self, obj):
- super().__init__(obj)
- self._type = WRITER
- self._settings = self._cc.ViewSettings
-
- @property
- def text(self):
- return self.paragraphs
-
- @property
- def paragraphs(self):
- return LOWriterTextRanges(self.obj.Text, self)
-
- @property
- def tables(self):
- return LOWriterTextTables(self)
-
- @property
- def selection(self):
- sel = self.obj.CurrentSelection
- if sel.ImplementationName == OBJ_TEXTS:
- if len(sel) == 1:
- sel = LOWriterTextRanges(sel, self)[0]
- else:
- sel = LOWriterTextRanges(sel, self)
- return sel
-
- if sel.ImplementationName == OBJ_SHAPES:
- if len(sel) == 1:
- sel = sel[0]
- sel = LODrawPage(sel.Parent)[sel.Name]
- return sel
-
- if sel.ImplementationName == OBJ_GRAPHIC:
- sel = self.dp[sel.Name]
- else:
- debug(sel.ImplementationName)
-
- return sel
-
- @property
- def dp(self):
- return self.draw_page
- @property
- def shapes(self):
- return self.draw_page
- @property
- def draw_page(self):
- return LODrawPage(self.obj.DrawPage)
-
- @property
- def cursor(self):
- return self.obj.Text.createTextCursor()
-
- @property
- def view_cursor(self):
- return self._cc.ViewCursor
-
- @property
- def page_styles(self):
- ps = self.obj.StyleFamilies['PageStyles']
- return LOWriterPageStyles(ps)
-
- @property
- def styles(self):
- return LOWriterStylesFamilies(self.obj.StyleFamilies)
-
- @property
- def search_descriptor(self):
- return self.obj.createSearchDescriptor()
-
- @property
- def replace_descriptor(self):
- return self.obj.createReplaceDescriptor()
-
- @property
- def view_web(self):
- return self._settings.ShowOnlineLayout
- @view_web.setter
- def view_web(self, value):
- self._settings.ShowOnlineLayout = value
-
- def goto_start(self):
- self.view_cursor.gotoStart(False)
- return self.selection
-
- def goto_end(self):
- self.view_cursor.gotoEnd(False)
- return self.selection
-
- def search(self, options, find_all=True):
- descriptor = self.search_descriptor
- descriptor.setSearchString(options.get('Search', ''))
- descriptor.SearchCaseSensitive = options.get('CaseSensitive', False)
- descriptor.SearchWords = options.get('Words', False)
- if 'Attributes' in options:
- attr = dict_to_property(options['Attributes'])
- descriptor.setSearchAttributes(attr)
- if hasattr(descriptor, 'SearchRegularExpression'):
- descriptor.SearchRegularExpression = options.get('RegularExpression', False)
- if hasattr(descriptor, 'SearchType') and 'Type' in options:
- descriptor.SearchType = options['Type']
-
- result = False
- if find_all:
- found = self.obj.findAll(descriptor)
- if len(found):
- result = [LOWriterTextRange(f, self) for f in found]
- else:
- found = self.obj.findFirst(descriptor)
- if found:
- result = LOWriterTextRange(found, self)
-
- return result
-
- def replace(self, options):
- descriptor = self.replace_descriptor
- descriptor.setSearchString(options['Search'])
- descriptor.setReplaceString(options['Replace'])
- descriptor.SearchCaseSensitive = options.get('CaseSensitive', False)
- descriptor.SearchWords = options.get('Words', False)
- if 'Attributes' in options:
- attr = dict_to_property(options['Attributes'])
- descriptor.setSearchAttributes(attr)
- if hasattr(descriptor, 'SearchRegularExpression'):
- descriptor.SearchRegularExpression = options.get('RegularExpression', False)
- if hasattr(descriptor, 'SearchType') and 'Type' in options:
- descriptor.SearchType = options['Type']
- found = self.obj.replaceAll(descriptor)
- return found
-
- def select(self, text):
- if hasattr(text, 'obj'):
- text = text.obj
- self._cc.select(text)
- return
-
-
-class LOShape(LOBaseObject):
-
- @property
- def cell(self):
- return self.anchor
-
- @property
- def anchor(self):
- obj = self.obj.Anchor
- if obj.ImplementationName == OBJ_CELL:
- obj = LOCalcRange(obj)
- elif obj.ImplementationName == OBJ_TEXT:
- obj = LOWriterTextRange(obj, LODocs().active)
- else:
- debug('Anchor', obj.ImplementationName)
- return obj
- @anchor.setter
- def anchor(self, value):
- if hasattr(value, 'obj'):
- value = value.obj
- try:
- self.obj.Anchor = value
- except Exception as e:
- self.obj.AnchorType = value
-
- @property
- def visible(self):
- return self.obj.Visible
- @visible.setter
- def visible(self, value):
- self.obj.Visible = value
-
- @property
- def path(self):
- return self.url
- @property
- def url(self):
- url = ''
- if self.is_image:
- url = _P.to_system(self.obj.GraphicURL.OriginURL)
- return url
-
- @property
- def mimetype(self):
- mt = ''
- if self.is_image:
- mt = self.obj.GraphicURL.MimeType
- return mt
-
- @property
- def linked(self):
- l = False
- if self.is_image:
- l = self.obj.GraphicURL.Linked
- return l
-
- def delete(self):
- self.remove()
- return
- def remove(self):
- self.obj.Parent.remove(self.obj)
- return
-
- def save(self, path: str, mimetype=DEFAULT_MIME_TYPE):
- if _P.is_dir(path):
- name = self.name
- ext = mimetype.lower()
- else:
- p = _P(path)
- path = p.path
- name = p.name
- ext = p.ext.lower()
-
- path = _P.join(path, f'{name}.{ext}')
- args = dict(
- URL = _P.to_url(path),
- MimeType = MIME_TYPE[ext],
- )
- if not _export_image(self.obj, args):
- path = ''
- return path
-
- # ~ def save2(self, path: str):
- # ~ size = len(self.obj.Bitmap.DIB)
- # ~ data = self.obj.GraphicStream.readBytes((), size)
- # ~ data = data[-1].value
- # ~ path = _P.join(path, f'{self.name}.png')
- # ~ _P.save_bin(path, b'')
- # ~ return
-
-
-class LODrawPage(LOBaseObject):
-
- def __init__(self, obj):
- super().__init__(obj)
-
- def __getitem__(self, index):
- if isinstance(index, int):
- shape = LOShape(self.obj[index], index)
- else:
- for i, o in enumerate(self.obj):
- shape = self.obj[i]
- name = shape.Name or f'shape{i}'
- if name == index:
- shape = LOShape(shape, i)
- break
- return shape
-
- def __iter__(self):
- self._index = 0
- return self
-
- def __next__(self):
- if self._index == self.count:
- raise StopIteration
- shape = self[self._index]
- self._index += 1
- return shape
-
-
- @property
- def name(self):
- return self.obj.Name
-
- @property
- def doc(self):
- return self.obj.Forms.Parent
-
- @property
- def width(self):
- return self.obj.Width
-
- @property
- def height(self):
- return self.obj.Height
-
- @property
- def count(self):
- return self.obj.Count
-
- @property
- def last(self):
- return self[self.count - 1]
-
- def create_instance(self, name):
- return self.doc.createInstance(name)
-
- def add(self, type_shape, options={}):
- args = options.copy()
- """Insert a shape in page, type shapes:
- Line
- Rectangle
- Ellipse
- Text
- Connector
- """
- index = self.count
- default_height = 3000
- if type_shape == 'Line':
- default_height = 0
- w = args.pop('Width', 3000)
- h = args.pop('Height', default_height)
- x = args.pop('X', 1000)
- y = args.pop('Y', 1000)
- name = args.pop('Name', f'{type_shape.lower()}{index}')
-
- service = f'com.sun.star.drawing.{type_shape}Shape'
- shape = self.create_instance(service)
- shape.Size = Size(w, h)
- shape.Position = Point(x, y)
- shape.Name = name
- self.obj.add(shape)
-
- if args:
- _set_properties(shape, args)
-
- return LOShape(self.obj[index], index)
-
- def remove(self, shape):
- if hasattr(shape, 'obj'):
- shape = shape.obj
- return self.obj.remove(shape)
-
- def remove_all(self):
- while self.count:
- self.obj.remove(self.obj[0])
- return
-
- def insert_image(self, path, options={}):
- args = options.copy()
- index = self.count
- w = args.get('Width', 3000)
- h = args.get('Height', 3000)
- x = args.get('X', 1000)
- y = args.get('Y', 1000)
- name = args.get('Name', f'image{index}')
-
- image = self.create_instance('com.sun.star.drawing.GraphicObjectShape')
- if isinstance(path, str):
- image.GraphicURL = _P.to_url(path)
- else:
- gp = create_instance('com.sun.star.graphic.GraphicProvider')
- properties = dict_to_property({'InputStream': path})
- image.Graphic = gp.queryGraphic(properties)
-
- self.obj.add(image)
- image.Size = Size(w, h)
- image.Position = Point(x, y)
- image.Name = name
- return LOShape(self.obj[index], index)
-
-
-class LODrawImpress(LODocument):
-
- def __init__(self, obj):
- super().__init__(obj)
-
- def __getitem__(self, index):
- if isinstance(index, int):
- page = self.obj.DrawPages[index]
- else:
- page = self.obj.DrawPages.getByName(index)
- return LODrawPage(page)
-
- @property
- def selection(self):
- sel = self.obj.CurrentSelection[0]
- # ~ return _get_class_uno(sel)
- return sel
-
- @property
- def current_page(self):
- return LODrawPage(self._cc.getCurrentPage())
-
- def paste(self):
- call_dispatch(self.frame, '.uno:Paste')
- return self.current_page[-1]
-
- def add(self, type_shape, args={}):
- return self.current_page.add(type_shape, args)
-
- def insert_image(self, path, args={}):
- self.current_page.insert_image(path, args)
- return
-
- # ~ def export(self, path, mimetype='png'):
- # ~ args = dict(
- # ~ URL = _P.to_url(path),
- # ~ MimeType = MIME_TYPE[mimetype],
- # ~ )
- # ~ result = _export_image(self.obj, args)
- # ~ return result
-
-
-class BaseRow:
- pass
-
-
-class BaseQuery(object):
- PY_TYPES = {
- 'VARCHAR': 'getString',
- 'INTEGER': 'getLong',
- 'DATE': 'getDate',
- # ~ 'SQL_LONG': 'getLong',
- # ~ 'SQL_VARYING': 'getString',
- # ~ 'SQL_FLOAT': 'getFloat',
- # ~ 'SQL_BOOLEAN': 'getBoolean',
- # ~ 'SQL_TYPE_DATE': 'getDate',
- # ~ 'SQL_TYPE_TIME': 'getTime',
- # ~ 'SQL_TIMESTAMP': 'getTimestamp',
- }
- # ~ TYPES_DATE = ('SQL_TYPE_DATE', 'SQL_TYPE_TIME', 'SQL_TIMESTAMP')
- TYPES_DATE = ('DATE', 'SQL_TYPE_TIME', 'SQL_TIMESTAMP')
-
- def __init__(self, query):
- self._query = query
- self._meta = query.MetaData
- self._cols = self._meta.ColumnCount
- self._names = query.Columns.ElementNames
- self._data = self._get_data()
-
- def __getitem__(self, index):
- return self._data[index]
-
- def __iter__(self):
- self._index = 0
- return self
-
- def __next__(self):
- try:
- row = self._data[self._index]
- except IndexError:
- raise StopIteration
- self._index += 1
- return row
-
- def _to_python(self, index):
- type_field = self._meta.getColumnTypeName(index)
- # ~ print('TF', type_field)
- value = getattr(self._query, self.PY_TYPES[type_field])(index)
- if type_field in self.TYPES_DATE:
- value = _struct_to_date(value)
- return value
-
- def _get_row(self):
- row = BaseRow()
- for i in range(1, self._cols + 1):
- column_name = self._meta.getColumnName(i)
- value = self._to_python(i)
- setattr(row, column_name, value)
- return row
-
- def _get_data(self):
- data = []
- while self._query.next():
- row = self._get_row()
- data.append(row)
- return data
-
- @property
- def tuples(self):
- data = [tuple(r.__dict__.values()) for r in self._data]
- return tuple(data)
-
- @property
- def dicts(self):
- data = [r.__dict__ for r in self._data]
- return tuple(data)
-
-
-def _add_listeners(events, control, name=''):
- listeners = {
- 'addActionListener': EventsButton,
- 'addMouseListener': EventsMouse,
- 'addFocusListener': EventsFocus,
- 'addItemListener': EventsItem,
- 'addKeyListener': EventsKey,
- 'addTabListener': EventsTab,
- 'addSpinListener': EventsSpin,
- }
- if hasattr(control, 'obj'):
- control = control.obj
- # ~ debug(control.ImplementationName)
- is_grid = control.ImplementationName == 'stardiv.Toolkit.GridControl'
- is_link = control.ImplementationName == 'stardiv.Toolkit.UnoFixedHyperlinkControl'
- is_roadmap = control.ImplementationName == 'stardiv.Toolkit.UnoRoadmapControl'
- is_pages = control.ImplementationName == 'stardiv.Toolkit.UnoMultiPageControl'
-
- for key, value in listeners.items():
- if hasattr(control, key):
- if is_grid and key == 'addMouseListener':
- control.addMouseListener(EventsMouseGrid(events, name))
- continue
- if is_link and key == 'addMouseListener':
- control.addMouseListener(EventsMouseLink(events, name))
- continue
- if is_roadmap and key == 'addItemListener':
- control.addItemListener(EventsItemRoadmap(events, name))
- continue
-
- getattr(control, key)(listeners[key](events, name))
-
- if is_grid:
- controllers = EventsGrid(events, name)
- control.addSelectionListener(controllers)
- control.Model.GridDataModel.addGridDataListener(controllers)
- return
-
-
-class EventsSpin(EventsListenerBase, XSpinListener):
-
- def __init__(self, controller, name):
- super().__init__(controller, name)
-
- def up(self, event):
- event_name = f'{self.name}_up'
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(event)
- return
-
- def down(self, event):
- event_name = f'{self.name}_up'
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(event)
- return
-
- def first(self, event):
- event_name = f'{self.name}_first'
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(event)
- return
-
- def last(self, event):
- event_name = f'{self.name}_last'
- 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 = '{}_click'.format(self._name)
- if event.ClickCount == 2:
- event_name = '{}_double_click'.format(self._name)
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(event)
- return
-
- def mouseReleased(self, event):
- event_name = '{}_after_click'.format(self._name)
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(event)
- return
-
- def mouseEntered(self, event):
- pass
-
- def mouseExited(self, event):
- pass
-
- # ~ XMouseMotionListener
- def mouseMoved(self, event):
- pass
-
- def mouseDragged(self, event):
- pass
-
-
-class EventsMouseLink(EventsMouse):
-
- def __init__(self, controller, name):
- super().__init__(controller, name)
- self._text_color = 0
-
- def mouseEntered(self, event):
- model = event.Source.Model
- self._text_color = model.TextColor or 0
- model.TextColor = get_color('blue')
- return
-
- def mouseExited(self, event):
- model = event.Source.Model
- model.TextColor = self._text_color
- return
-
-
-class EventsButton(EventsListenerBase, XActionListener):
-
- def __init__(self, controller, name):
- super().__init__(controller, name)
-
- def actionPerformed(self, event):
- event_name = f'{self.name}_action'
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(event)
- return
-
-
-class EventsFocus(EventsListenerBase, XFocusListener):
- CONTROLS = (
- 'stardiv.Toolkit.UnoControlEditModel',
- )
-
- def __init__(self, controller, name):
- super().__init__(controller, name)
-
- def focusGained(self, event):
- service = event.Source.Model.ImplementationName
- # ~ print('Focus enter', service)
- if service in self.CONTROLS:
- obj = event.Source.Model
- obj.BackgroundColor = COLOR_ON_FOCUS
- return
-
- def focusLost(self, event):
- service = event.Source.Model.ImplementationName
- if service in self.CONTROLS:
- obj = event.Source.Model
- obj.BackgroundColor = -1
- return
-
-
-class EventsKey(EventsListenerBase, XKeyListener):
- """
- event.KeyChar
- event.KeyCode
- event.KeyFunc
- event.Modifiers
- """
-
- def __init__(self, controller, name):
- super().__init__(controller, name)
-
- def keyPressed(self, event):
- pass
-
- def keyReleased(self, event):
- event_name = '{}_key_released'.format(self._name)
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(event)
- # ~ else:
- # ~ if event.KeyFunc == QUIT and hasattr(self._cls, 'close'):
- # ~ self._cls.close()
- return
-
-
-class EventsItem(EventsListenerBase, XItemListener):
-
- def __init__(self, controller, name):
- super().__init__(controller, name)
-
- def disposing(self, event):
- pass
-
- def itemStateChanged(self, event):
- event_name = '{}_item_changed'.format(self.name)
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(event)
- return
-
-
-class EventsItemRoadmap(EventsItem):
-
- def itemStateChanged(self, event):
- dialog = event.Source.Context.Model
- dialog.Step = event.ItemId + 1
- return
-
-
-class EventsGrid(EventsListenerBase, XGridDataListener, XGridSelectionListener):
-
- def __init__(self, controller, name):
- super().__init__(controller, name)
-
- def dataChanged(self, event):
- event_name = '{}_data_changed'.format(self.name)
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(event)
- return
-
- def rowHeadingChanged(self, event):
- pass
-
- def rowsInserted(self, event):
- pass
-
- def rowsRemoved(self, evemt):
- pass
-
- def selectionChanged(self, event):
- event_name = '{}_selection_changed'.format(self.name)
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(event)
- return
-
-
-class EventsMouseGrid(EventsMouse):
- selected = False
-
- def mousePressed(self, event):
- super().mousePressed(event)
- # ~ obj = event.Source
- # ~ col = obj.getColumnAtPoint(event.X, event.Y)
- # ~ row = obj.getRowAtPoint(event.X, event.Y)
- # ~ print(col, row)
- # ~ if col == -1 and row == -1:
- # ~ if self.selected:
- # ~ obj.deselectAllRows()
- # ~ else:
- # ~ obj.selectAllRows()
- # ~ self.selected = not self.selected
- return
-
- def mouseReleased(self, event):
- # ~ obj = event.Source
- # ~ col = obj.getColumnAtPoint(event.X, event.Y)
- # ~ row = obj.getRowAtPoint(event.X, event.Y)
- # ~ if row == -1 and col > -1:
- # ~ gdm = obj.Model.GridDataModel
- # ~ for i in range(gdm.RowCount):
- # ~ gdm.updateRowHeading(i, i + 1)
- return
-
-
-class EventsTab(EventsListenerBase, XTabListener):
-
- def __init__(self, controller, name):
- super().__init__(controller, name)
-
- def activated(self, id):
- event_name = '{}_activated'.format(self.name)
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(id)
- return
-
-
-class EventsMenu(EventsListenerBase, XMenuListener):
-
- def __init__(self, controller):
- super().__init__(controller, '')
-
- def itemHighlighted(self, event):
- pass
-
- def itemSelected(self, event):
- name = event.Source.getCommand(event.MenuId)
- if name.startswith('menu'):
- event_name = '{}_selected'.format(name)
- else:
- event_name = 'menu_{}_selected'.format(name)
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(event)
- return
-
- def itemActivated(self, event):
- return
-
- def itemDeactivated(self, event):
- return
-
-
-class EventsWindow(EventsListenerBase, XTopWindowListener, XWindowListener):
-
- def __init__(self, cls):
- self._cls = cls
- super().__init__(cls.events, cls.name, cls._window)
-
- def windowOpened(self, event):
- event_name = '{}_opened'.format(self._name)
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(event)
- return
-
- def windowActivated(self, event):
- control_name = '{}_activated'.format(event.Source.Model.Name)
- if hasattr(self._controller, control_name):
- getattr(self._controller, control_name)(event)
- return
-
- def windowDeactivated(self, event):
- control_name = '{}_deactivated'.format(event.Source.Model.Name)
- if hasattr(self._controller, control_name):
- getattr(self._controller, control_name)(event)
- return
-
- def windowMinimized(self, event):
- pass
-
- def windowNormalized(self, event):
- pass
-
- def windowClosing(self, event):
- if self._window:
- control_name = 'window_closing'
- else:
- control_name = '{}_closing'.format(event.Source.Model.Name)
-
- if hasattr(self._controller, control_name):
- getattr(self._controller, control_name)(event)
- # ~ else:
- # ~ if not self._modal and not self._block:
- # ~ event.Source.Visible = False
- return
-
- def windowClosed(self, event):
- control_name = '{}_closed'.format(event.Source.Model.Name)
- if hasattr(self._controller, control_name):
- getattr(self._controller, control_name)(event)
- return
-
- # ~ XWindowListener
- def windowResized(self, event):
- sb = self._cls._subcont
- sb.setPosSize(0, 0, event.Width, event.Height, SIZE)
- event_name = '{}_resized'.format(self._name)
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(event)
- return
-
- def windowMoved(self, event):
- pass
-
- def windowShown(self, event):
- pass
-
- def windowHidden(self, event):
- pass
-
-
-# ~ BorderColor = ?
-# ~ FontStyleName = ?
-# ~ HelpURL = ?
-class UnoBaseObject(object):
-
- def __init__(self, obj, path=''):
- self._obj = obj
- self._model = obj.Model
-
- def __setattr__(self, name, value):
- exists = hasattr(self, name)
- if not exists and not name in ('_obj', '_model'):
- setattr(self._model, name, value)
- else:
- super().__setattr__(name, value)
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- pass
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def model(self):
- return self._model
- @property
- def m(self):
- return self._model
-
- @property
- def properties(self):
- return {}
- @properties.setter
- def properties(self, values):
- _set_properties(self.model, values)
-
- @property
- def name(self):
- return self.model.Name
-
- @property
- def parent(self):
- return self.obj.Context
-
- @property
- def tag(self):
- return self.model.Tag
- @tag.setter
- def tag(self, value):
- self.model.Tag = value
-
- @property
- def visible(self):
- return self.obj.Visible
- @visible.setter
- def visible(self, value):
- self.obj.setVisible(value)
-
- @property
- def enabled(self):
- return self.model.Enabled
- @enabled.setter
- def enabled(self, value):
- self.model.Enabled = value
-
- @property
- def step(self):
- return self.model.Step
- @step.setter
- def step(self, value):
- self.model.Step = value
-
- @property
- def align(self):
- return self.model.Align
- @align.setter
- def align(self, value):
- self.model.Align = value
-
- @property
- def valign(self):
- return self.model.VerticalAlign
- @valign.setter
- def valign(self, value):
- self.model.VerticalAlign = value
-
- @property
- def font_weight(self):
- return self.model.FontWeight
- @font_weight.setter
- def font_weight(self, value):
- self.model.FontWeight = value
-
- @property
- def font_height(self):
- return self.model.FontHeight
- @font_height.setter
- def font_height(self, value):
- self.model.FontHeight = value
-
- @property
- def font_name(self):
- return self.model.FontName
- @font_name.setter
- def font_name(self, value):
- self.model.FontName = value
-
- @property
- def font_underline(self):
- return self.model.FontUnderline
- @font_underline.setter
- def font_underline(self, value):
- self.model.FontUnderline = value
-
- @property
- def text_color(self):
- return self.model.TextColor
- @text_color.setter
- def text_color(self, value):
- self.model.TextColor = value
-
- @property
- def back_color(self):
- return self.model.BackgroundColor
- @back_color.setter
- def back_color(self, value):
- self.model.BackgroundColor = value
-
- @property
- def multi_line(self):
- return self.model.MultiLine
- @multi_line.setter
- def multi_line(self, value):
- self.model.MultiLine = value
-
- @property
- def help_text(self):
- return self.model.HelpText
- @help_text.setter
- def help_text(self, value):
- self.model.HelpText = value
-
- @property
- def border(self):
- return self.model.Border
- @border.setter
- def border(self, value):
- # ~ Bug for report
- self.model.Border = value
-
- @property
- def width(self):
- return self._model.Width
- @width.setter
- def width(self, value):
- self.model.Width = value
-
- @property
- def height(self):
- return self.model.Height
- @height.setter
- def height(self, value):
- self.model.Height = value
-
- def _get_possize(self, name):
- ps = self.obj.getPosSize()
- return getattr(ps, name)
-
- def _set_possize(self, name, value):
- ps = self.obj.getPosSize()
- setattr(ps, name, value)
- self.obj.setPosSize(ps.X, ps.Y, ps.Width, ps.Height, POSSIZE)
- return
-
- @property
- def x(self):
- if hasattr(self.model, 'PositionX'):
- return self.model.PositionX
- return self._get_possize('X')
- @x.setter
- def x(self, value):
- if hasattr(self.model, 'PositionX'):
- self.model.PositionX = value
- else:
- self._set_possize('X', value)
-
- @property
- def y(self):
- if hasattr(self.model, 'PositionY'):
- return self.model.PositionY
- return self._get_possize('Y')
- @y.setter
- def y(self, value):
- if hasattr(self.model, 'PositionY'):
- self.model.PositionY = value
- else:
- self._set_possize('Y', value)
-
- @property
- def tab_index(self):
- return self._model.TabIndex
- @tab_index.setter
- def tab_index(self, value):
- self.model.TabIndex = value
-
- @property
- def tab_stop(self):
- return self._model.Tabstop
- @tab_stop.setter
- def tab_stop(self, value):
- self.model.Tabstop = value
-
- @property
- def ps(self):
- ps = self.obj.getPosSize()
- return ps
- @ps.setter
- def ps(self, ps):
- self.obj.setPosSize(ps.X, ps.Y, ps.Width, ps.Height, POSSIZE)
-
- def set_focus(self):
- self.obj.setFocus()
- return
-
- def ps_from(self, source):
- self.ps = source.ps
- return
-
- def center(self, horizontal=True, vertical=False):
- p = self.parent.Model
- w = p.Width
- h = p.Height
- if horizontal:
- x = w / 2 - self.width / 2
- self.x = x
- if vertical:
- y = h / 2 - self.height / 2
- self.y = y
- return
-
- def move(self, origin, x=0, y=5, center=False):
- if x:
- self.x = origin.x + origin.width + x
- else:
- self.x = origin.x
- if y:
- h = origin.height
- if y < 0:
- h = 0
- self.y = origin.y + h + y
- else:
- self.y = origin.y
-
- if center:
- self.center()
- return
-
-
-class UnoLabel(UnoBaseObject):
-
- def __init__(self, obj):
- super().__init__(obj)
-
- @property
- def type(self):
- return 'label'
-
- @property
- def value(self):
- return self.model.Label
- @value.setter
- def value(self, value):
- self.model.Label = value
-
-
-class UnoLabelLink(UnoLabel):
-
- def __init__(self, obj):
- super().__init__(obj)
-
- @property
- def type(self):
- return 'link'
-
-
-class UnoButton(UnoBaseObject):
-
- def __init__(self, obj):
- super().__init__(obj)
-
- @property
- def type(self):
- return 'button'
-
- @property
- def value(self):
- return self.model.Label
- @value.setter
- def value(self, value):
- self.model.Label = value
-
- @property
- def image(self):
- return self.model.ImageURL
- @image.setter
- def image(self, value):
- self.model.ImageURL = _P.to_url(value)
-
-
-class UnoRadio(UnoBaseObject):
-
- def __init__(self, obj):
- super().__init__(obj)
-
- @property
- def type(self):
- return 'radio'
-
- @property
- def value(self):
- return self.model.Label
- @value.setter
- def value(self, value):
- self.model.Label = value
-
-
-class UnoCheckBox(UnoBaseObject):
-
- def __init__(self, obj):
- super().__init__(obj)
-
- @property
- def type(self):
- return 'checkbox'
-
- @property
- def value(self):
- return self.model.State
- @value.setter
- def value(self, value):
- self.model.State = value
-
- @property
- def label(self):
- return self.model.Label
- @label.setter
- def label(self, value):
- self.model.Label = value
-
- @property
- def tri_state(self):
- return self.model.TriState
- @tri_state.setter
- def tri_state(self, value):
- self.model.TriState = value
-
-
-# ~ https://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1awt_1_1UnoControlEditModel.html
-class UnoText(UnoBaseObject):
-
- def __init__(self, obj):
- super().__init__(obj)
-
- @property
- def type(self):
- return 'text'
-
- @property
- def value(self):
- return self.model.Text
- @value.setter
- def value(self, value):
- self.model.Text = value
-
- @property
- def echochar(self):
- return chr(self.model.EchoChar)
- @echochar.setter
- def echochar(self, value):
- if value:
- self.model.EchoChar = ord(value[0])
- else:
- self.model.EchoChar = 0
-
- def validate(self):
- return
-
-
-class UnoImage(UnoBaseObject):
-
- def __init__(self, obj):
- super().__init__(obj)
-
- @property
- def type(self):
- return 'image'
-
- @property
- def value(self):
- return self.url
- @value.setter
- def value(self, value):
- self.url = value
-
- @property
- def url(self):
- return self.m.ImageURL
- @url.setter
- def url(self, value):
- self.m.ImageURL = None
- self.m.ImageURL = _P.to_url(value)
-
-
-class UnoListBox(UnoBaseObject):
-
- def __init__(self, obj):
- super().__init__(obj)
- self._path = ''
-
- def __setattr__(self, name, value):
- if name in ('_path',):
- self.__dict__[name] = value
- else:
- super().__setattr__(name, value)
-
- @property
- def type(self):
- return 'listbox'
-
- @property
- def value(self):
- return self.obj.getSelectedItem()
-
- @property
- def count(self):
- return len(self.data)
-
- @property
- def data(self):
- return self.model.StringItemList
- @data.setter
- def data(self, values):
- self.model.StringItemList = list(sorted(values))
-
- @property
- def path(self):
- return self._path
- @path.setter
- def path(self, value):
- self._path = value
-
- def unselect(self):
- self.obj.selectItem(self.value, False)
- return
-
- def select(self, pos=0):
- if isinstance(pos, str):
- self.obj.selectItem(pos, True)
- else:
- self.obj.selectItemPos(pos, True)
- return
-
- def clear(self):
- self.model.removeAllItems()
- return
-
- def _set_image_url(self, image):
- if _P.exists(image):
- return _P.to_url(image)
-
- path = _P.join(self._path, DIR['images'], image)
- return _P.to_url(path)
-
- def insert(self, value, path='', pos=-1, show=True):
- if pos < 0:
- pos = self.count
- if path:
- self.model.insertItem(pos, value, self._set_image_url(path))
- else:
- self.model.insertItemText(pos, value)
- if show:
- self.select(pos)
- return
-
-
-class UnoRoadmap(UnoBaseObject):
-
- def __init__(self, obj):
- super().__init__(obj)
- self._options = ()
-
- def __setattr__(self, name, value):
- if name in ('_options',):
- self.__dict__[name] = value
- else:
- super().__setattr__(name, value)
-
- @property
- def options(self):
- return self._options
- @options.setter
- def options(self, values):
- self._options = values
- for i, v in enumerate(values):
- opt = self.model.createInstance()
- opt.ID = i
- opt.Label = v
- self.model.insertByIndex(i, opt)
- return
-
- @property
- def enabled(self):
- return True
- @enabled.setter
- def enabled(self, value):
- for m in self.model:
- m.Enabled = value
- return
-
- def set_enabled(self, index, value):
- self.model.getByIndex(index).Enabled = value
- return
-
-
-class UnoTree(UnoBaseObject):
-
- def __init__(self, obj, ):
- super().__init__(obj)
- self._tdm = None
- self._data = []
-
- def __setattr__(self, name, value):
- if name in ('_tdm', '_data'):
- self.__dict__[name] = value
- else:
- super().__setattr__(name, value)
-
- @property
- def selection(self):
- sel = self.obj.Selection
- return sel.DataValue, sel.DisplayValue
-
- @property
- def parent(self):
- parent = self.obj.Selection.Parent
- if parent is None:
- return ()
- return parent.DataValue, parent.DisplayValue
-
- def _get_parents(self, node):
- value = (node.DisplayValue,)
- parent = node.Parent
- if parent is None:
- return value
- return self._get_parents(parent) + value
-
- @property
- def parents(self):
- values = self._get_parents(self.obj.Selection)
- return values
-
- @property
- def root(self):
- if self._tdm is None:
- return ''
- return self._tdm.Root.DisplayValue
- @root.setter
- def root(self, value):
- self._add_data_model(value)
-
- def _add_data_model(self, name):
- tdm = create_instance('com.sun.star.awt.tree.MutableTreeDataModel')
- root = tdm.createNode(name, True)
- root.DataValue = 0
- tdm.setRoot(root)
- self.model.DataModel = tdm
- self._tdm = self.model.DataModel
- return
-
- @property
- def path(self):
- return self.root
- @path.setter
- def path(self, value):
- self.data = _P.walk_dir(value, True)
-
- @property
- def data(self):
- return self._data
- @data.setter
- def data(self, values):
- self._data = list(values)
- self._add_data()
-
- def _add_data(self):
- if not self.data:
- return
-
- parents = {}
- for node in self.data:
- parent = parents.get(node[1], self._tdm.Root)
- child = self._tdm.createNode(node[2], False)
- child.DataValue = node[0]
- parent.appendChild(child)
- parents[node[0]] = child
- self.obj.expandNode(self._tdm.Root)
- return
-
-
-# ~ https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1awt_1_1grid.html
-class UnoGrid(UnoBaseObject):
-
- def __init__(self, obj):
- super().__init__(obj)
- self._gdm = self.model.GridDataModel
- self._data = []
- self._formats = ()
-
- def __setattr__(self, name, value):
- if name in ('_gdm', '_data', '_formats'):
- self.__dict__[name] = value
- else:
- super().__setattr__(name, value)
-
- def __getitem__(self, key):
- value = self._gdm.getCellData(key[0], key[1])
- return value
-
- def __setitem__(self, key, value):
- self._gdm.updateCellData(key[0], key[1], value)
- return
-
- @property
- def type(self):
- return 'grid'
-
- @property
- def columns(self):
- return {}
- @columns.setter
- def columns(self, values):
- # ~ self._columns = values
- #~ https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1awt_1_1grid_1_1XGridColumn.html
- model = create_instance('com.sun.star.awt.grid.DefaultGridColumnModel', True)
- for properties in values:
- column = create_instance('com.sun.star.awt.grid.GridColumn', True)
- for k, v in properties.items():
- setattr(column, k, v)
- model.addColumn(column)
- self.model.ColumnModel = model
- return
-
- @property
- def data(self):
- return self._data
- @data.setter
- def data(self, values):
- self._data = values
- self.clear()
- headings = tuple(range(1, len(values) + 1))
- self._gdm.addRows(headings, values)
- # ~ rows = range(grid_dm.RowCount)
- # ~ colors = [COLORS['GRAY'] if r % 2 else COLORS['WHITE'] for r in rows]
- # ~ grid.Model.RowBackgroundColors = tuple(colors)
- return
-
- @property
- def value(self):
- if self.column == -1 or self.row == -1:
- return ''
- return self[self.column, self.row]
- @value.setter
- def value(self, value):
- if self.column > -1 and self.row > -1:
- self[self.column, self.row] = value
-
- @property
- def row(self):
- return self.obj.CurrentRow
-
- @property
- def row_count(self):
- return self._gdm.RowCount
-
- @property
- def column(self):
- return self.obj.CurrentColumn
-
- @property
- def is_valid(self):
- return not (self.row == -1 or self.column == -1)
-
- @property
- def selected_rows(self):
- value = self.obj.SelectedRows
- return value
-
- @property
- def row_background_colors(self):
- value = self.m.RowBackgroundColors
- return value
- @row_background_colors.setter
- def row_background_colors(self, colors):
- c = Color()
- self.m.RowBackgroundColors = (c(colors[0]), c(colors[1]))
-
- @property
- def formats(self):
- return self._formats
- @formats.setter
- def formats(self, values):
- self._formats = values
-
- def clear(self):
- self._gdm.removeAllRows()
- return
-
- def _format_columns(self, data):
- row = data
- if self.formats:
- for i, f in enumerate(formats):
- if f:
- row[i] = f.format(data[i])
- return row
-
- def add_row(self, data):
- self._data.append(data)
- row = self._format_columns(data)
- self._gdm.addRow(self.row_count + 1, row)
- return
-
- def set_cell_tooltip(self, col, row, value):
- self._gdm.updateCellToolTip(col, row, value)
- return
-
- def get_cell_tooltip(self, col, row):
- value = self._gdm.getCellToolTip(col, row)
- return value
-
- def sort(self, column, asc=True):
- self._gdm.sortByColumn(column, asc)
- self.update_row_heading()
- return
-
- def update_row_heading(self):
- for i in range(self.row_count):
- self._gdm.updateRowHeading(i, i + 1)
- return
-
- def remove_row(self, row):
- self._gdm.removeRow(row)
- del self._data[row]
- self.update_row_heading()
- return
-
-
-class UnoPage(object):
-
- def __init__(self, obj):
- self._obj = obj
- self._events = None
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- pass
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def model(self):
- return self._obj.Model
-
- # ~ @property
- # ~ def id(self):
- # ~ return self.m.TabPageID
-
- @property
- def parent(self):
- return self.obj.Context
-
- def _set_image_url(self, image):
- if _P.exists(image):
- return _P.to_url(image)
-
- path = _P.join(self._path, DIR['images'], image)
- return _P.to_url(path)
-
- def _special_properties(self, tipo, args):
- if tipo == 'link' and not 'Label' in args:
- args['Label'] = args['URL']
- return args
-
- if tipo == 'button':
- if 'ImageURL' in args:
- args['ImageURL'] = self._set_image_url(args['ImageURL'])
- args['FocusOnClick'] = args.get('FocusOnClick', False)
- return args
-
- if tipo == 'roadmap':
- args['Height'] = args.get('Height', self.height)
- if 'Title' in args:
- args['Text'] = args.pop('Title')
- return args
-
- if tipo == 'tree':
- args['SelectionType'] = args.get('SelectionType', SINGLE)
- return args
-
- if tipo == 'grid':
- args['ShowRowHeader'] = args.get('ShowRowHeader', True)
- return args
-
- if tipo == 'pages':
- args['Width'] = args.get('Width', self.width)
- args['Height'] = args.get('Height', self.height)
-
- return args
-
- def add_control(self, args):
- tipo = args.pop('Type').lower()
- root = args.pop('Root', '')
- sheets = args.pop('Sheets', ())
- columns = args.pop('Columns', ())
-
- args = self._special_properties(tipo, args)
- model = self.model.createInstance(UNO_MODELS[tipo])
- _set_properties(model, args)
- name = args['Name']
- self.model.insertByName(name, model)
- control = self.obj.getControl(name)
- _add_listeners(self._events, control, name)
- control = UNO_CLASSES[tipo](control)
-
- if tipo in ('listbox',):
- control.path = self.path
-
- if tipo == 'tree' and root:
- control.root = root
- elif tipo == 'grid' and columns:
- control.columns = columns
- elif tipo == 'pages' and sheets:
- control.sheets = sheets
- control.events = self.events
-
- setattr(self, name, control)
- return control
-
-
-class UnoPages(UnoBaseObject):
-
- def __init__(self, obj):
- super().__init__(obj)
- self._sheets = []
- self._events = None
-
- def __setattr__(self, name, value):
- if name in ('_sheets', '_events'):
- self.__dict__[name] = value
- else:
- super().__setattr__(name, value)
-
- def __getitem__(self, index):
- name = index
- if isinstance(index, int):
- name = f'sheet{index}'
- sheet = self.obj.getControl(name)
- page = UnoPage(sheet)
- page._events = self._events
- return page
-
- @property
- def type(self):
- return 'pages'
-
- @property
- def current(self):
- return self.obj.ActiveTabID
- @property
- def active(self):
- return self.current
-
- @property
- def sheets(self):
- return self._sheets
- @sheets.setter
- def sheets(self, values):
- self._sheets = values
- for i, title in enumerate(values):
- sheet = self.m.createInstance('com.sun.star.awt.UnoPageModel')
- sheet.Title = title
- self.m.insertByName(f'sheet{i + 1}', sheet)
- return
-
- @property
- def events(self):
- return self._events
- @events.setter
- def events(self, controllers):
- self._events = controllers
-
- @property
- def visible(self):
- return self.obj.Visible
- @visible.setter
- def visible(self, value):
- self.obj.Visible = value
-
- def insert(self, title):
- self._sheets.append(title)
- id = len(self._sheets)
- sheet = self.m.createInstance('com.sun.star.awt.UnoPageModel')
- sheet.Title = title
- self.m.insertByName(f'sheet{id}', sheet)
- return self[id]
-
- def remove(self, id):
- self.obj.removeTab(id)
- return
-
- def activate(self, id):
- self.obj.activateTab(id)
- return
-
-
-class UnoSpinButton(UnoBaseObject):
-
- def __init__(self, obj):
- super().__init__(obj)
-
- @property
- def type(self):
- return 'spinbutton'
-
- @property
- def value(self):
- return self.model.Label
- @value.setter
- def value(self, value):
- self.model.Label = value
-
-
-class UnoNumericField(UnoBaseObject):
-
- def __init__(self, obj):
- super().__init__(obj)
-
- @property
- def type(self):
- return 'numeric'
-
- @property
- def int(self):
- return int(self.value)
-
- @property
- def value(self):
- return self.model.Value
- @value.setter
- def value(self, value):
- self.model.Value = value
-
-
-UNO_CLASSES = {
- 'label': UnoLabel,
- 'link': UnoLabelLink,
- 'button': UnoButton,
- 'radio': UnoRadio,
- 'checkbox': UnoCheckBox,
- 'text': UnoText,
- 'image': UnoImage,
- 'listbox': UnoListBox,
- 'roadmap': UnoRoadmap,
- 'tree': UnoTree,
- 'grid': UnoGrid,
- 'pages': UnoPages,
- 'spinbutton': UnoSpinButton,
- 'numeric': UnoNumericField,
-}
-
-UNO_MODELS = {
- 'label': 'com.sun.star.awt.UnoControlFixedTextModel',
- 'link': 'com.sun.star.awt.UnoControlFixedHyperlinkModel',
- 'button': 'com.sun.star.awt.UnoControlButtonModel',
- 'radio': 'com.sun.star.awt.UnoControlRadioButtonModel',
- 'checkbox': 'com.sun.star.awt.UnoControlCheckBoxModel',
- 'text': 'com.sun.star.awt.UnoControlEditModel',
- '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',
- 'grid': 'com.sun.star.awt.grid.UnoControlGridModel',
- 'pages': 'com.sun.star.awt.UnoMultiPageModel',
- 'groupbox': 'com.sun.star.awt.UnoControlGroupBoxModel',
- 'combobox': 'com.sun.star.awt.UnoControlComboBoxModel',
- 'spinbutton': 'com.sun.star.awt.UnoControlSpinButtonModel',
- 'numeric': 'com.sun.star.awt.UnoControlNumericFieldModel',
-}
-# ~ 'CurrencyField': 'com.sun.star.awt.UnoControlCurrencyFieldModel',
-# ~ 'DateField': 'com.sun.star.awt.UnoControlDateFieldModel',
-# ~ 'FileControl': 'com.sun.star.awt.UnoControlFileControlModel',
-# ~ 'FormattedField': 'com.sun.star.awt.UnoControlFormattedFieldModel',
-# ~ 'PatternField': 'com.sun.star.awt.UnoControlPatternFieldModel',
-# ~ 'ProgressBar': 'com.sun.star.awt.UnoControlProgressBarModel',
-# ~ 'ScrollBar': 'com.sun.star.awt.UnoControlScrollBarModel',
-# ~ 'SimpleAnimation': 'com.sun.star.awt.UnoControlSimpleAnimationModel',
-# ~ 'Throbber': 'com.sun.star.awt.UnoControlThrobberModel',
-# ~ 'TimeField': 'com.sun.star.awt.UnoControlTimeFieldModel',
-
-
-class LODialog(object):
- SEPARATION = 5
- MODELS = {
- 'label': 'com.sun.star.awt.UnoControlFixedTextModel',
- 'link': 'com.sun.star.awt.UnoControlFixedHyperlinkModel',
- 'button': 'com.sun.star.awt.UnoControlButtonModel',
- 'radio': 'com.sun.star.awt.UnoControlRadioButtonModel',
- 'checkbox': 'com.sun.star.awt.UnoControlCheckBoxModel',
- 'text': 'com.sun.star.awt.UnoControlEditModel',
- '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',
- 'grid': 'com.sun.star.awt.grid.UnoControlGridModel',
- 'pages': 'com.sun.star.awt.UnoMultiPageModel',
- 'groupbox': 'com.sun.star.awt.UnoControlGroupBoxModel',
- 'combobox': 'com.sun.star.awt.UnoControlComboBoxModel',
- 'spinbutton': 'com.sun.star.awt.UnoControlSpinButtonModel',
- 'numeric': 'com.sun.star.awt.UnoControlNumericFieldModel',
- }
-
- def __init__(self, args={}):
- self._obj = self._create(args)
- self._model = self.obj.Model
- self._events = None
- self._modal = True
- self._controls = {}
- self._color_on_focus = COLOR_ON_FOCUS
- self._id = ''
- self._path = ''
- self._init_controls()
-
- def _create(self, args):
- service = 'com.sun.star.awt.DialogProvider'
- path = args.pop('Path', '')
- if path:
- dp = create_instance(service, True)
- dlg = dp.createDialog(_P.to_url(path))
- return dlg
-
- if 'Location' in args:
- name = args['Name']
- library = args.get('Library', 'Standard')
- location = args.get('Location', 'application').lower()
- if location == 'user':
- location = 'application'
- url = f'vnd.sun.star.script:{library}.{name}?location={location}'
- if location == 'document':
- dp = create_instance(service, args=docs.active.obj)
- else:
- dp = create_instance(service, True)
- # ~ uid = docs.active.uid
- # ~ url = f'vnd.sun.star.tdoc:/{uid}/Dialogs/{library}/{name}.xml'
- dlg = dp.createDialog(url)
- return dlg
-
- dlg = create_instance('com.sun.star.awt.UnoControlDialog', True)
- model = create_instance('com.sun.star.awt.UnoControlDialogModel', True)
- toolkit = create_instance('com.sun.star.awt.Toolkit', True)
-
- args['Title'] = args.get('Title', TITLE)
- args['Width'] = args.get('Width', 200)
- args['Height'] = args.get('Height', 150)
-
- _set_properties(model, args)
- dlg.setModel(model)
- dlg.setVisible(False)
- dlg.createPeer(toolkit, None)
- return dlg
-
- def _get_type_control(self, name):
- name = name.split('.')[2]
- types = {
- 'UnoFixedTextControl': 'label',
- 'UnoEditControl': 'text',
- 'UnoButtonControl': 'button',
- }
- return types[name]
-
- def _init_controls(self):
- for control in self.obj.getControls():
- tipo = self._get_type_control(control.ImplementationName)
- name = control.Model.Name
- control = UNO_CLASSES[tipo](control)
- setattr(self, name, control)
- return
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def model(self):
- return self._model
-
- @property
- def controls(self):
- return self._controls
-
- @property
- def path(self):
- return self._path
- @property
- def path_images(self):
- return _P.join(self.path, DIR['images'])
- @property
- def id(self):
- return self._id
- @id.setter
- def id(self, value):
- self._id = value
- self._path = _P.from_id(value)
-
- @property
- def height(self):
- return self.model.Height
- @height.setter
- def height(self, value):
- self.model.Height = value
-
- @property
- def width(self):
- return self.model.Width
- @width.setter
- def width(self, value):
- self.model.Width = value
-
- @property
- def visible(self):
- return self.obj.Visible
- @visible.setter
- def visible(self, value):
- self.obj.Visible = value
-
- @property
- def step(self):
- return self.model.Step
- @step.setter
- def step(self, value):
- self.model.Step = value
-
- @property
- def events(self):
- return self._events
- @events.setter
- def events(self, controllers):
- self._events = controllers(self)
- self._connect_listeners()
-
- @property
- def color_on_focus(self):
- return self._color_on_focus
- @color_on_focus.setter
- def color_on_focus(self, value):
- self._color_on_focus = get_color(value)
-
- def _connect_listeners(self):
- for control in self.obj.Controls:
- _add_listeners(self.events, control, control.Model.Name)
- return
-
- def _set_image_url(self, image):
- if _P.exists(image):
- return _P.to_url(image)
-
- path = _P.join(self._path, DIR['images'], image)
- return _P.to_url(path)
-
- def _special_properties(self, tipo, args):
- if tipo == 'link' and not 'Label' in args:
- args['Label'] = args['URL']
- return args
-
- if tipo == 'button':
- if 'ImageURL' in args:
- args['ImageURL'] = self._set_image_url(args['ImageURL'])
- args['FocusOnClick'] = args.get('FocusOnClick', False)
- return args
-
- if tipo == 'roadmap':
- args['Height'] = args.get('Height', self.height)
- if 'Title' in args:
- args['Text'] = args.pop('Title')
- return args
-
- if tipo == 'tree':
- args['SelectionType'] = args.get('SelectionType', SINGLE)
- return args
-
- if tipo == 'grid':
- args['X'] = args.get('X', self.SEPARATION)
- args['Y'] = args.get('Y', self.SEPARATION)
- args['Width'] = args.get('Width', self.width - self.SEPARATION * 2)
- args['Height'] = args.get('Height', self.height - self.SEPARATION * 2)
- args['ShowRowHeader'] = args.get('ShowRowHeader', True)
- return args
-
- if tipo == 'pages':
- args['Width'] = args.get('Width', self.width)
- args['Height'] = args.get('Height', self.height)
-
- return args
-
- def add_control(self, args):
- tipo = args.pop('Type').lower()
- root = args.pop('Root', '')
- sheets = args.pop('Sheets', ())
- columns = args.pop('Columns', ())
-
- args = self._special_properties(tipo, args)
- model = self.model.createInstance(self.MODELS[tipo])
-
- _set_properties(model, args)
- name = args['Name']
- self.model.insertByName(name, model)
- control = self.obj.getControl(name)
- _add_listeners(self.events, control, name)
- control = UNO_CLASSES[tipo](control)
-
- if tipo in ('listbox',):
- control.path = self.path
-
- if tipo == 'tree' and root:
- control.root = root
- elif tipo == 'grid' and columns:
- control.columns = columns
- elif tipo == 'pages' and sheets:
- control.sheets = sheets
- control.events = self.events
-
- setattr(self, name, control)
- self._controls[name] = control
- return control
-
- def center(self, control, x=0, y=0):
- w = self.width
- h = self.height
-
- if isinstance(control, tuple):
- wt = self.SEPARATION * -1
- for c in control:
- wt += c.width + self.SEPARATION
- x = w / 2 - wt / 2
- for c in control:
- c.x = x
- x = c.x + c.width + self.SEPARATION
- return
-
- if x < 0:
- x = w + x - control.width
- elif x == 0:
- x = w / 2 - control.width / 2
- if y < 0:
- y = h + y - control.height
- elif y == 0:
- y = h / 2 - control.height / 2
- control.x = x
- control.y = y
- return
-
- def open(self, modal=True):
- self._modal = modal
- if modal:
- return self.obj.execute()
- else:
- self.visible = True
- return
-
- def close(self, value=0):
- if self._modal:
- value = self.obj.endDialog(value)
- else:
- self.visible = False
- self.obj.dispose()
- return value
-
- def set_values(self, data):
- for k, v in data.items():
- self._controls[k].value = v
- return
-
-
-class LOCells(object):
-
- def __getitem__(self, index):
- return LODocs().active.active[index]
-
-
-class LOWindow(object):
- EMPTY = """
-
-"""
- MODELS = {
- 'label': 'com.sun.star.awt.UnoControlFixedTextModel',
- 'link': 'com.sun.star.awt.UnoControlFixedHyperlinkModel',
- 'button': 'com.sun.star.awt.UnoControlButtonModel',
- 'radio': 'com.sun.star.awt.UnoControlRadioButtonModel',
- 'checkbox': 'com.sun.star.awt.UnoControlCheckBoxModel',
- 'text': 'com.sun.star.awt.UnoControlEditModel',
- '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',
- 'grid': 'com.sun.star.awt.grid.UnoControlGridModel',
- 'pages': 'com.sun.star.awt.UnoMultiPageModel',
- 'groupbox': 'com.sun.star.awt.UnoControlGroupBoxModel',
- 'combobox': 'com.sun.star.awt.UnoControlComboBoxModel',
- }
-
- def __init__(self, args):
- self._events = None
- self._menu = None
- self._container = None
- self._model = None
- self._id = ''
- self._path = ''
- self._obj = self._create(args)
-
- def _create(self, properties):
- ps = (
- properties.get('X', 0),
- properties.get('Y', 0),
- properties.get('Width', 500),
- properties.get('Height', 500),
- )
- self._title = properties.get('Title', TITLE)
- self._create_frame(ps)
- self._create_container(ps)
- self._create_subcontainer(ps)
- # ~ self._create_splitter(ps)
- return
-
- def _create_frame(self, ps):
- service = 'com.sun.star.frame.TaskCreator'
- tc = create_instance(service, True)
- self._frame = tc.createInstanceWithArguments((
- NamedValue('FrameName', 'EasyMacroWin'),
- NamedValue('PosSize', Rectangle(*ps)),
- ))
- self._window = self._frame.getContainerWindow()
- self._toolkit = self._window.getToolkit()
- desktop = get_desktop()
- self._frame.setCreator(desktop)
- desktop.getFrames().append(self._frame)
- self._frame.Title = self._title
- return
-
- def _create_container(self, ps):
- service = 'com.sun.star.awt.UnoControlContainer'
- self._container = create_instance(service, True)
- service = 'com.sun.star.awt.UnoControlContainerModel'
- model = create_instance(service, True)
- model.BackgroundColor = get_color((225, 225, 225))
- self._container.setModel(model)
- self._container.createPeer(self._toolkit, self._window)
- self._container.setPosSize(*ps, POSSIZE)
- self._frame.setComponent(self._container, None)
- return
-
- def _create_subcontainer(self, ps):
- service = 'com.sun.star.awt.ContainerWindowProvider'
- cwp = create_instance(service, True)
-
- path_tmp = _P.save_tmp(self.EMPTY)
- subcont = cwp.createContainerWindow(
- _P.to_url(path_tmp), '', self._container.getPeer(), None)
- _P.kill(path_tmp)
-
- subcont.setPosSize(0, 0, 500, 500, POSSIZE)
- subcont.setVisible(True)
- self._container.addControl('subcont', subcont)
- self._subcont = subcont
- self._model = subcont.Model
- return
-
- def _create_popupmenu(self, menus):
- menu = create_instance('com.sun.star.awt.PopupMenu', True)
- for i, m in enumerate(menus):
- label = m['label']
- cmd = m.get('event', '')
- if not cmd:
- cmd = label.lower().replace(' ', '_')
- if label == '-':
- menu.insertSeparator(i)
- else:
- menu.insertItem(i, label, m.get('style', 0), i)
- menu.setCommand(i, cmd)
- # ~ menu.setItemImage(i, path?, True)
- menu.addMenuListener(EventsMenu(self.events))
- return menu
-
- def _create_menu(self, menus):
- #~ https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1awt_1_1XMenu.html
- #~ nItemId specifies the ID of the menu item to be inserted.
- #~ aText specifies the label of the menu item.
- #~ nItemStyle 0 = Standard, CHECKABLE = 1, RADIOCHECK = 2, AUTOCHECK = 4
- #~ nItemPos specifies the position where the menu item will be inserted.
- self._menu = create_instance('com.sun.star.awt.MenuBar', True)
- for i, m in enumerate(menus):
- self._menu.insertItem(i, m['label'], m.get('style', 0), i)
- cmd = m['label'].lower().replace(' ', '_')
- self._menu.setCommand(i, cmd)
- submenu = self._create_popupmenu(m['submenu'])
- self._menu.setPopupMenu(i, submenu)
-
- self._window.setMenuBar(self._menu)
- return
-
- def _add_listeners(self, control=None):
- if self.events is None:
- return
- controller = EventsWindow(self)
- self._window.addTopWindowListener(controller)
- self._window.addWindowListener(controller)
- # ~ self._container.addKeyListener(EventsKeyWindow(self))
- return
-
- def _set_image_url(self, image):
- if _P.exists(image):
- return _P.to_url(image)
-
- path = _P.join(self._path, DIR['images'], image)
- return _P.to_url(path)
-
- def _special_properties(self, tipo, args):
- if tipo == 'link' and not 'Label' in args:
- args['Label'] = args['URL']
- return args
-
- if tipo == 'button':
- if 'ImageURL' in args:
- args['ImageURL'] = self._set_image_url(args['ImageURL'])
- args['FocusOnClick'] = args.get('FocusOnClick', False)
- return args
-
- if tipo == 'roadmap':
- args['Height'] = args.get('Height', self.height)
- if 'Title' in args:
- args['Text'] = args.pop('Title')
- return args
-
- if tipo == 'tree':
- args['SelectionType'] = args.get('SelectionType', SINGLE)
- return args
-
- if tipo == 'grid':
- args['ShowRowHeader'] = args.get('ShowRowHeader', True)
- return args
-
- if tipo == 'pages':
- args['Width'] = args.get('Width', self.width)
- args['Height'] = args.get('Height', self.height)
-
- return args
-
- def add_control(self, args):
- tipo = args.pop('Type').lower()
- root = args.pop('Root', '')
- sheets = args.pop('Sheets', ())
- columns = args.pop('Columns', ())
-
- args = self._special_properties(tipo, args)
- model = self.model.createInstance(self.MODELS[tipo])
- _set_properties(model, args)
- name = args['Name']
- self.model.insertByName(name, model)
- control = self._subcont.getControl(name)
- _add_listeners(self.events, control, name)
- control = UNO_CLASSES[tipo](control)
-
- # ~ if tipo in ('listbox',):
- # ~ control.path = self.path
-
- if tipo == 'tree' and root:
- control.root = root
- elif tipo == 'grid' and columns:
- control.columns = columns
- elif tipo == 'pages' and sheets:
- control.sheets = sheets
- control.events = self.events
-
- setattr(self, name, control)
- return control
-
- @property
- def events(self):
- return self._events
- @events.setter
- def events(self, controllers):
- self._events = controllers(self)
- self._add_listeners()
-
- @property
- def model(self):
- return self._model
-
- @property
- def width(self):
- return self._container.Size.Width
-
- @property
- def height(self):
- return self._container.Size.Height
-
- @property
- def name(self):
- return self._title.lower().replace(' ', '_')
-
- def add_menu(self, menus):
- self._create_menu(menus)
- return
-
- def open(self):
- self._window.setVisible(True)
- return
-
- def close(self):
- self._window.setMenuBar(None)
- self._window.dispose()
- self._frame.close(True)
- return
-
-
-class LODBServer(object):
- DRIVERS = {
- 'mysql': 'mysqlc',
- 'mariadb': 'mysqlc',
- 'postgres': 'postgresql:postgresql',
- }
- PORTS = {
- 'mysql': 3306,
- 'mariadb': 3306,
- 'postgres': 5432,
- }
-
- def __init__(self):
- self._conn = None
- self._error = 'Not connected'
- self._type = ''
- self._drivers = []
-
- def __str__(self):
- return f'DB type {self._type}'
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- self.disconnet()
-
- @property
- def is_connected(self):
- return not self._conn is None
-
- @property
- def error(self):
- return self._error
-
- @property
- def drivers(self):
- return self._drivers
-
- def disconnet(self):
- if not self._conn is None:
- if not self._conn.isClosed():
- self._conn.close()
- self._conn.dispose()
- return
-
- def connect(self, options={}):
- args = options.copy()
- self._error = ''
- self._type = args.get('type', 'postgres')
- driver = self.DRIVERS[self._type]
- server = args.get('server', 'localhost')
- port = args.get('port', self.PORTS[self._type])
- dbname = args.get('dbname', '')
- user = args['user']
- password = args['password']
-
- data = {'user': user, 'password': password}
- url = f'sdbc:{driver}:{server}:{port}/{dbname}'
-
- # ~ https://downloads.mariadb.com/Connectors/java/
- # ~ data['JavaDriverClass'] = 'org.mariadb.jdbc.Driver'
- # ~ url = f'jdbc:mysql://{server}:{port}/{dbname}'
-
- args = dict_to_property(data)
- manager = create_instance('com.sun.star.sdbc.DriverManager')
- self._drivers = [d.ImplementationName for d in manager]
-
- try:
- self._conn = manager.getConnectionWithInfo(url, args)
- except Exception as e:
- error(e)
- self._error = str(e)
-
- return self
-
- def execute(self, sql):
- query = self._conn.createStatement()
- try:
- query.execute(sql)
- result = True
- except Exception as e:
- error(e)
- self._error = str(e)
- result = False
-
- return result
-
-
-def create_window(args):
- return LOWindow(args)
-
-
-class Paths(object):
-
- @classmethod
- def image(cls, path):
- # ~ sfa = create_instance('com.sun.star.ucb.SimpleFileAccess')
- # ~ stream = sfa.openFileRead(cls.to_url(path))
- gp = create_instance('com.sun.star.graphic.GraphicProvider')
- if isinstance(path, str):
- properties = (PropertyValue(Name='URL', Value=cls.to_url(path)),)
- else:
- properties = (PropertyValue(Name='InputStream', Value=path),)
- image = gp.queryGraphic(properties)
- return image
-
-
-class IOStream(object):
-
- @classmethod
- def qr(cls, data, **kwargs):
- import segno
-
- kwargs['kind'] = kwargs.get('kind', 'svg')
- kwargs['scale'] = kwargs.get('scale', 8)
- kwargs['border'] = kwargs.get('border', 2)
- buffer = cls.buffer()
- segno.make(data).save(buffer, **kwargs)
- stream = cls.input(buffer)
- return stream
-
-
-class SpellChecker(object):
-
- def __init__(self):
- service = 'com.sun.star.linguistic2.SpellChecker'
- self._spellchecker = create_instance(service, True)
- self._locale = LOCALE
-
- @property
- def locale(self):
- slocal = f'{self._locale.Language}-{self._locale.Country}'
- return slocale
- @locale.setter
- def locale(self, value):
- lang = value.split('-')
- self._locale = Locale(lang[0], lang[1], '')
-
- def is_valid(self, word):
- result = self._spellchecker.isValid(word, self._locale, ())
- return result
-
- def spell(self, word):
- result = self._spellchecker.spell(word, self._locale, ())
- if result:
- result = result.getAlternatives()
- if not isinstance(result, tuple):
- result = ()
- return result
-
-
-def spell(word, locale=''):
- sc = SpellChecker()
- if locale:
- sc.locale = locale
- return sc.spell(word)
-
-
-def __getattr__(name):
- if name == 'current_region':
- return LODocs().active.selection.current_region
- if name in ('rectangle', 'pos_size'):
- return Rectangle()
- if name == 'db':
- return LODBServer()
- if name == 'cells':
- return LOCells()
- raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
-
-
-def create_dialog(args={}):
- return LODialog(args)
-
-
-def inputbox(message, default='', title=TITLE, echochar=''):
-
- class ControllersInput(object):
-
- def __init__(self, dlg):
- self.d = dlg
-
- def cmd_ok_action(self, event):
- self.d.close(1)
- return
-
- args = {
- 'Title': title,
- 'Width': 200,
- 'Height': 80,
- }
- dlg = LODialog(args)
- dlg.events = ControllersInput
-
- args = {
- 'Type': 'Label',
- 'Name': 'lbl_msg',
- 'Label': message,
- 'Width': 140,
- 'Height': 50,
- 'X': 5,
- 'Y': 5,
- 'MultiLine': True,
- 'Border': 1,
- }
- dlg.add_control(args)
-
- args = {
- 'Type': 'Text',
- 'Name': 'txt_value',
- 'Text': default,
- 'Width': 190,
- 'Height': 15,
- }
- if echochar:
- args['EchoChar'] = ord(echochar[0])
- dlg.add_control(args)
- dlg.txt_value.move(dlg.lbl_msg)
-
- args = {
- 'Type': 'button',
- 'Name': 'cmd_ok',
- 'Label': _('OK'),
- 'Width': 40,
- 'Height': 15,
- 'DefaultButton': True,
- 'PushButtonType': 1,
- }
- dlg.add_control(args)
- dlg.cmd_ok.move(dlg.lbl_msg, 10, 0)
-
- args = {
- 'Type': 'button',
- 'Name': 'cmd_cancel',
- 'Label': _('Cancel'),
- 'Width': 40,
- 'Height': 15,
- 'PushButtonType': 2,
- }
- dlg.add_control(args)
- dlg.cmd_cancel.move(dlg.cmd_ok)
-
- if dlg.open():
- return dlg.txt_value.value
-
- return ''
diff --git a/source/easymacro.bk2 b/source/easymacro.bk2
deleted file mode 100644
index 0d78e21..0000000
--- a/source/easymacro.bk2
+++ /dev/null
@@ -1,4671 +0,0 @@
-#!/usr/bin/env python3
-
-# == Rapid Develop Macros in LibreOffice ==
-
-import base64
-import ctypes
-import gettext
-import zipfile
-
-from collections import OrderedDict
-from collections.abc import MutableMapping
-from decimal import Decimal
-from enum import IntEnum
-
-import imaplib
-
-from com.sun.star.awt.PosSize import POSSIZE, SIZE
-
-from com.sun.star.sheet import TableFilterField
-from com.sun.star.table.CellContentType import EMPTY, VALUE, TEXT, FORMULA
-from com.sun.star.util import Time, Date, DateTime
-
-from com.sun.star.text.ControlCharacter import PARAGRAPH_BREAK
-
-from com.sun.star.lang import Locale
-from com.sun.star.awt import XActionListener
-from com.sun.star.awt import XMenuListener
-from com.sun.star.awt import XMouseListener
-from com.sun.star.awt import XMouseMotionListener
-from com.sun.star.awt import XFocusListener
-from com.sun.star.awt import XKeyListener
-from com.sun.star.awt import XItemListener
-from com.sun.star.awt import XTabListener
-from com.sun.star.awt import XSpinListener
-from com.sun.star.awt import XWindowListener
-from com.sun.star.awt import XTopWindowListener
-from com.sun.star.awt.grid import XGridDataListener
-from com.sun.star.awt.grid import XGridSelectionListener
-from com.sun.star.script import ScriptEventDescriptor
-
-
-# ~ https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1awt_1_1FontUnderline.html
-from com.sun.star.awt import FontUnderline
-from com.sun.star.style.VerticalAlignment import TOP, MIDDLE, BOTTOM
-
-from com.sun.star.view.SelectionType import SINGLE, MULTI, RANGE
-
-from com.sun.star.sdb.CommandType import TABLE, QUERY, COMMAND
-
-try:
- from peewee import Database, DateTimeField, DateField, TimeField, \
- __exception_wrapper__
-except ImportError as e:
- Database = DateField = TimeField = DateTimeField = object
- print('You need install peewee, only if you will develop with Base')
-
-
-LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
-LOG_DATE = '%d/%m/%Y %H:%M:%S'
-logging.addLevelName(logging.ERROR, '\033[1;41mERROR\033[1;0m')
-logging.addLevelName(logging.DEBUG, '\x1b[33mDEBUG\033[1;0m')
-logging.addLevelName(logging.INFO, '\x1b[32mINFO\033[1;0m')
-logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT, datefmt=LOG_DATE)
-log = logging.getLogger(__name__)
-
-
-# ~ You can get custom salt
-# ~ codecs.encode(os.urandom(16), 'hex')
-# ~ but, not modify this file, modify in import file
-SALT = b'c9548699d4e432dfd2b46adddafbb06d'
-
-LOG_NAME = 'ZAZ'
-
-LEFT = 0
-CENTER = 1
-RIGHT = 2
-
-CALC = 'calc'
-WRITER = 'writer'
-DRAW = 'draw'
-IMPRESS = 'impress'
-BASE = 'base'
-MATH = 'math'
-BASIC = 'basic'
-MAIN = 'main'
-TYPE_DOC = {
- CALC: 'com.sun.star.sheet.SpreadsheetDocument',
- WRITER: 'com.sun.star.text.TextDocument',
- DRAW: 'com.sun.star.drawing.DrawingDocument',
- IMPRESS: 'com.sun.star.presentation.PresentationDocument',
- MATH: 'com.sun.star.formula.FormulaProperties',
- BASE: 'com.sun.star.sdb.DocumentDataSource',
- BASIC: 'com.sun.star.script.BasicIDE',
- MAIN: 'com.sun.star.frame.StartModule',
-}
-
-OBJ_SHAPE = 'com.sun.star.comp.sc.ScShapeObj'
-OBJ_GRAPHIC = 'SwXTextGraphicObject'
-
-OBJ_TEXTS = 'SwXTextRanges'
-OBJ_TEXT = 'SwXTextRange'
-
-CLSID = {
- 'FORMULA': '078B7ABA-54FC-457F-8551-6147e776a997',
-}
-
-SERVICES = {
- 'TEXT_EMBEDDED': 'com.sun.star.text.TextEmbeddedObject',
- 'TEXT_TABLE': 'com.sun.star.text.TextTable',
- 'GRAPHIC': 'com.sun.star.text.GraphicObject',
-}
-
-
-# ~ from com.sun.star.sheet.FilterOperator import EMPTY, NO_EMPTY, EQUAL, NOT_EQUAL
-class FilterOperator(IntEnum):
- EMPTY = 0
- NO_EMPTY = 1
- EQUAL = 2
- NOT_EQUAL = 3
-
-# ~ https://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1awt_1_1UnoControlEditModel.html#a54d3ff280d892218d71e667f81ce99d4
-class Border(IntEnum):
- NO_BORDER = 0
- BORDER = 1
- SIMPLE = 2
-
-
-# ~ https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1sheet.html#aa5aa6dbecaeb5e18a476b0a58279c57a
-class ValidationType():
- from com.sun.star.sheet.ValidationType \
- import ANY, WHOLE, DECIMAL, DATE, TIME, TEXT_LEN, LIST, CUSTOM
-VT = ValidationType
-
-
-# ~ https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1sheet.html#aecf58149730f4c8c5c18c70f3c7c5db7
-class ValidationAlertStyle():
- from com.sun.star.sheet.ValidationAlertStyle \
- import STOP, WARNING, INFO, MACRO
-VAS = ValidationAlertStyle
-
-
-# ~ https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1sheet_1_1ConditionOperator2.html
-class ConditionOperator():
- from com.sun.star.sheet.ConditionOperator2 \
- import NONE, EQUAL, NOT_EQUAL, GREATER, GREATER_EQUAL, LESS, \
- LESS_EQUAL, BETWEEN, NOT_BETWEEN, FORMULA, DUPLICATE, NOT_DUPLICATE
-CO = ConditionOperator
-
-
-class DataPilotFieldOrientation():
- from com.sun.star.sheet.DataPilotFieldOrientation \
- import HIDDEN, COLUMN, ROW, PAGE, DATA
-DPFO = DataPilotFieldOrientation
-
-
-class CellInsertMode():
- from com.sun.star.sheet.CellInsertMode import DOWN, RIGHT, ROWS, COLUMNS
-CIM = CellInsertMode
-
-
-class CellDeleteMode():
- from com.sun.star.sheet.CellDeleteMode import UP, LEFT, ROWS, COLUMNS
-CDM = CellDeleteMode
-
-
-class FormButtonType():
- from com.sun.star.form.FormButtonType import PUSH, SUBMIT, RESET, URL
-FBT = FormButtonType
-
-
-class TextContentAnchorType():
- from com.sun.star.text.TextContentAnchorType \
- import AT_PARAGRAPH, AS_CHARACTER, AT_PAGE, AT_FRAME, AT_CHARACTER
-TCAT = TextContentAnchorType
-
-
-SECONDS_DAY = 60 * 60 * 24
-DIR = {
- 'images': 'images',
- 'locales': 'locales',
-}
-
-KEY = {
- 'enter': 1280,
-}
-
-DEFAULT_MIME_TYPE = 'png'
-MIME_TYPE = {
- 'png': 'image/png',
- 'jpg': 'image/jpeg',
-}
-
-
-try:
- COUNTRY = LANGUAGE.split('-')[1]
-except:
- COUNTRY = ''
-LOCALE = Locale(LANG, COUNTRY, '')
-
-
-def inspect(obj: Any, to_sheet: bool=True) -> None:
- if hasattr(obj, 'obj'):
- obj = obj.obj
-
- if to_sheet:
- _inspect_to_sheet(obj)
- else:
- zaz = create_instance('net.elmau.zaz.inspect')
- zaz.inspect(obj)
- return
-
-
-def get_type_doc(obj: Any) -> str:
- for k, v in TYPE_DOC.items():
- if obj.supportsService(v):
- return k
- return ''
-
-
-def _get_class_doc(obj: Any) -> Any:
- classes = {
- CALC: LOCalc,
- WRITER: LOWriter,
- DRAW: LODraw,
- IMPRESS: LOImpress,
- BASE: LOBase,
- MATH: LOMath,
- BASIC: LOBasic,
- }
- type_doc = get_type_doc(obj)
- return classes[type_doc](obj)
-
-
-def _date_to_struct(value):
- if isinstance(value, datetime.datetime):
- d = DateTime()
- d.Year = value.year
- d.Month = value.month
- d.Day = value.day
- d.Hours = value.hour
- d.Minutes = value.minute
- d.Seconds = value.second
- elif isinstance(value, datetime.date):
- d = Date()
- d.Day = value.day
- d.Month = value.month
- d.Year = value.year
- elif isinstance(value, datetime.time):
- d = Time()
- d.Hours = value.hour
- d.Minutes = value.minute
- d.Seconds = value.second
- return d
-
-
-def _struct_to_date(value):
- d = None
- if isinstance(value, Time):
- d = datetime.time(value.Hours, value.Minutes, value.Seconds)
- elif isinstance(value, Date):
- if value != Date():
- d = datetime.date(value.Year, value.Month, value.Day)
- elif isinstance(value, DateTime):
- if value.Year > 0:
- d = datetime.datetime(
- value.Year, value.Month, value.Day,
- value.Hours, value.Minutes, value.Seconds)
- return d
-
-
-def install_locales(path: str, domain: str='base', dir_locales=DIR['locales']):
- path_locales = _P.join(_P(path).path, dir_locales)
- try:
- lang = gettext.translation(domain, path_locales, languages=[LANG])
- lang.install()
- _ = lang.gettext
- except Exception as e:
- from gettext import gettext as _
- error(e)
- return _
-
-
-def _export_image(obj, args):
- name = 'com.sun.star.drawing.GraphicExportFilter'
- exporter = create_instance(name)
- path = _P.to_system(args['URL'])
- args = dict_to_property(args)
- exporter.setSourceDocument(obj)
- exporter.filter(args)
- return _P.exists(path)
-
-
-def get_size_screen():
- res = ''
- if IS_WIN:
- user32 = ctypes.windll.user32
- res = f'{user32.GetSystemMetrics(0)}x{user32.GetSystemMetrics(1)}'
- else:
- try:
- args = 'xrandr | grep "*" | cut -d " " -f4'
- res = run(args, split=False)
- except Exception as e:
- error(e)
- return res.strip()
-
-
-def _get_key(password):
- from cryptography.hazmat.primitives import hashes
- from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
-
- kdf = PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt=SALT,
- iterations=100000)
- key = base64.urlsafe_b64encode(kdf.derive(password.encode()))
- return key
-
-
-def encrypt(data, password):
- from cryptography.fernet import Fernet
-
- f = Fernet(_get_key(password))
- if isinstance(data, str):
- data = data.encode()
- token = f.encrypt(data).decode()
- return token
-
-
-def decrypt(token, password):
- from cryptography.fernet import Fernet, InvalidToken
-
- data = ''
- f = Fernet(_get_key(password))
- try:
- data = f.decrypt(token.encode()).decode()
- except InvalidToken as e:
- error('Invalid Token')
- return data
-
-
-def switch_design_mode(doc):
- call_dispatch(doc.frame, '.uno:SwitchControlDesignMode')
- return
-
-
-class ImapServer(object):
-
- def __init__(self, config):
- self._server = None
- self._error = ''
- self._is_connect = self._login(config)
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- self.close()
-
- @property
- def is_connect(self):
- return self._is_connect
-
- @property
- def error(self):
- return self._error
-
- def _login(self, config):
- try:
- # ~ hosts = 'gmail' in config['server']
- if config['ssl']:
- self._server = imaplib.IMAP4_SSL(config['server'], config['port'])
- else:
- self._server = imaplib.IMAP4(config['server'], config['port'])
- self._server.login(config['user'], config['password'])
- self._server.select()
- return True
- except imaplib.IMAP4.error as e:
- self._error = str(e)
- return False
- except Exception as e:
- self._error = str(e)
- return False
- return False
-
- def get_folders(self, exclude=()):
- folders = {}
- result, subdir = self._server.list()
- for s in subdir:
- print(s.decode('utf-8'))
- return folders
-
- def close(self):
- try:
- self._server.close()
- self._server.logout()
- msg = 'Close connection...'
- debug(msg)
- except:
- pass
- return
-
-
-# ~ Classes
-
-class LOBaseObject(object):
-
- def __init__(self, obj):
- self._obj = obj
-
- def __setattr__(self, name, value):
- exists = hasattr(self, name)
- if not exists and not name in ('_obj', '_index', '_view'):
- setattr(self._obj, name, value)
- else:
- super().__setattr__(name, value)
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- pass
-
- @property
- def obj(self):
- return self._obj
-
-
-class LOCalc(LODocument):
-
- @property
- def db_ranges(self):
- # ~ return LOCalcDataBaseRanges(self.obj.DataBaseRanges)
- return self.obj.DatabaseRanges
-
- def render(self, data, sheet=None, clean=True):
- if sheet is None:
- sheet = self.active
- return sheet.render(data, clean=clean)
-
-
-class LOChart(object):
-
- def __init__(self, name, obj, draw_page):
- self._name = name
- self._obj = obj
- self._eobj = self._obj.EmbeddedObject
- self._type = 'Column'
- self._cell = None
- self._shape = self._get_shape(draw_page)
- self._pos = self._shape.Position
-
- def __getitem__(self, index):
- return LOBaseObject(self.diagram.getDataRowProperties(index))
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- pass
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def name(self):
- return self._name
-
- @property
- def diagram(self):
- return self._eobj.Diagram
-
- @property
- def type(self):
- return self._type
- @type.setter
- def type(self, value):
- self._type = value
- if value == 'Bar':
- self.diagram.Vertical = True
- return
- type_chart = f'com.sun.star.chart.{value}Diagram'
- self._eobj.setDiagram(self._eobj.createInstance(type_chart))
-
- @property
- def cell(self):
- return self._cell
- @cell.setter
- def cell(self, value):
- self._cell = value
- self._shape.Anchor = value.obj
-
- @property
- def position(self):
- return self._pos
- @position.setter
- def position(self, value):
- self._pos = value
- self._shape.Position = value
-
- def _get_shape(self, draw_page):
- for shape in draw_page:
- if shape.PersistName == self.name:
- break
- return shape
-
-
-class LOSheetCharts(object):
-
- def __init__(self, obj, sheet):
- self._obj = obj
- self._sheet = sheet
-
- def __getitem__(self, index):
- return LOChart(index, self.obj[index], self._sheet.draw_page)
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- pass
-
- def __contains__(self, item):
- return item in self.obj
-
- def __len__(self):
- return len(self.obj)
-
- @property
- def obj(self):
- return self._obj
-
- def new(self, name, pos_size, data):
- self.obj.addNewByName(name, pos_size, data, True, True)
- return LOChart(name, self.obj[name], self._sheet.draw_page)
-
-
-class LOSheetTableField(object):
-
- def __init__(self, obj):
- self._obj = obj
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- pass
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def name(self):
- return self.obj.Name
-
- @property
- def orientation(self):
- return self.obj.Orientation
- @orientation.setter
- def orientation(self, value):
- self.obj.Orientation = value
-
-
-# ~ com.sun.star.sheet.DataPilotFieldOrientation.ROW
-class LOSheetTable(object):
-
- def __init__(self, obj):
- self._obj = obj
- self._source = None
-
- def __getitem__(self, index):
- field = self.obj.DataPilotFields[index]
- return LOSheetTableField(field)
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- pass
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def filter(self):
- return self.obj.ShowFilterButton
- @filter.setter
- def filter(self, value):
- self.obj.ShowFilterButton = value
-
- @property
- def source(self):
- return self._source
- @source.setter
- def source(self, value):
- self._source = value
- self.obj.SourceRange = value.range_address
-
- @property
- def rows(self):
- return self.obj.RowFields
- @rows.setter
- def rows(self, values):
- if not isinstance(values, tuple):
- values = (values,)
- for v in values:
- with self[v] as f:
- f.orientation = DPFO.ROW
- @property
- def columns(self):
- return self.obj.ColumnFields
- @columns.setter
- def columns(self, values):
- if not isinstance(values, tuple):
- values = (values,)
- for v in values:
- with self[v] as f:
- f.orientation = DPFO.COLUMN
-
- @property
- def data(self):
- return self.obj.DataFields
- @data.setter
- def data(self, values):
- if not isinstance(values, tuple):
- values = (values,)
- for v in values:
- with self[v] as f:
- f.orientation = DPFO.DATA
-
-
-class LOSheetTables(object):
-
- def __init__(self, obj, sheet):
- self._obj = obj
- self._sheet = sheet
-
- def __getitem__(self, index):
- return LOSheetTable(self.obj[index])
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- pass
-
- def __contains__(self, item):
- return item in self.obj
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def count(self):
- return self.obj.Count
-
- @property
- def names(self):
- return self.obj.ElementNames
-
- def new(self, name, target):
- table = self.obj.createDataPilotDescriptor()
- self.obj.insertNewByName(name, target.address, table)
- return LOSheetTable(self.obj[name])
-
- def remove(self, name):
- self.obj.removeByName(name)
- return
-
-
-# ~ class LOFormControl(LOBaseObject):
-class LOFormControl():
- EVENTS = {
- 'action': 'actionPerformed',
- 'click': 'mousePressed',
- }
- TYPES = {
- 'actionPerformed': 'XActionListener',
- 'mousePressed': 'XMouseListener',
- }
-
- def __init__(self, obj, view, form):
- self._obj = obj
- self._view = view
- self._form = form
- self._m = view.Model
- self._index = -1
-
- # ~ def __setattr__(self, name, value):
- # ~ if name in ('_form', '_view', '_m', '_index'):
- # ~ self.__dict__[name] = value
- # ~ else:
- # ~ super().__setattr__(name, value)
-
- def __str__(self):
- return f'{self.name} ({self.type}) {[self.index]}'
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def form(self):
- return self._form
-
- @property
- def doc(self):
- return self.obj.Parent.Forms.Parent
-
- @property
- def name(self):
- return self._m.Name
- @name.setter
- def name(self, value):
- self._m.Name = value
-
- @property
- def tag(self):
- return self._m.Tag
- @tag.setter
- def tag(self, value):
- self._m.Tag = value
-
- @property
- def index(self):
- return self._index
- @index.setter
- def index(self, value):
- self._index = value
-
- @property
- def enabled(self):
- return self._m.Enabled
- @enabled.setter
- def enabled(self, value):
- self._m.Enabled = value
-
- @property
- def anchor(self):
- return self.obj.Anchor
- @anchor.setter
- def anchor(self, value):
- size = None
- if hasattr(value, 'obj'):
- size = getattr(value, 'size', None)
- value = value.obj
- self.obj.Anchor = value
- if not size is None:
- self.size = size
- try:
- self.obj.ResizeWithCell = True
- except:
- pass
-
- @property
- def size(self):
- return self.obj.Size
- @size.setter
- def size(self, value):
- self.obj.Size = value
-
- @property
- def events(self):
- return self.form.getScriptEvents(self.index)
- def add_event(self, name, macro):
- if not 'name' in macro:
- macro['name'] = '{}_{}'.format(self.name, name)
-
- event = ScriptEventDescriptor()
- event.AddListenerParam = ''
- event.EventMethod = self.EVENTS[name]
- event.ListenerType = self.TYPES[event.EventMethod]
- event.ScriptCode = _get_url_script(macro)
- event.ScriptType = 'Script'
-
- for ev in self.events:
- if ev.EventMethod == event.EventMethod and \
- ev.ListenerType == event.ListenerType:
- self.form.revokeScriptEvent(self.index,
- event.ListenerType, event.EventMethod, event.AddListenerParam)
- break
-
- self.form.registerScriptEvent(self.index, event)
- return
-
- def set_focus(self):
- self._view.setFocus()
- return
-
-
-class LOFormControlLabel(LOFormControl):
-
- def __init__(self, obj, view, form):
- super().__init__(obj, view, form)
-
- @property
- def type(self):
- return 'label'
-
- @property
- def value(self):
- return self._m.Label
- @value.setter
- def value(self, value):
- self._m.Label = value
-
-
-class LOFormControlText(LOFormControl):
-
- def __init__(self, obj, view, form):
- super().__init__(obj, view, form)
-
- @property
- def type(self):
- return 'text'
-
- @property
- def value(self):
- return self._m.Text
- @value.setter
- def value(self, value):
- self._m.Text = value
-
-
-class LOFormControlButton(LOFormControl):
-
- def __init__(self, obj, view, form):
- super().__init__(obj, view, form)
-
- @property
- def type(self):
- return 'button'
-
- @property
- def value(self):
- return self._m.Label
- @value.setter
- def value(self, value):
- self._m.Text = Label
-
- @property
- def url(self):
- return self._m.TargetURL
- @url.setter
- def url(self, value):
- self._m.TargetURL = value
- self._m.ButtonType = FormButtonType.URL
-
-
-FORM_CONTROL_CLASS = {
- 'label': LOFormControlLabel,
- 'text': LOFormControlText,
- 'button': LOFormControlButton,
-}
-
-
-class LOForm(object):
- MODELS = {
- 'label': 'com.sun.star.form.component.FixedText',
- 'text': 'com.sun.star.form.component.TextField',
- 'button': 'com.sun.star.form.component.CommandButton',
- }
-
- def __init__(self, obj, draw_page):
- self._obj = obj
- self._dp = draw_page
- self._controls = {}
- self._init_controls()
-
- def __getitem__(self, index):
- control = self.obj[index]
- return self._controls[control.Name]
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- pass
-
- def __contains__(self, item):
- return item in self.obj
-
- def __len__(self):
- return len(self.obj)
-
- def __str__(self):
- return f'Form: {self.name}'
-
- def _init_controls(self):
- types = {
- 'com.sun.star.form.OFixedTextModel': 'label',
- 'com.sun.star.form.OEditModel': 'text',
- 'com.sun.star.form.OButtonModel': 'button',
- }
- for i, control in enumerate(self.obj):
- name = control.Name
- tipo = types[control.ImplementationName]
- view = self.doc.CurrentController.getControl(control)
- control = FORM_CONTROL_CLASS[tipo](control, view, self._obj)
- control.index = i
- setattr(self, name, control)
- self._controls[name] = control
- return
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def name(self):
- return self.obj.Name
- @name.setter
- def name(self, value):
- self.obj.Name = value
-
- @property
- def source(self):
- return self.obj.DataSourceName
- @source.setter
- def source(self, value):
- self.obj.DataSourceName = value
-
- @property
- def type(self):
- return self.obj.CommandType
- @type.setter
- def type(self, value):
- self.obj.CommandType = value
-
- @property
- def command(self):
- return self.obj.Command
- @command.setter
- def command(self, value):
- self.obj.Command = value
-
- @property
- def doc(self):
- return self.obj.Parent.Parent
-
- def _special_properties(self, tipo, args):
- if tipo == 'button':
- # ~ if 'ImageURL' in args:
- # ~ args['ImageURL'] = self._set_image_url(args['ImageURL'])
- args['FocusOnClick'] = args.get('FocusOnClick', False)
- return args
- return args
-
- def add(self, args):
- name = args['Name']
- tipo = args.pop('Type').lower()
- w = args.pop('Width', 1000)
- h = args.pop('Height', 200)
- x = args.pop('X', 0)
- y = args.pop('Y', 0)
- control = self.doc.createInstance('com.sun.star.drawing.ControlShape')
- control.setSize(Size(w, h))
- control.setPosition(Point(x, y))
- model = self.doc.createInstance(self.MODELS[tipo])
- args = self._special_properties(tipo, args)
- _set_properties(model, args)
- control.Control = model
- index = len(self)
- self.obj.insertByIndex(index, model)
- self._dp.add(control)
- view = self.doc.CurrentController.getControl(self.obj.getByName(name))
- control = FORM_CONTROL_CLASS[tipo](control, view, self.obj)
- control.index = index
- setattr(self, name, control)
- self._controls[name] = control
- return control
-
-
-class LOSheetForms(object):
-
- def __init__(self, draw_page):
- self._dp = draw_page
- self._obj = draw_page.Forms
-
- def __getitem__(self, index):
- return LOForm(self.obj[index], self._dp)
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- pass
-
- def __contains__(self, item):
- return item in self.obj
-
- def __len__(self):
- return len(self.obj)
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def doc(self):
- return self.obj.Parent
-
- @property
- def count(self):
- return len(self)
-
- @property
- def names(self):
- return self.obj.ElementNames
-
- def insert(self, name=''):
- if not name:
- name = f'form{self.count + 1}'
- form = self.doc.createInstance('com.sun.star.form.component.Form')
- self.obj.insertByName(name, form)
- return LOForm(form, self._dp)
-
- def remove(self, index):
- if isinstance(index, int):
- self.obj.removeByIndex(index)
- else:
- self.obj.removeByName(index)
- return
-
-
-# ~ IsFiltered,
-# ~ IsManualPageBreak,
-# ~ IsStartOfNewPage
-class LOSheetRows(object):
-
- def __init__(self, sheet, obj):
- self._sheet = sheet
- self._obj = obj
-
- def __getitem__(self, index):
- if isinstance(index, int):
- rows = LOSheetRows(self._sheet, self.obj[index])
- else:
- rango = self._sheet[index.start:index.stop,0:]
- rows = LOSheetRows(self._sheet, rango.obj.Rows)
- return rows
-
- def __len__(self):
- return self.obj.Count
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def visible(self):
- return self._obj.IsVisible
- @visible.setter
- def visible(self, value):
- self._obj.IsVisible = value
-
- @property
- def color(self):
- return self.obj.CellBackColor
- @color.setter
- def color(self, value):
- self.obj.CellBackColor = value
-
- @property
- def is_transparent(self):
- return self.obj.IsCellBackgroundTransparent
- @is_transparent.setter
- def is_transparent(self, value):
- self.obj.IsCellBackgroundTransparent = value
-
- @property
- def height(self):
- return self.obj.Height
- @height.setter
- def height(self, value):
- self.obj.Height = value
-
- def optimal(self):
- self.obj.OptimalHeight = True
- return
-
- def insert(self, index, count):
- self.obj.insertByIndex(index, count)
- return
-
- def remove(self, index, count):
- self.obj.removeByIndex(index, count)
- return
-
-
-# ~ IsManualPageBreak,
-# ~ IsStartOfNewPage
-class LOSheetColumns(object):
-
- def __init__(self, sheet, obj):
- self._sheet = sheet
- self._obj = obj
-
- def __getitem__(self, index):
- if isinstance(index, (int, str)):
- rows = LOSheetColumns(self._sheet, self.obj[index])
- else:
- rango = self._sheet[0,index.start:index.stop]
- rows = LOSheetColumns(self._sheet, rango.obj.Columns)
- return rows
-
- def __len__(self):
- return self.obj.Count
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def visible(self):
- return self._obj.IsVisible
- @visible.setter
- def visible(self, value):
- self._obj.IsVisible = value
-
- @property
- def width(self):
- return self.obj.Width
- @width.setter
- def width(self, value):
- self.obj.Width = value
-
- def optimal(self):
- self.obj.OptimalWidth = True
- return
-
- def insert(self, index, count):
- self.obj.insertByIndex(index, count)
- return
-
- def remove(self, index, count):
- self.obj.removeByIndex(index, count)
- return
-
-
-class LOCalcSheet(object):
-
- @property
- def draw_page(self):
- return LODrawPage(self.obj.DrawPage)
- @property
- def dp(self):
- return self.draw_page
-
- @property
- def shapes(self):
- return self.draw_page
-
- @property
- def charts(self):
- return LOSheetCharts(self.obj.Charts, self)
-
- @property
- def tables(self):
- return LOSheetTables(self.obj.DataPilotTables, self)
-
- @property
- def rows(self):
- return LOSheetRows(self, self.obj.Rows)
-
- @property
- def columns(self):
- return LOSheetColumns(self, self.obj.Columns)
-
- @property
- def forms(self):
- return LOSheetForms(self.obj.DrawPage)
-
- @property
- def search_descriptor(self):
- return self.obj.createSearchDescriptor()
-
- @property
- def replace_descriptor(self):
- return self.obj.createReplaceDescriptor()
-
- def render(self, data, rango=None, clean=True):
- if rango is None:
- rango = self.used_area
- return rango.render(data, clean)
-
- def find(self, search_string, rango=None):
- if rango is None:
- rango = self.used_area
- return rango.find(search_string)
-
-
-class LOCalcRange(object):
-
- def __contains__(self, item):
- return item.in_range(self)
-
- @property
- def back_color(self):
- return self._obj.CellBackColor
- @back_color.setter
- def back_color(self, value):
- self._obj.CellBackColor = get_color(value)
-
- @property
- def columns(self):
- return self.obj.Columns.Count
-
- @property
- def column(self):
- c1 = self.address.Column
- c2 = c1 + 1
- ra = self.current_region.range_address
- r1 = ra.StartRow
- r2 = ra.EndRow + 1
- return LOCalcRange(self.sheet[r1:r2, c1:c2].obj)
-
- @property
- def rows(self):
- return LOSheetRows(self.sheet, self.obj.Rows)
-
- @property
- def row(self):
- r1 = self.address.Row
- r2 = r1 + 1
- ra = self.current_region.range_address
- c1 = ra.StartColumn
- c2 = ra.EndColumn + 1
- return LOCalcRange(self.sheet[r1:r2, c1:c2].obj)
-
- @property
- def type(self):
- return self.obj.Type
-
- @property
- def error(self):
- return self.obj.getError()
-
- # ~ https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1sheet_1_1CellFlags.html
- @property
- def value(self):
- v = None
- if self.type == VALUE:
- v = self.obj.getValue()
- elif self.type == TEXT:
- v = self.obj.getString()
- elif self.type == FORMULA:
- v = self.obj.getFormula()
- return v
- @value.setter
- def value(self, data):
- if isinstance(data, str):
- if data[0] in '=':
- self.obj.setFormula(data)
- else:
- self.obj.setString(data)
- elif isinstance(data, Decimal):
- self.obj.setValue(float(data))
- elif isinstance(data, (int, float, bool)):
- self.obj.setValue(data)
- elif isinstance(data, datetime.datetime):
- d = data.toordinal()
- t = (data - datetime.datetime.fromordinal(d)).seconds / SECONDS_DAY
- self.obj.setValue(d - DATE_OFFSET + t)
- elif isinstance(data, datetime.date):
- d = data.toordinal()
- self.obj.setValue(d - DATE_OFFSET)
- elif isinstance(data, datetime.time):
- d = (data.hour * 3600 + data.minute * 60 + data.second) / SECONDS_DAY
- self.obj.setValue(d)
-
- @property
- def date(self):
- value = int(self.obj.Value)
- date = datetime.date.fromordinal(value + DATE_OFFSET)
- return date
-
- @property
- def time(self):
- seconds = self.obj.Value * SECONDS_DAY
- time_delta = datetime.timedelta(seconds=seconds)
- time = (datetime.datetime.min + time_delta).time()
- return time
-
- @property
- def datetime(self):
- return datetime.datetime.combine(self.date, self.time)
-
- @property
- def dict(self):
- rows = self.data
- k = rows[0]
- data = [dict(zip(k, r)) for r in rows[1:]]
- return data
- @dict.setter
- def dict(self, values):
- data = [tuple(values[0].keys())]
- data += [tuple(d.values()) for d in values]
- self.data = data
-
- @property
- def formula(self):
- return self.obj.getFormulaArray()
- @formula.setter
- def formula(self, values):
- self.obj.setFormulaArray(values)
-
- @property
- def array_formula(self):
- return self.obj.ArrayFormula
- @array_formula.setter
- def array_formula(self, value):
- self.obj.ArrayFormula = value
-
- @property
- def next_cell(self):
- a = self.current_region.range_address
- col = a.StartColumn
- row = a.EndRow + 1
- return LOCalcRange(self.sheet[row, col].obj)
-
- @property
- def position(self):
- return self.obj.Position
-
- @property
- def size(self):
- return self.obj.Size
-
- @property
- def possize(self):
- data = {
- 'Width': self.size.Width,
- 'Height': self.size.Height,
- 'X': self.position.X,
- 'Y': self.position.Y,
- }
- return data
-
- @property
- def visible(self):
- cursor = self.cursor
- rangos = cursor.queryVisibleCells()
- rangos = LOCalcRanges(rangos)
- return rangos
-
- @property
- def merged_area(self):
- cursor = self.cursor
- cursor.collapseToMergedArea()
- rango = LOCalcRange(self.sheet[cursor.AbsoluteName].obj)
- return rango
-
- @property
- def empty(self):
- cursor = self.cursor
- rangos = cursor.queryEmptyCells()
- rangos = [LOCalcRange(self.sheet[r.AbsoluteName].obj) for r in rangos]
- return tuple(rangos)
-
- def query_content(self, type_content=1023):
- cursor = self.cursor
- rangos = cursor.queryContentCells(type_content)
- rangos = [LOCalcRange(self.sheet[r.AbsoluteName].obj) for r in rangos]
- return tuple(rangos)
-
- @property
- def merge(self):
- return self.obj.IsMerged
- @merge.setter
- def merge(self, value):
- self.obj.merge(value)
-
- @property
- def auto_format(self):
- return ''
- @auto_format.setter
- def auto_format(self, value):
- self.obj.autoFormat(value)
-
- @property
- def validation(self):
- return self.obj.Validation
- @validation.setter
- def validation(self, values):
- current = self.validation
- if not values:
- current.Type = ValidationType.ANY
- current.ShowInputMessage = False
- else:
- is_list = False
- for k, v in values.items():
- if k == 'Type' and v == VT.LIST:
- is_list = True
- if k == 'Formula1' and is_list:
- if isinstance(v, (tuple, list)):
- v = ';'.join(['"{}"'.format(i) for i in v])
- setattr(current, k, v)
- self.obj.Validation = current
-
- def select(self):
- self.doc._cc.select(self.obj)
- return
-
- def search(self, options, find_all=True):
- rangos = None
-
- descriptor = self.sheet.search_descriptor
- descriptor.setSearchString(options['Search'])
- descriptor.SearchCaseSensitive = options.get('CaseSensitive', False)
- descriptor.SearchWords = options.get('Words', False)
- if hasattr(descriptor, 'SearchRegularExpression'):
- descriptor.SearchRegularExpression = options.get('RegularExpression', False)
- if hasattr(descriptor, 'SearchType') and 'Type' in options:
- descriptor.SearchType = options['Type']
-
- if find_all:
- found = self.obj.findAll(descriptor)
- else:
- found = self.obj.findFirst(descriptor)
-
- if found:
- if found.ImplementationName == OBJ_CELL:
- rangos = LOCalcRange(found)
- else:
- rangos = [LOCalcRange(f) for f in found]
-
- return rangos
-
- def replace(self, options):
- descriptor = self.sheet.replace_descriptor
- descriptor.setSearchString(options['Search'])
- descriptor.setReplaceString(options['Replace'])
- descriptor.SearchCaseSensitive = options.get('CaseSensitive', False)
- descriptor.SearchWords = options.get('Words', False)
- if hasattr(descriptor, 'SearchRegularExpression'):
- descriptor.SearchRegularExpression = options.get('RegularExpression', False)
- if hasattr(descriptor, 'SearchType') and 'Type' in options:
- descriptor.SearchType = options['Type']
- count = self.obj.replaceAll(descriptor)
- return count
-
- def in_range(self, rango):
- if isinstance(rango, LOCalcRange):
- address = rango.range_address
- else:
- address = rango.RangeAddress
- result = self.cursor.queryIntersection(address)
- return bool(result.Count)
-
- def move(self, target):
- sheet = self.sheet.obj
- sheet.moveRange(target.address, self.range_address)
- return
-
- def insert(self, insert_mode=CIM.DOWN):
- sheet = self.sheet.obj
- sheet.insertCells(self.range_address, insert_mode)
- return
-
- def delete(self, delete_mode=CDM.UP):
- sheet = self.sheet.obj
- sheet.removeRange(self.range_address, delete_mode)
- return
-
- def copy_from(self, source):
- self.sheet.obj.copyRange(self.address, source.range_address)
- return
-
- def copy_to(self, target):
- self.sheet.obj.copyRange(target.address, self.range_address)
- return
-
- # ~ def copy_to(self, cell, formula=False):
- # ~ rango = cell.to_size(self.rows, self.columns)
- # ~ if formula:
- # ~ rango.formula = self.formula
- # ~ else:
- # ~ rango.data = self.data
- # ~ return
-
- # ~ def copy_from(self, rango, formula=False):
- # ~ data = rango
- # ~ if isinstance(rango, LOCalcRange):
- # ~ if formula:
- # ~ data = rango.formula
- # ~ else:
- # ~ data = rango.data
- # ~ rows = len(data)
- # ~ cols = len(data[0])
- # ~ if formula:
- # ~ self.to_size(rows, cols).formula = data
- # ~ else:
- # ~ self.to_size(rows, cols).data = data
- # ~ return
-
- def optimal_width(self):
- self.obj.Columns.OptimalWidth = True
- return
-
- def clean_render(self, template='\{(\w.+)\}'):
- self._sd.SearchRegularExpression = True
- self._sd.setSearchString(template)
- self.obj.replaceAll(self._sd)
- return
-
- def render(self, data, clean=True):
- self._sd = self.sheet.obj.createSearchDescriptor()
- self._sd.SearchCaseSensitive = False
- for k, v in data.items():
- cell = self._render_value(k, v)
- return cell
-
- def _render_value(self, key, value, parent=''):
- cell = None
- if isinstance(value, dict):
- for k, v in value.items():
- # ~ print(1, 'RENDER', k, v)
- cell = self._render_value(k, v, key)
- return cell
- elif isinstance(value, (list, tuple)):
- self._render_list(key, value)
- return
-
- search = f'{{{key}}}'
- if parent:
- search = f'{{{parent}.{key}}}'
- ranges = self.find_all(search)
-
- if ranges is None:
- return
-
- # ~ for cell in ranges or range(0):
- for cell in ranges:
- self._set_new_value(cell, search, value)
- return LOCalcRange(cell)
-
- def _set_new_value(self, cell, search, value):
- if not cell.ImplementationName == 'ScCellObj':
- return
-
- if isinstance(value, str):
- pattern = re.compile(search, re.IGNORECASE)
- new_value = pattern.sub(value, cell.String)
- cell.String = new_value
- else:
- LOCalcRange(cell).value = value
- return
-
- def _render_list(self, key, rows):
- for row in rows:
- for k, v in row.items():
- self._render_value(k, v)
- return
-
- def find(self, search_string):
- if self._sd is None:
- self._sd = self.sheet.obj.createSearchDescriptor()
- self._sd.SearchCaseSensitive = False
-
- self._sd.setSearchString(search_string)
- cell = self.obj.findFirst(self._sd)
- if cell:
- cell = LOCalcRange(cell)
- return cell
-
- def find_all(self, search_string):
- if self._sd is None:
- self._sd = self.sheet.obj.createSearchDescriptor()
- self._sd.SearchCaseSensitive = False
-
- self._sd.setSearchString(search_string)
- ranges = self.obj.findAll(self._sd)
- return ranges
-
- def filter(self, args, with_headers=True):
- ff = TableFilterField()
- ff.Field = args['Field']
- ff.Operator = args['Operator']
- if isinstance(args['Value'], str):
- ff.IsNumeric = False
- ff.StringValue = args['Value']
- else:
- ff.IsNumeric = True
- ff.NumericValue = args['Value']
-
- fd = self.obj.createFilterDescriptor(True)
- fd.ContainsHeader = with_headers
- fd.FilterFields = ((ff,))
- # ~ self.obj.AutoFilter = True
- self.obj.filter(fd)
- return
-
- def copy_format_from(self, rango):
- rango.select()
- self.doc.copy()
- self.select()
- args = {
- 'Flags': 'T',
- 'MoveMode': 4,
- }
- url = '.uno:InsertContents'
- call_dispatch(self.doc.frame, url, args)
- return
-
- def to_image(self):
- self.select()
- self.doc.copy()
- args = {'SelectedFormat': 141}
- url = '.uno:ClipboardFormatItems'
- call_dispatch(self.doc.frame, url, args)
- return self.sheet.shapes[-1]
-
- def insert_image(self, path, options={}):
- args = options.copy()
- ps = self.possize
- args['Width'] = args.get('Width', ps['Width'])
- args['Height'] = args.get('Height', ps['Height'])
- args['X'] = args.get('X', ps['X'])
- args['Y'] = args.get('Y', ps['Y'])
- # ~ img.ResizeWithCell = True
- img = self.sheet.dp.insert_image(path, args)
- img.anchor = self.obj
- args.clear()
- return img
-
- def insert_shape(self, tipo, args={}):
- ps = self.possize
- args['Width'] = args.get('Width', ps['Width'])
- args['Height'] = args.get('Height', ps['Height'])
- args['X'] = args.get('X', ps['X'])
- args['Y'] = args.get('Y', ps['Y'])
-
- shape = self.sheet.dp.add(tipo, args)
- shape.anchor = self.obj
- args.clear()
- return
-
- def filter_by_color(self, cell):
- rangos = cell.column[1:,:].visible
- for r in rangos:
- for c in r:
- if c.back_color != cell.back_color:
- c.rows.visible = False
- return
-
- def clear(self, what=1023):
- # ~ http://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1sheet_1_1CellFlags.html
- self.obj.clearContents(what)
- return
-
- def transpose(self):
- # ~ 'Flags': 'A',
- # ~ 'FormulaCommand': 0,
- # ~ 'SkipEmptyCells': False,
- # ~ 'AsLink': False,
- # ~ 'MoveMode': 4,
- self.select()
- self.doc.copy()
- self.clear(1023)
- self[0,0].select()
- self.doc.insert_contents({'Transpose': True})
- _CB.set('')
- return
-
- def transpose_data(self, formula=False):
- data = self.data
- if formula:
- data = self.formula
- data = tuple(zip(*data))
- self.clear(1023)
- self[0,0].copy_from(data, formula=formula)
- return
-
- def merge_by_row(self):
- for r in range(len(self.rows)):
- self[r].merge = True
- return
-
- def fill(self, source=1):
- self.obj.fillAuto(0, source)
- return
-
- def _cast(self, t, v):
- if not t:
- return v
-
- if t == datetime.date:
- nv = datetime.date.fromordinal(int(v) + DATE_OFFSET)
- else:
- nv = t(v)
- return nv
-
- def get_data(self, types):
- values = [
- [self._cast(types[i], v) for i, v in enumerate(row)]
- for row in self.data
- ]
- return values
-
-
-class LOWriterStyles(object):
-
- def __init__(self, styles):
- self._styles = styles
-
- @property
- def names(self):
- return {s.DisplayName: s.Name for s in self._styles}
-
- def __str__(self):
- return '\n'.join(tuple(self.names.values()))
-
-
-class LOWriterStylesFamilies(object):
-
- def __init__(self, styles):
- self._styles = styles
-
- def __getitem__(self, index):
- styles = {
- 'Character': 'CharacterStyles',
- 'Paragraph': 'ParagraphStyles',
- 'Page': 'PageStyles',
- 'Frame': 'FrameStyles',
- 'Numbering': 'NumberingStyles',
- 'Table': 'TableStyles',
- 'Cell': 'CellStyles',
- }
- name = styles.get(index, index)
- return LOWriterStyles(self._styles[name])
-
- def __iter__(self):
- self._index = 0
- return self
-
- def __next__(self):
- obj = LOWriterStyles(self._styles[self._index])
- self._index += 1
- return obj
- # ~ raise StopIteration
-
- @property
- def names(self):
- return self._styles.ElementNames
-
- def __str__(self):
- return '\n'.join(self.names)
-
-
-class LOWriterPageStyle(LOBaseObject):
-
- def __init__(self, obj):
- super().__init__(obj)
-
- def __str__(self):
- return f'Page Style: {self.name}'
-
- @property
- def name(self):
- return self._obj.Name
-
-
-class LOWriterPageStyles(object):
-
- def __init__(self, styles):
- self._styles = styles
-
- def __getitem__(self, index):
- return LOWriterPageStyle(self._styles[index])
-
- @property
- def names(self):
- return self._styles.ElementNames
-
- def __str__(self):
- return '\n'.join(self.names)
-
-
-class LOWriterTextRange(object):
-
- def __init__(self, obj, doc):
- self._obj = obj
- self._doc = doc
- self._is_paragraph = self.obj.ImplementationName == 'SwXParagraph'
- self._is_table = self.obj.ImplementationName == 'SwXTextTable'
- self._is_text = self.obj.ImplementationName == 'SwXTextPortion'
- self._is_section = not self.obj.TextSection is None
- self._parts = []
- if self._is_paragraph:
- self._parts = [LOWriterTextRange(p, doc) for p in obj]
-
- def __iter__(self):
- self._index = 0
- return self
-
- def __next__(self):
- try:
- obj = self._parts[self._index]
- except IndexError:
- raise StopIteration
-
- self._index += 1
- return obj
-
- @property
- def string(self):
- s = ''
- if not self._is_table:
- s = self.obj.String
- return s
- @string.setter
- def string(self, value):
- self.obj.String = value
-
- @property
- def value(self):
- return self.string
- @value.setter
- def value(self, value):
- self.string = value
-
- @property
- def style(self):
- s = ''
- if self.is_paragraph:
- s = self.obj.ParaStyleName
- elif self.is_text:
- s = self.obj.CharStyleName
- return s
- @style.setter
- def style(self, value):
- if self.is_paragraph:
- self.obj.ParaStyleName = value
- elif self.is_text:
- self.obj.CharStyleName = value
-
- @property
- def is_paragraph(self):
- return self._is_paragraph
-
- @property
- def is_table(self):
- return self._is_table
-
- @property
- def is_text(self):
- return self._is_text
-
- @property
- def is_section(self):
- return self._is_section
-
- @property
- def text_cursor(self):
- return self.text.createTextCursor()
-
- @property
- def dp(self):
- return self._doc.dp
-
- @property
- def paragraph(self):
- cursor = self.cursor
- cursor.gotoStartOfParagraph(False)
- cursor.gotoNextParagraph(True)
- return LOWriterTextRange(cursor, self._doc)
-
- def goto_start(self):
- if self.is_section:
- rango = self.obj.TextSection.Anchor.Start
- else:
- rango = self.obj.Start
- return LOWriterTextRange(rango, self._doc)
-
- def goto_end(self):
- if self.is_section:
- rango = self.obj.TextSection.Anchor.End
- else:
- rango = self.obj.End
- return LOWriterTextRange(rango, self._doc)
-
- def goto_previous(self, expand=True):
- cursor = self.cursor
- cursor.gotoPreviousParagraph(expand)
- return LOWriterTextRange(cursor, self._doc)
-
- def goto_next(self, expand=True):
- cursor = self.cursor
- cursor.gotoNextParagraph(expand)
- return LOWriterTextRange(cursor, self._doc)
-
- def go_left(self, from_self=True, count=1, expand=False):
- cursor = self.cursor
- if not from_self:
- cursor = self.text_cursor
- cursor.gotoRange(self.obj, False)
- cursor.goLeft(count, expand)
- return LOWriterTextRange(cursor, self._doc)
-
- def go_right(self, from_self=True, count=1, expand=False):
- cursor = self.cursor
- if not from_self:
- cursor = self.text_cursor
- cursor.gotoRange(self.obj, False)
- cursor.goRight(count, expand)
- return LOWriterTextRange(cursor, self._doc)
-
- def delete(self):
- self.value = ''
- return
-
- def offset(self):
- cursor = self.cursor.getEnd()
- return LOWriterTextRange(cursor, self._doc)
-
- def insert_content(self, data, cursor=None, replace=False):
- if cursor is None:
- cursor = self.cursor
- self.text.insertTextContent(cursor, data, replace)
- return
-
- def insert_math(self, formula,
- anchor_type=TextContentAnchorType.AS_CHARACTER,
- cursor=None, replace=False):
-
- math = self._doc.create_instance(SERVICES['TEXT_EMBEDDED'])
- math.CLSID = CLSID['FORMULA']
- math.AnchorType = anchor_type
- self.insert_content(math, cursor, replace)
- math.EmbeddedObject.Component.Formula = formula
- return math
-
- def new_line(self, count=1):
- cursor = self.cursor
- for i in range(count):
- self.text.insertControlCharacter(cursor, PARAGRAPH_BREAK, False)
- return LOWriterTextRange(cursor, self._doc)
-
- def insert_table(self, data):
- table = self._doc.create_instance(SERVICES['TEXT_TABLE'])
- rows = len(data)
- cols = len(data[0])
- table.initialize(rows, cols)
- self.insert_content(table)
- table.DataArray = data
- name = table.Name
- table = LOWriterTextTable(self._doc.tables[name], self._doc)
- return table
-
- def insert_shape(self, tipo, args={}):
- # ~ args['Width'] = args.get('Width', 1000)
- # ~ args['Height'] = args.get('Height', 1000)
- # ~ args['X'] = args.get('X', 0)
- # ~ args['Y'] = args.get('Y', 0)
- shape = self._doc.dp.add(tipo, args)
- # ~ shape.anchor = self.obj
- return shape
-
- def insert_image(self, path, args={}):
- w = args.get('Width', 1000)
- h = args.get('Height', 1000)
-
- image = self._doc.create_instance(SERVICES['GRAPHIC'])
- image.GraphicURL = _P.to_url(path)
- image.AnchorType = TextContentAnchorType.AS_CHARACTER
- image.Width = w
- image.Height = h
- self.insert_content(image)
- return self._doc.dp.last
-
-
-class LOWriterTextRanges(object):
-
- def __init__(self, obj, doc):
- self._obj = obj
- self._doc = doc
- self._paragraphs = [LOWriterTextRange(p, doc) for p in obj]
-
- def __len__(self):
- return len(self._paragraphs)
-
- def __getitem__(self, index):
- return self._paragraphs[index]
-
- def __iter__(self):
- self._index = 0
- return self
-
- def __next__(self):
- try:
- obj = self._paragraphs[self._index]
- except IndexError:
- raise StopIteration
-
- self._index += 1
- return obj
-
- @property
- def obj(self):
- return self._obj
-
-
-class LOWriterTextTable(object):
-
- def __init__(self, obj, doc):
- self._obj = obj
- self._doc = doc
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def name(self):
- return self._obj.Name
-
- @property
- def data(self):
- return self.obj.DataArray
- @data.setter
- def data(self, values):
- self.obj.DataArray = values
-
- @property
- def style(self):
- return self.obj.TableTemplateName
- @style.setter
- def style(self, value):
- self.obj.autoFormat(value)
-
-
-class LOWriterTextTables(object):
-
- def __init__(self, doc):
- self._doc = doc
- self._obj = doc.obj.TextTables
-
- def __getitem__(self, key):
- return LOWriterTextTable(self._obj[key], self._doc)
-
- def __len__(self):
- return self._obj.Count
-
- def insert(self, data, text_range=None):
- if text_range is None:
- text_range = self._doc.selection
- text_range.insert_table(data)
- return
-
-
-class LOWriter(LODocument):
-
- def __init__(self, obj):
- super().__init__(obj)
- self._type = WRITER
- self._settings = self._cc.ViewSettings
-
- @property
- def text(self):
- return self.paragraphs
-
- @property
- def paragraphs(self):
- return LOWriterTextRanges(self.obj.Text, self)
-
- @property
- def tables(self):
- return LOWriterTextTables(self)
-
- @property
- def selection(self):
- sel = self.obj.CurrentSelection
- if sel.ImplementationName == OBJ_TEXTS:
- if len(sel) == 1:
- sel = LOWriterTextRanges(sel, self)[0]
- else:
- sel = LOWriterTextRanges(sel, self)
- return sel
-
- if sel.ImplementationName == OBJ_SHAPES:
- if len(sel) == 1:
- sel = sel[0]
- sel = LODrawPage(sel.Parent)[sel.Name]
- return sel
-
- if sel.ImplementationName == OBJ_GRAPHIC:
- sel = self.dp[sel.Name]
- else:
- debug(sel.ImplementationName)
-
- return sel
-
- @property
- def dp(self):
- return self.draw_page
- @property
- def shapes(self):
- return self.draw_page
- @property
- def draw_page(self):
- return LODrawPage(self.obj.DrawPage)
-
- @property
- def cursor(self):
- return self.obj.Text.createTextCursor()
-
- @property
- def view_cursor(self):
- return self._cc.ViewCursor
-
- @property
- def page_styles(self):
- ps = self.obj.StyleFamilies['PageStyles']
- return LOWriterPageStyles(ps)
-
- @property
- def styles(self):
- return LOWriterStylesFamilies(self.obj.StyleFamilies)
-
- @property
- def search_descriptor(self):
- return self.obj.createSearchDescriptor()
-
- @property
- def replace_descriptor(self):
- return self.obj.createReplaceDescriptor()
-
- @property
- def view_web(self):
- return self._settings.ShowOnlineLayout
- @view_web.setter
- def view_web(self, value):
- self._settings.ShowOnlineLayout = value
-
- def goto_start(self):
- self.view_cursor.gotoStart(False)
- return self.selection
-
- def goto_end(self):
- self.view_cursor.gotoEnd(False)
- return self.selection
-
- def search(self, options, find_all=True):
- descriptor = self.search_descriptor
- descriptor.setSearchString(options.get('Search', ''))
- descriptor.SearchCaseSensitive = options.get('CaseSensitive', False)
- descriptor.SearchWords = options.get('Words', False)
- if 'Attributes' in options:
- attr = dict_to_property(options['Attributes'])
- descriptor.setSearchAttributes(attr)
- if hasattr(descriptor, 'SearchRegularExpression'):
- descriptor.SearchRegularExpression = options.get('RegularExpression', False)
- if hasattr(descriptor, 'SearchType') and 'Type' in options:
- descriptor.SearchType = options['Type']
-
- result = False
- if find_all:
- found = self.obj.findAll(descriptor)
- if len(found):
- result = [LOWriterTextRange(f, self) for f in found]
- else:
- found = self.obj.findFirst(descriptor)
- if found:
- result = LOWriterTextRange(found, self)
-
- return result
-
- def replace(self, options):
- descriptor = self.replace_descriptor
- descriptor.setSearchString(options['Search'])
- descriptor.setReplaceString(options['Replace'])
- descriptor.SearchCaseSensitive = options.get('CaseSensitive', False)
- descriptor.SearchWords = options.get('Words', False)
- if 'Attributes' in options:
- attr = dict_to_property(options['Attributes'])
- descriptor.setSearchAttributes(attr)
- if hasattr(descriptor, 'SearchRegularExpression'):
- descriptor.SearchRegularExpression = options.get('RegularExpression', False)
- if hasattr(descriptor, 'SearchType') and 'Type' in options:
- descriptor.SearchType = options['Type']
- found = self.obj.replaceAll(descriptor)
- return found
-
- def select(self, text):
- if hasattr(text, 'obj'):
- text = text.obj
- self._cc.select(text)
- return
-
-
-class LOShape(LOBaseObject):
-
- @property
- def cell(self):
- return self.anchor
-
- @property
- def anchor(self):
- obj = self.obj.Anchor
- if obj.ImplementationName == OBJ_CELL:
- obj = LOCalcRange(obj)
- elif obj.ImplementationName == OBJ_TEXT:
- obj = LOWriterTextRange(obj, LODocs().active)
- else:
- debug('Anchor', obj.ImplementationName)
- return obj
- @anchor.setter
- def anchor(self, value):
- if hasattr(value, 'obj'):
- value = value.obj
- try:
- self.obj.Anchor = value
- except Exception as e:
- self.obj.AnchorType = value
-
- @property
- def visible(self):
- return self.obj.Visible
- @visible.setter
- def visible(self, value):
- self.obj.Visible = value
-
- @property
- def path(self):
- return self.url
- @property
- def url(self):
- url = ''
- if self.is_image:
- url = _P.to_system(self.obj.GraphicURL.OriginURL)
- return url
-
- @property
- def mimetype(self):
- mt = ''
- if self.is_image:
- mt = self.obj.GraphicURL.MimeType
- return mt
-
- @property
- def linked(self):
- l = False
- if self.is_image:
- l = self.obj.GraphicURL.Linked
- return l
-
- def delete(self):
- self.remove()
- return
- def remove(self):
- self.obj.Parent.remove(self.obj)
- return
-
- def save(self, path: str, mimetype=DEFAULT_MIME_TYPE):
- if _P.is_dir(path):
- name = self.name
- ext = mimetype.lower()
- else:
- p = _P(path)
- path = p.path
- name = p.name
- ext = p.ext.lower()
-
- path = _P.join(path, f'{name}.{ext}')
- args = dict(
- URL = _P.to_url(path),
- MimeType = MIME_TYPE[ext],
- )
- if not _export_image(self.obj, args):
- path = ''
- return path
-
- # ~ def save2(self, path: str):
- # ~ size = len(self.obj.Bitmap.DIB)
- # ~ data = self.obj.GraphicStream.readBytes((), size)
- # ~ data = data[-1].value
- # ~ path = _P.join(path, f'{self.name}.png')
- # ~ _P.save_bin(path, b'')
- # ~ return
-
-
-class LODrawPage(LOBaseObject):
-
- def __init__(self, obj):
- super().__init__(obj)
-
- def __getitem__(self, index):
- if isinstance(index, int):
- shape = LOShape(self.obj[index], index)
- else:
- for i, o in enumerate(self.obj):
- shape = self.obj[i]
- name = shape.Name or f'shape{i}'
- if name == index:
- shape = LOShape(shape, i)
- break
- return shape
-
- def __iter__(self):
- self._index = 0
- return self
-
- def __next__(self):
- if self._index == self.count:
- raise StopIteration
- shape = self[self._index]
- self._index += 1
- return shape
-
-
- @property
- def name(self):
- return self.obj.Name
-
- @property
- def doc(self):
- return self.obj.Forms.Parent
-
- @property
- def width(self):
- return self.obj.Width
-
- @property
- def height(self):
- return self.obj.Height
-
- @property
- def count(self):
- return self.obj.Count
-
- @property
- def last(self):
- return self[self.count - 1]
-
- def create_instance(self, name):
- return self.doc.createInstance(name)
-
- def add(self, type_shape, options={}):
- args = options.copy()
- """Insert a shape in page, type shapes:
- Line
- Rectangle
- Ellipse
- Text
- Connector
- """
- index = self.count
- default_height = 3000
- if type_shape == 'Line':
- default_height = 0
- w = args.pop('Width', 3000)
- h = args.pop('Height', default_height)
- x = args.pop('X', 1000)
- y = args.pop('Y', 1000)
- name = args.pop('Name', f'{type_shape.lower()}{index}')
-
- service = f'com.sun.star.drawing.{type_shape}Shape'
- shape = self.create_instance(service)
- shape.Size = Size(w, h)
- shape.Position = Point(x, y)
- shape.Name = name
- self.obj.add(shape)
-
- if args:
- _set_properties(shape, args)
-
- return LOShape(self.obj[index], index)
-
- def remove(self, shape):
- if hasattr(shape, 'obj'):
- shape = shape.obj
- return self.obj.remove(shape)
-
- def remove_all(self):
- while self.count:
- self.obj.remove(self.obj[0])
- return
-
- def insert_image(self, path, options={}):
- args = options.copy()
- index = self.count
- w = args.get('Width', 3000)
- h = args.get('Height', 3000)
- x = args.get('X', 1000)
- y = args.get('Y', 1000)
- name = args.get('Name', f'image{index}')
-
- image = self.create_instance('com.sun.star.drawing.GraphicObjectShape')
- if isinstance(path, str):
- image.GraphicURL = _P.to_url(path)
- else:
- gp = create_instance('com.sun.star.graphic.GraphicProvider')
- properties = dict_to_property({'InputStream': path})
- image.Graphic = gp.queryGraphic(properties)
-
- self.obj.add(image)
- image.Size = Size(w, h)
- image.Position = Point(x, y)
- image.Name = name
- return LOShape(self.obj[index], index)
-
-
-class LODrawImpress(LODocument):
-
- def __init__(self, obj):
- super().__init__(obj)
-
- def __getitem__(self, index):
- if isinstance(index, int):
- page = self.obj.DrawPages[index]
- else:
- page = self.obj.DrawPages.getByName(index)
- return LODrawPage(page)
-
- @property
- def selection(self):
- sel = self.obj.CurrentSelection[0]
- # ~ return _get_class_uno(sel)
- return sel
-
- @property
- def current_page(self):
- return LODrawPage(self._cc.getCurrentPage())
-
- def paste(self):
- call_dispatch(self.frame, '.uno:Paste')
- return self.current_page[-1]
-
- def add(self, type_shape, args={}):
- return self.current_page.add(type_shape, args)
-
- def insert_image(self, path, args={}):
- self.current_page.insert_image(path, args)
- return
-
- # ~ def export(self, path, mimetype='png'):
- # ~ args = dict(
- # ~ URL = _P.to_url(path),
- # ~ MimeType = MIME_TYPE[mimetype],
- # ~ )
- # ~ result = _export_image(self.obj, args)
- # ~ return result
-
-
-class BaseRow:
- pass
-
-
-class BaseQuery(object):
- PY_TYPES = {
- 'VARCHAR': 'getString',
- 'INTEGER': 'getLong',
- 'DATE': 'getDate',
- # ~ 'SQL_LONG': 'getLong',
- # ~ 'SQL_VARYING': 'getString',
- # ~ 'SQL_FLOAT': 'getFloat',
- # ~ 'SQL_BOOLEAN': 'getBoolean',
- # ~ 'SQL_TYPE_DATE': 'getDate',
- # ~ 'SQL_TYPE_TIME': 'getTime',
- # ~ 'SQL_TIMESTAMP': 'getTimestamp',
- }
- # ~ TYPES_DATE = ('SQL_TYPE_DATE', 'SQL_TYPE_TIME', 'SQL_TIMESTAMP')
- TYPES_DATE = ('DATE', 'SQL_TYPE_TIME', 'SQL_TIMESTAMP')
-
- def __init__(self, query):
- self._query = query
- self._meta = query.MetaData
- self._cols = self._meta.ColumnCount
- self._names = query.Columns.ElementNames
- self._data = self._get_data()
-
- def __getitem__(self, index):
- return self._data[index]
-
- def __iter__(self):
- self._index = 0
- return self
-
- def __next__(self):
- try:
- row = self._data[self._index]
- except IndexError:
- raise StopIteration
- self._index += 1
- return row
-
- def _to_python(self, index):
- type_field = self._meta.getColumnTypeName(index)
- # ~ print('TF', type_field)
- value = getattr(self._query, self.PY_TYPES[type_field])(index)
- if type_field in self.TYPES_DATE:
- value = _struct_to_date(value)
- return value
-
- def _get_row(self):
- row = BaseRow()
- for i in range(1, self._cols + 1):
- column_name = self._meta.getColumnName(i)
- value = self._to_python(i)
- setattr(row, column_name, value)
- return row
-
- def _get_data(self):
- data = []
- while self._query.next():
- row = self._get_row()
- data.append(row)
- return data
-
- @property
- def tuples(self):
- data = [tuple(r.__dict__.values()) for r in self._data]
- return tuple(data)
-
- @property
- def dicts(self):
- data = [r.__dict__ for r in self._data]
- return tuple(data)
-
-
-def _add_listeners(events, control, name=''):
- listeners = {
- 'addActionListener': EventsButton,
- 'addMouseListener': EventsMouse,
- 'addFocusListener': EventsFocus,
- 'addItemListener': EventsItem,
- 'addKeyListener': EventsKey,
- 'addTabListener': EventsTab,
- 'addSpinListener': EventsSpin,
- }
- if hasattr(control, 'obj'):
- control = control.obj
- # ~ debug(control.ImplementationName)
- is_grid = control.ImplementationName == 'stardiv.Toolkit.GridControl'
- is_link = control.ImplementationName == 'stardiv.Toolkit.UnoFixedHyperlinkControl'
- is_roadmap = control.ImplementationName == 'stardiv.Toolkit.UnoRoadmapControl'
- is_pages = control.ImplementationName == 'stardiv.Toolkit.UnoMultiPageControl'
-
- for key, value in listeners.items():
- if hasattr(control, key):
- if is_grid and key == 'addMouseListener':
- control.addMouseListener(EventsMouseGrid(events, name))
- continue
- if is_link and key == 'addMouseListener':
- control.addMouseListener(EventsMouseLink(events, name))
- continue
- if is_roadmap and key == 'addItemListener':
- control.addItemListener(EventsItemRoadmap(events, name))
- continue
-
- getattr(control, key)(listeners[key](events, name))
-
- if is_grid:
- controllers = EventsGrid(events, name)
- control.addSelectionListener(controllers)
- control.Model.GridDataModel.addGridDataListener(controllers)
- return
-
-
-class EventsSpin(EventsListenerBase, XSpinListener):
-
- def __init__(self, controller, name):
- super().__init__(controller, name)
-
- def up(self, event):
- event_name = f'{self.name}_up'
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(event)
- return
-
- def down(self, event):
- event_name = f'{self.name}_up'
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(event)
- return
-
- def first(self, event):
- event_name = f'{self.name}_first'
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(event)
- return
-
- def last(self, event):
- event_name = f'{self.name}_last'
- 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 = '{}_click'.format(self._name)
- if event.ClickCount == 2:
- event_name = '{}_double_click'.format(self._name)
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(event)
- return
-
- def mouseReleased(self, event):
- event_name = '{}_after_click'.format(self._name)
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(event)
- return
-
- def mouseEntered(self, event):
- pass
-
- def mouseExited(self, event):
- pass
-
- # ~ XMouseMotionListener
- def mouseMoved(self, event):
- pass
-
- def mouseDragged(self, event):
- pass
-
-
-class EventsMouseLink(EventsMouse):
-
- def __init__(self, controller, name):
- super().__init__(controller, name)
- self._text_color = 0
-
- def mouseEntered(self, event):
- model = event.Source.Model
- self._text_color = model.TextColor or 0
- model.TextColor = get_color('blue')
- return
-
- def mouseExited(self, event):
- model = event.Source.Model
- model.TextColor = self._text_color
- return
-
-
-class EventsButton(EventsListenerBase, XActionListener):
-
- def __init__(self, controller, name):
- super().__init__(controller, name)
-
- def actionPerformed(self, event):
- event_name = f'{self.name}_action'
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(event)
- return
-
-
-class EventsFocus(EventsListenerBase, XFocusListener):
- CONTROLS = (
- 'stardiv.Toolkit.UnoControlEditModel',
- )
-
- def __init__(self, controller, name):
- super().__init__(controller, name)
-
- def focusGained(self, event):
- service = event.Source.Model.ImplementationName
- # ~ print('Focus enter', service)
- if service in self.CONTROLS:
- obj = event.Source.Model
- obj.BackgroundColor = COLOR_ON_FOCUS
- return
-
- def focusLost(self, event):
- service = event.Source.Model.ImplementationName
- if service in self.CONTROLS:
- obj = event.Source.Model
- obj.BackgroundColor = -1
- return
-
-
-class EventsKey(EventsListenerBase, XKeyListener):
- """
- event.KeyChar
- event.KeyCode
- event.KeyFunc
- event.Modifiers
- """
-
- def __init__(self, controller, name):
- super().__init__(controller, name)
-
- def keyPressed(self, event):
- pass
-
- def keyReleased(self, event):
- event_name = '{}_key_released'.format(self._name)
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(event)
- # ~ else:
- # ~ if event.KeyFunc == QUIT and hasattr(self._cls, 'close'):
- # ~ self._cls.close()
- return
-
-
-class EventsItem(EventsListenerBase, XItemListener):
-
- def __init__(self, controller, name):
- super().__init__(controller, name)
-
- def disposing(self, event):
- pass
-
- def itemStateChanged(self, event):
- event_name = '{}_item_changed'.format(self.name)
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(event)
- return
-
-
-class EventsItemRoadmap(EventsItem):
-
- def itemStateChanged(self, event):
- dialog = event.Source.Context.Model
- dialog.Step = event.ItemId + 1
- return
-
-
-class EventsGrid(EventsListenerBase, XGridDataListener, XGridSelectionListener):
-
- def __init__(self, controller, name):
- super().__init__(controller, name)
-
- def dataChanged(self, event):
- event_name = '{}_data_changed'.format(self.name)
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(event)
- return
-
- def rowHeadingChanged(self, event):
- pass
-
- def rowsInserted(self, event):
- pass
-
- def rowsRemoved(self, evemt):
- pass
-
- def selectionChanged(self, event):
- event_name = '{}_selection_changed'.format(self.name)
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(event)
- return
-
-
-class EventsMouseGrid(EventsMouse):
- selected = False
-
- def mousePressed(self, event):
- super().mousePressed(event)
- # ~ obj = event.Source
- # ~ col = obj.getColumnAtPoint(event.X, event.Y)
- # ~ row = obj.getRowAtPoint(event.X, event.Y)
- # ~ print(col, row)
- # ~ if col == -1 and row == -1:
- # ~ if self.selected:
- # ~ obj.deselectAllRows()
- # ~ else:
- # ~ obj.selectAllRows()
- # ~ self.selected = not self.selected
- return
-
- def mouseReleased(self, event):
- # ~ obj = event.Source
- # ~ col = obj.getColumnAtPoint(event.X, event.Y)
- # ~ row = obj.getRowAtPoint(event.X, event.Y)
- # ~ if row == -1 and col > -1:
- # ~ gdm = obj.Model.GridDataModel
- # ~ for i in range(gdm.RowCount):
- # ~ gdm.updateRowHeading(i, i + 1)
- return
-
-
-class EventsTab(EventsListenerBase, XTabListener):
-
- def __init__(self, controller, name):
- super().__init__(controller, name)
-
- def activated(self, id):
- event_name = '{}_activated'.format(self.name)
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(id)
- return
-
-
-class EventsMenu(EventsListenerBase, XMenuListener):
-
- def __init__(self, controller):
- super().__init__(controller, '')
-
- def itemHighlighted(self, event):
- pass
-
- def itemSelected(self, event):
- name = event.Source.getCommand(event.MenuId)
- if name.startswith('menu'):
- event_name = '{}_selected'.format(name)
- else:
- event_name = 'menu_{}_selected'.format(name)
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(event)
- return
-
- def itemActivated(self, event):
- return
-
- def itemDeactivated(self, event):
- return
-
-
-class EventsWindow(EventsListenerBase, XTopWindowListener, XWindowListener):
-
- def __init__(self, cls):
- self._cls = cls
- super().__init__(cls.events, cls.name, cls._window)
-
- def windowOpened(self, event):
- event_name = '{}_opened'.format(self._name)
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(event)
- return
-
- def windowActivated(self, event):
- control_name = '{}_activated'.format(event.Source.Model.Name)
- if hasattr(self._controller, control_name):
- getattr(self._controller, control_name)(event)
- return
-
- def windowDeactivated(self, event):
- control_name = '{}_deactivated'.format(event.Source.Model.Name)
- if hasattr(self._controller, control_name):
- getattr(self._controller, control_name)(event)
- return
-
- def windowMinimized(self, event):
- pass
-
- def windowNormalized(self, event):
- pass
-
- def windowClosing(self, event):
- if self._window:
- control_name = 'window_closing'
- else:
- control_name = '{}_closing'.format(event.Source.Model.Name)
-
- if hasattr(self._controller, control_name):
- getattr(self._controller, control_name)(event)
- # ~ else:
- # ~ if not self._modal and not self._block:
- # ~ event.Source.Visible = False
- return
-
- def windowClosed(self, event):
- control_name = '{}_closed'.format(event.Source.Model.Name)
- if hasattr(self._controller, control_name):
- getattr(self._controller, control_name)(event)
- return
-
- # ~ XWindowListener
- def windowResized(self, event):
- sb = self._cls._subcont
- sb.setPosSize(0, 0, event.Width, event.Height, SIZE)
- event_name = '{}_resized'.format(self._name)
- if hasattr(self._controller, event_name):
- getattr(self._controller, event_name)(event)
- return
-
- def windowMoved(self, event):
- pass
-
- def windowShown(self, event):
- pass
-
- def windowHidden(self, event):
- pass
-
-
-# ~ BorderColor = ?
-# ~ FontStyleName = ?
-# ~ HelpURL = ?
-class UnoBaseObject(object):
-
- def __init__(self, obj, path=''):
- self._obj = obj
- self._model = obj.Model
-
- def __setattr__(self, name, value):
- exists = hasattr(self, name)
- if not exists and not name in ('_obj', '_model'):
- setattr(self._model, name, value)
- else:
- super().__setattr__(name, value)
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- pass
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def model(self):
- return self._model
- @property
- def m(self):
- return self._model
-
- @property
- def properties(self):
- return {}
- @properties.setter
- def properties(self, values):
- _set_properties(self.model, values)
-
- @property
- def name(self):
- return self.model.Name
-
- @property
- def parent(self):
- return self.obj.Context
-
- @property
- def tag(self):
- return self.model.Tag
- @tag.setter
- def tag(self, value):
- self.model.Tag = value
-
- @property
- def visible(self):
- return self.obj.Visible
- @visible.setter
- def visible(self, value):
- self.obj.setVisible(value)
-
- @property
- def enabled(self):
- return self.model.Enabled
- @enabled.setter
- def enabled(self, value):
- self.model.Enabled = value
-
- @property
- def step(self):
- return self.model.Step
- @step.setter
- def step(self, value):
- self.model.Step = value
-
- @property
- def align(self):
- return self.model.Align
- @align.setter
- def align(self, value):
- self.model.Align = value
-
- @property
- def valign(self):
- return self.model.VerticalAlign
- @valign.setter
- def valign(self, value):
- self.model.VerticalAlign = value
-
- @property
- def font_weight(self):
- return self.model.FontWeight
- @font_weight.setter
- def font_weight(self, value):
- self.model.FontWeight = value
-
- @property
- def font_height(self):
- return self.model.FontHeight
- @font_height.setter
- def font_height(self, value):
- self.model.FontHeight = value
-
- @property
- def font_name(self):
- return self.model.FontName
- @font_name.setter
- def font_name(self, value):
- self.model.FontName = value
-
- @property
- def font_underline(self):
- return self.model.FontUnderline
- @font_underline.setter
- def font_underline(self, value):
- self.model.FontUnderline = value
-
- @property
- def text_color(self):
- return self.model.TextColor
- @text_color.setter
- def text_color(self, value):
- self.model.TextColor = value
-
- @property
- def back_color(self):
- return self.model.BackgroundColor
- @back_color.setter
- def back_color(self, value):
- self.model.BackgroundColor = value
-
- @property
- def multi_line(self):
- return self.model.MultiLine
- @multi_line.setter
- def multi_line(self, value):
- self.model.MultiLine = value
-
- @property
- def help_text(self):
- return self.model.HelpText
- @help_text.setter
- def help_text(self, value):
- self.model.HelpText = value
-
- @property
- def border(self):
- return self.model.Border
- @border.setter
- def border(self, value):
- # ~ Bug for report
- self.model.Border = value
-
- @property
- def width(self):
- return self._model.Width
- @width.setter
- def width(self, value):
- self.model.Width = value
-
- @property
- def height(self):
- return self.model.Height
- @height.setter
- def height(self, value):
- self.model.Height = value
-
- def _get_possize(self, name):
- ps = self.obj.getPosSize()
- return getattr(ps, name)
-
- def _set_possize(self, name, value):
- ps = self.obj.getPosSize()
- setattr(ps, name, value)
- self.obj.setPosSize(ps.X, ps.Y, ps.Width, ps.Height, POSSIZE)
- return
-
- @property
- def x(self):
- if hasattr(self.model, 'PositionX'):
- return self.model.PositionX
- return self._get_possize('X')
- @x.setter
- def x(self, value):
- if hasattr(self.model, 'PositionX'):
- self.model.PositionX = value
- else:
- self._set_possize('X', value)
-
- @property
- def y(self):
- if hasattr(self.model, 'PositionY'):
- return self.model.PositionY
- return self._get_possize('Y')
- @y.setter
- def y(self, value):
- if hasattr(self.model, 'PositionY'):
- self.model.PositionY = value
- else:
- self._set_possize('Y', value)
-
- @property
- def tab_index(self):
- return self._model.TabIndex
- @tab_index.setter
- def tab_index(self, value):
- self.model.TabIndex = value
-
- @property
- def tab_stop(self):
- return self._model.Tabstop
- @tab_stop.setter
- def tab_stop(self, value):
- self.model.Tabstop = value
-
- @property
- def ps(self):
- ps = self.obj.getPosSize()
- return ps
- @ps.setter
- def ps(self, ps):
- self.obj.setPosSize(ps.X, ps.Y, ps.Width, ps.Height, POSSIZE)
-
- def set_focus(self):
- self.obj.setFocus()
- return
-
- def ps_from(self, source):
- self.ps = source.ps
- return
-
- def center(self, horizontal=True, vertical=False):
- p = self.parent.Model
- w = p.Width
- h = p.Height
- if horizontal:
- x = w / 2 - self.width / 2
- self.x = x
- if vertical:
- y = h / 2 - self.height / 2
- self.y = y
- return
-
- def move(self, origin, x=0, y=5, center=False):
- if x:
- self.x = origin.x + origin.width + x
- else:
- self.x = origin.x
- if y:
- h = origin.height
- if y < 0:
- h = 0
- self.y = origin.y + h + y
- else:
- self.y = origin.y
-
- if center:
- self.center()
- return
-
-
-class UnoLabel(UnoBaseObject):
-
- def __init__(self, obj):
- super().__init__(obj)
-
- @property
- def type(self):
- return 'label'
-
- @property
- def value(self):
- return self.model.Label
- @value.setter
- def value(self, value):
- self.model.Label = value
-
-
-class UnoLabelLink(UnoLabel):
-
- def __init__(self, obj):
- super().__init__(obj)
-
- @property
- def type(self):
- return 'link'
-
-
-class UnoButton(UnoBaseObject):
-
- def __init__(self, obj):
- super().__init__(obj)
-
- @property
- def type(self):
- return 'button'
-
- @property
- def value(self):
- return self.model.Label
- @value.setter
- def value(self, value):
- self.model.Label = value
-
- @property
- def image(self):
- return self.model.ImageURL
- @image.setter
- def image(self, value):
- self.model.ImageURL = _P.to_url(value)
-
-
-class UnoRadio(UnoBaseObject):
-
- def __init__(self, obj):
- super().__init__(obj)
-
- @property
- def type(self):
- return 'radio'
-
- @property
- def value(self):
- return self.model.Label
- @value.setter
- def value(self, value):
- self.model.Label = value
-
-
-class UnoCheckBox(UnoBaseObject):
-
- def __init__(self, obj):
- super().__init__(obj)
-
- @property
- def type(self):
- return 'checkbox'
-
- @property
- def value(self):
- return self.model.State
- @value.setter
- def value(self, value):
- self.model.State = value
-
- @property
- def label(self):
- return self.model.Label
- @label.setter
- def label(self, value):
- self.model.Label = value
-
- @property
- def tri_state(self):
- return self.model.TriState
- @tri_state.setter
- def tri_state(self, value):
- self.model.TriState = value
-
-
-# ~ https://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1awt_1_1UnoControlEditModel.html
-class UnoText(UnoBaseObject):
-
- def __init__(self, obj):
- super().__init__(obj)
-
- @property
- def type(self):
- return 'text'
-
- @property
- def value(self):
- return self.model.Text
- @value.setter
- def value(self, value):
- self.model.Text = value
-
- @property
- def echochar(self):
- return chr(self.model.EchoChar)
- @echochar.setter
- def echochar(self, value):
- if value:
- self.model.EchoChar = ord(value[0])
- else:
- self.model.EchoChar = 0
-
- def validate(self):
- return
-
-
-class UnoImage(UnoBaseObject):
-
- def __init__(self, obj):
- super().__init__(obj)
-
- @property
- def type(self):
- return 'image'
-
- @property
- def value(self):
- return self.url
- @value.setter
- def value(self, value):
- self.url = value
-
- @property
- def url(self):
- return self.m.ImageURL
- @url.setter
- def url(self, value):
- self.m.ImageURL = None
- self.m.ImageURL = _P.to_url(value)
-
-
-class UnoListBox(UnoBaseObject):
-
- def __init__(self, obj):
- super().__init__(obj)
- self._path = ''
-
- def __setattr__(self, name, value):
- if name in ('_path',):
- self.__dict__[name] = value
- else:
- super().__setattr__(name, value)
-
- @property
- def type(self):
- return 'listbox'
-
- @property
- def value(self):
- return self.obj.getSelectedItem()
-
- @property
- def count(self):
- return len(self.data)
-
- @property
- def data(self):
- return self.model.StringItemList
- @data.setter
- def data(self, values):
- self.model.StringItemList = list(sorted(values))
-
- @property
- def path(self):
- return self._path
- @path.setter
- def path(self, value):
- self._path = value
-
- def unselect(self):
- self.obj.selectItem(self.value, False)
- return
-
- def select(self, pos=0):
- if isinstance(pos, str):
- self.obj.selectItem(pos, True)
- else:
- self.obj.selectItemPos(pos, True)
- return
-
- def clear(self):
- self.model.removeAllItems()
- return
-
- def _set_image_url(self, image):
- if _P.exists(image):
- return _P.to_url(image)
-
- path = _P.join(self._path, DIR['images'], image)
- return _P.to_url(path)
-
- def insert(self, value, path='', pos=-1, show=True):
- if pos < 0:
- pos = self.count
- if path:
- self.model.insertItem(pos, value, self._set_image_url(path))
- else:
- self.model.insertItemText(pos, value)
- if show:
- self.select(pos)
- return
-
-
-class UnoRoadmap(UnoBaseObject):
-
- def __init__(self, obj):
- super().__init__(obj)
- self._options = ()
-
- def __setattr__(self, name, value):
- if name in ('_options',):
- self.__dict__[name] = value
- else:
- super().__setattr__(name, value)
-
- @property
- def options(self):
- return self._options
- @options.setter
- def options(self, values):
- self._options = values
- for i, v in enumerate(values):
- opt = self.model.createInstance()
- opt.ID = i
- opt.Label = v
- self.model.insertByIndex(i, opt)
- return
-
- @property
- def enabled(self):
- return True
- @enabled.setter
- def enabled(self, value):
- for m in self.model:
- m.Enabled = value
- return
-
- def set_enabled(self, index, value):
- self.model.getByIndex(index).Enabled = value
- return
-
-
-class UnoTree(UnoBaseObject):
-
- def __init__(self, obj, ):
- super().__init__(obj)
- self._tdm = None
- self._data = []
-
- def __setattr__(self, name, value):
- if name in ('_tdm', '_data'):
- self.__dict__[name] = value
- else:
- super().__setattr__(name, value)
-
- @property
- def selection(self):
- sel = self.obj.Selection
- return sel.DataValue, sel.DisplayValue
-
- @property
- def parent(self):
- parent = self.obj.Selection.Parent
- if parent is None:
- return ()
- return parent.DataValue, parent.DisplayValue
-
- def _get_parents(self, node):
- value = (node.DisplayValue,)
- parent = node.Parent
- if parent is None:
- return value
- return self._get_parents(parent) + value
-
- @property
- def parents(self):
- values = self._get_parents(self.obj.Selection)
- return values
-
- @property
- def root(self):
- if self._tdm is None:
- return ''
- return self._tdm.Root.DisplayValue
- @root.setter
- def root(self, value):
- self._add_data_model(value)
-
- def _add_data_model(self, name):
- tdm = create_instance('com.sun.star.awt.tree.MutableTreeDataModel')
- root = tdm.createNode(name, True)
- root.DataValue = 0
- tdm.setRoot(root)
- self.model.DataModel = tdm
- self._tdm = self.model.DataModel
- return
-
- @property
- def path(self):
- return self.root
- @path.setter
- def path(self, value):
- self.data = _P.walk_dir(value, True)
-
- @property
- def data(self):
- return self._data
- @data.setter
- def data(self, values):
- self._data = list(values)
- self._add_data()
-
- def _add_data(self):
- if not self.data:
- return
-
- parents = {}
- for node in self.data:
- parent = parents.get(node[1], self._tdm.Root)
- child = self._tdm.createNode(node[2], False)
- child.DataValue = node[0]
- parent.appendChild(child)
- parents[node[0]] = child
- self.obj.expandNode(self._tdm.Root)
- return
-
-
-# ~ https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1awt_1_1grid.html
-class UnoGrid(UnoBaseObject):
-
- def __init__(self, obj):
- super().__init__(obj)
- self._gdm = self.model.GridDataModel
- self._data = []
- self._formats = ()
-
- def __setattr__(self, name, value):
- if name in ('_gdm', '_data', '_formats'):
- self.__dict__[name] = value
- else:
- super().__setattr__(name, value)
-
- def __getitem__(self, key):
- value = self._gdm.getCellData(key[0], key[1])
- return value
-
- def __setitem__(self, key, value):
- self._gdm.updateCellData(key[0], key[1], value)
- return
-
- @property
- def type(self):
- return 'grid'
-
- @property
- def columns(self):
- return {}
- @columns.setter
- def columns(self, values):
- # ~ self._columns = values
- #~ https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1awt_1_1grid_1_1XGridColumn.html
- model = create_instance('com.sun.star.awt.grid.DefaultGridColumnModel', True)
- for properties in values:
- column = create_instance('com.sun.star.awt.grid.GridColumn', True)
- for k, v in properties.items():
- setattr(column, k, v)
- model.addColumn(column)
- self.model.ColumnModel = model
- return
-
- @property
- def data(self):
- return self._data
- @data.setter
- def data(self, values):
- self._data = values
- self.clear()
- headings = tuple(range(1, len(values) + 1))
- self._gdm.addRows(headings, values)
- # ~ rows = range(grid_dm.RowCount)
- # ~ colors = [COLORS['GRAY'] if r % 2 else COLORS['WHITE'] for r in rows]
- # ~ grid.Model.RowBackgroundColors = tuple(colors)
- return
-
- @property
- def value(self):
- if self.column == -1 or self.row == -1:
- return ''
- return self[self.column, self.row]
- @value.setter
- def value(self, value):
- if self.column > -1 and self.row > -1:
- self[self.column, self.row] = value
-
- @property
- def row(self):
- return self.obj.CurrentRow
-
- @property
- def row_count(self):
- return self._gdm.RowCount
-
- @property
- def column(self):
- return self.obj.CurrentColumn
-
- @property
- def is_valid(self):
- return not (self.row == -1 or self.column == -1)
-
- @property
- def selected_rows(self):
- value = self.obj.SelectedRows
- return value
-
- @property
- def row_background_colors(self):
- value = self.m.RowBackgroundColors
- return value
- @row_background_colors.setter
- def row_background_colors(self, colors):
- c = Color()
- self.m.RowBackgroundColors = (c(colors[0]), c(colors[1]))
-
- @property
- def formats(self):
- return self._formats
- @formats.setter
- def formats(self, values):
- self._formats = values
-
- def clear(self):
- self._gdm.removeAllRows()
- return
-
- def _format_columns(self, data):
- row = data
- if self.formats:
- for i, f in enumerate(formats):
- if f:
- row[i] = f.format(data[i])
- return row
-
- def add_row(self, data):
- self._data.append(data)
- row = self._format_columns(data)
- self._gdm.addRow(self.row_count + 1, row)
- return
-
- def set_cell_tooltip(self, col, row, value):
- self._gdm.updateCellToolTip(col, row, value)
- return
-
- def get_cell_tooltip(self, col, row):
- value = self._gdm.getCellToolTip(col, row)
- return value
-
- def sort(self, column, asc=True):
- self._gdm.sortByColumn(column, asc)
- self.update_row_heading()
- return
-
- def update_row_heading(self):
- for i in range(self.row_count):
- self._gdm.updateRowHeading(i, i + 1)
- return
-
- def remove_row(self, row):
- self._gdm.removeRow(row)
- del self._data[row]
- self.update_row_heading()
- return
-
-
-class UnoPage(object):
-
- def __init__(self, obj):
- self._obj = obj
- self._events = None
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- pass
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def model(self):
- return self._obj.Model
-
- # ~ @property
- # ~ def id(self):
- # ~ return self.m.TabPageID
-
- @property
- def parent(self):
- return self.obj.Context
-
- def _set_image_url(self, image):
- if _P.exists(image):
- return _P.to_url(image)
-
- path = _P.join(self._path, DIR['images'], image)
- return _P.to_url(path)
-
- def _special_properties(self, tipo, args):
- if tipo == 'link' and not 'Label' in args:
- args['Label'] = args['URL']
- return args
-
- if tipo == 'button':
- if 'ImageURL' in args:
- args['ImageURL'] = self._set_image_url(args['ImageURL'])
- args['FocusOnClick'] = args.get('FocusOnClick', False)
- return args
-
- if tipo == 'roadmap':
- args['Height'] = args.get('Height', self.height)
- if 'Title' in args:
- args['Text'] = args.pop('Title')
- return args
-
- if tipo == 'tree':
- args['SelectionType'] = args.get('SelectionType', SINGLE)
- return args
-
- if tipo == 'grid':
- args['ShowRowHeader'] = args.get('ShowRowHeader', True)
- return args
-
- if tipo == 'pages':
- args['Width'] = args.get('Width', self.width)
- args['Height'] = args.get('Height', self.height)
-
- return args
-
- def add_control(self, args):
- tipo = args.pop('Type').lower()
- root = args.pop('Root', '')
- sheets = args.pop('Sheets', ())
- columns = args.pop('Columns', ())
-
- args = self._special_properties(tipo, args)
- model = self.model.createInstance(UNO_MODELS[tipo])
- _set_properties(model, args)
- name = args['Name']
- self.model.insertByName(name, model)
- control = self.obj.getControl(name)
- _add_listeners(self._events, control, name)
- control = UNO_CLASSES[tipo](control)
-
- if tipo in ('listbox',):
- control.path = self.path
-
- if tipo == 'tree' and root:
- control.root = root
- elif tipo == 'grid' and columns:
- control.columns = columns
- elif tipo == 'pages' and sheets:
- control.sheets = sheets
- control.events = self.events
-
- setattr(self, name, control)
- return control
-
-
-class UnoPages(UnoBaseObject):
-
- def __init__(self, obj):
- super().__init__(obj)
- self._sheets = []
- self._events = None
-
- def __setattr__(self, name, value):
- if name in ('_sheets', '_events'):
- self.__dict__[name] = value
- else:
- super().__setattr__(name, value)
-
- def __getitem__(self, index):
- name = index
- if isinstance(index, int):
- name = f'sheet{index}'
- sheet = self.obj.getControl(name)
- page = UnoPage(sheet)
- page._events = self._events
- return page
-
- @property
- def type(self):
- return 'pages'
-
- @property
- def current(self):
- return self.obj.ActiveTabID
- @property
- def active(self):
- return self.current
-
- @property
- def sheets(self):
- return self._sheets
- @sheets.setter
- def sheets(self, values):
- self._sheets = values
- for i, title in enumerate(values):
- sheet = self.m.createInstance('com.sun.star.awt.UnoPageModel')
- sheet.Title = title
- self.m.insertByName(f'sheet{i + 1}', sheet)
- return
-
- @property
- def events(self):
- return self._events
- @events.setter
- def events(self, controllers):
- self._events = controllers
-
- @property
- def visible(self):
- return self.obj.Visible
- @visible.setter
- def visible(self, value):
- self.obj.Visible = value
-
- def insert(self, title):
- self._sheets.append(title)
- id = len(self._sheets)
- sheet = self.m.createInstance('com.sun.star.awt.UnoPageModel')
- sheet.Title = title
- self.m.insertByName(f'sheet{id}', sheet)
- return self[id]
-
- def remove(self, id):
- self.obj.removeTab(id)
- return
-
- def activate(self, id):
- self.obj.activateTab(id)
- return
-
-
-class UnoSpinButton(UnoBaseObject):
-
- def __init__(self, obj):
- super().__init__(obj)
-
- @property
- def type(self):
- return 'spinbutton'
-
- @property
- def value(self):
- return self.model.Label
- @value.setter
- def value(self, value):
- self.model.Label = value
-
-
-class UnoNumericField(UnoBaseObject):
-
- def __init__(self, obj):
- super().__init__(obj)
-
- @property
- def type(self):
- return 'numeric'
-
- @property
- def int(self):
- return int(self.value)
-
- @property
- def value(self):
- return self.model.Value
- @value.setter
- def value(self, value):
- self.model.Value = value
-
-
-UNO_CLASSES = {
- 'label': UnoLabel,
- 'link': UnoLabelLink,
- 'button': UnoButton,
- 'radio': UnoRadio,
- 'checkbox': UnoCheckBox,
- 'text': UnoText,
- 'image': UnoImage,
- 'listbox': UnoListBox,
- 'roadmap': UnoRoadmap,
- 'tree': UnoTree,
- 'grid': UnoGrid,
- 'pages': UnoPages,
- 'spinbutton': UnoSpinButton,
- 'numeric': UnoNumericField,
-}
-
-UNO_MODELS = {
- 'label': 'com.sun.star.awt.UnoControlFixedTextModel',
- 'link': 'com.sun.star.awt.UnoControlFixedHyperlinkModel',
- 'button': 'com.sun.star.awt.UnoControlButtonModel',
- 'radio': 'com.sun.star.awt.UnoControlRadioButtonModel',
- 'checkbox': 'com.sun.star.awt.UnoControlCheckBoxModel',
- 'text': 'com.sun.star.awt.UnoControlEditModel',
- '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',
- 'grid': 'com.sun.star.awt.grid.UnoControlGridModel',
- 'pages': 'com.sun.star.awt.UnoMultiPageModel',
- 'groupbox': 'com.sun.star.awt.UnoControlGroupBoxModel',
- 'combobox': 'com.sun.star.awt.UnoControlComboBoxModel',
- 'spinbutton': 'com.sun.star.awt.UnoControlSpinButtonModel',
- 'numeric': 'com.sun.star.awt.UnoControlNumericFieldModel',
-}
-# ~ 'CurrencyField': 'com.sun.star.awt.UnoControlCurrencyFieldModel',
-# ~ 'DateField': 'com.sun.star.awt.UnoControlDateFieldModel',
-# ~ 'FileControl': 'com.sun.star.awt.UnoControlFileControlModel',
-# ~ 'FormattedField': 'com.sun.star.awt.UnoControlFormattedFieldModel',
-# ~ 'PatternField': 'com.sun.star.awt.UnoControlPatternFieldModel',
-# ~ 'ProgressBar': 'com.sun.star.awt.UnoControlProgressBarModel',
-# ~ 'ScrollBar': 'com.sun.star.awt.UnoControlScrollBarModel',
-# ~ 'SimpleAnimation': 'com.sun.star.awt.UnoControlSimpleAnimationModel',
-# ~ 'Throbber': 'com.sun.star.awt.UnoControlThrobberModel',
-# ~ 'TimeField': 'com.sun.star.awt.UnoControlTimeFieldModel',
-
-
-class LODialog(object):
- SEPARATION = 5
- MODELS = {
- 'label': 'com.sun.star.awt.UnoControlFixedTextModel',
- 'link': 'com.sun.star.awt.UnoControlFixedHyperlinkModel',
- 'button': 'com.sun.star.awt.UnoControlButtonModel',
- 'radio': 'com.sun.star.awt.UnoControlRadioButtonModel',
- 'checkbox': 'com.sun.star.awt.UnoControlCheckBoxModel',
- 'text': 'com.sun.star.awt.UnoControlEditModel',
- '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',
- 'grid': 'com.sun.star.awt.grid.UnoControlGridModel',
- 'pages': 'com.sun.star.awt.UnoMultiPageModel',
- 'groupbox': 'com.sun.star.awt.UnoControlGroupBoxModel',
- 'combobox': 'com.sun.star.awt.UnoControlComboBoxModel',
- 'spinbutton': 'com.sun.star.awt.UnoControlSpinButtonModel',
- 'numeric': 'com.sun.star.awt.UnoControlNumericFieldModel',
- }
-
- def __init__(self, args={}):
- self._obj = self._create(args)
- self._model = self.obj.Model
- self._events = None
- self._modal = True
- self._controls = {}
- self._color_on_focus = COLOR_ON_FOCUS
- self._id = ''
- self._path = ''
- self._init_controls()
-
- def _create(self, args):
- service = 'com.sun.star.awt.DialogProvider'
- path = args.pop('Path', '')
- if path:
- dp = create_instance(service, True)
- dlg = dp.createDialog(_P.to_url(path))
- return dlg
-
- if 'Location' in args:
- name = args['Name']
- library = args.get('Library', 'Standard')
- location = args.get('Location', 'application').lower()
- if location == 'user':
- location = 'application'
- url = f'vnd.sun.star.script:{library}.{name}?location={location}'
- if location == 'document':
- dp = create_instance(service, args=docs.active.obj)
- else:
- dp = create_instance(service, True)
- # ~ uid = docs.active.uid
- # ~ url = f'vnd.sun.star.tdoc:/{uid}/Dialogs/{library}/{name}.xml'
- dlg = dp.createDialog(url)
- return dlg
-
- dlg = create_instance('com.sun.star.awt.UnoControlDialog', True)
- model = create_instance('com.sun.star.awt.UnoControlDialogModel', True)
- toolkit = create_instance('com.sun.star.awt.Toolkit', True)
-
- args['Title'] = args.get('Title', TITLE)
- args['Width'] = args.get('Width', 200)
- args['Height'] = args.get('Height', 150)
-
- _set_properties(model, args)
- dlg.setModel(model)
- dlg.setVisible(False)
- dlg.createPeer(toolkit, None)
- return dlg
-
- def _get_type_control(self, name):
- name = name.split('.')[2]
- types = {
- 'UnoFixedTextControl': 'label',
- 'UnoEditControl': 'text',
- 'UnoButtonControl': 'button',
- }
- return types[name]
-
- def _init_controls(self):
- for control in self.obj.getControls():
- tipo = self._get_type_control(control.ImplementationName)
- name = control.Model.Name
- control = UNO_CLASSES[tipo](control)
- setattr(self, name, control)
- return
-
- @property
- def obj(self):
- return self._obj
-
- @property
- def model(self):
- return self._model
-
- @property
- def controls(self):
- return self._controls
-
- @property
- def path(self):
- return self._path
- @property
- def path_images(self):
- return _P.join(self.path, DIR['images'])
- @property
- def id(self):
- return self._id
- @id.setter
- def id(self, value):
- self._id = value
- self._path = _P.from_id(value)
-
- @property
- def height(self):
- return self.model.Height
- @height.setter
- def height(self, value):
- self.model.Height = value
-
- @property
- def width(self):
- return self.model.Width
- @width.setter
- def width(self, value):
- self.model.Width = value
-
- @property
- def visible(self):
- return self.obj.Visible
- @visible.setter
- def visible(self, value):
- self.obj.Visible = value
-
- @property
- def step(self):
- return self.model.Step
- @step.setter
- def step(self, value):
- self.model.Step = value
-
- @property
- def events(self):
- return self._events
- @events.setter
- def events(self, controllers):
- self._events = controllers(self)
- self._connect_listeners()
-
- @property
- def color_on_focus(self):
- return self._color_on_focus
- @color_on_focus.setter
- def color_on_focus(self, value):
- self._color_on_focus = get_color(value)
-
- def _connect_listeners(self):
- for control in self.obj.Controls:
- _add_listeners(self.events, control, control.Model.Name)
- return
-
- def _set_image_url(self, image):
- if _P.exists(image):
- return _P.to_url(image)
-
- path = _P.join(self._path, DIR['images'], image)
- return _P.to_url(path)
-
- def _special_properties(self, tipo, args):
- if tipo == 'link' and not 'Label' in args:
- args['Label'] = args['URL']
- return args
-
- if tipo == 'button':
- if 'ImageURL' in args:
- args['ImageURL'] = self._set_image_url(args['ImageURL'])
- args['FocusOnClick'] = args.get('FocusOnClick', False)
- return args
-
- if tipo == 'roadmap':
- args['Height'] = args.get('Height', self.height)
- if 'Title' in args:
- args['Text'] = args.pop('Title')
- return args
-
- if tipo == 'tree':
- args['SelectionType'] = args.get('SelectionType', SINGLE)
- return args
-
- if tipo == 'grid':
- args['X'] = args.get('X', self.SEPARATION)
- args['Y'] = args.get('Y', self.SEPARATION)
- args['Width'] = args.get('Width', self.width - self.SEPARATION * 2)
- args['Height'] = args.get('Height', self.height - self.SEPARATION * 2)
- args['ShowRowHeader'] = args.get('ShowRowHeader', True)
- return args
-
- if tipo == 'pages':
- args['Width'] = args.get('Width', self.width)
- args['Height'] = args.get('Height', self.height)
-
- return args
-
- def add_control(self, args):
- tipo = args.pop('Type').lower()
- root = args.pop('Root', '')
- sheets = args.pop('Sheets', ())
- columns = args.pop('Columns', ())
-
- args = self._special_properties(tipo, args)
- model = self.model.createInstance(self.MODELS[tipo])
-
- _set_properties(model, args)
- name = args['Name']
- self.model.insertByName(name, model)
- control = self.obj.getControl(name)
- _add_listeners(self.events, control, name)
- control = UNO_CLASSES[tipo](control)
-
- if tipo in ('listbox',):
- control.path = self.path
-
- if tipo == 'tree' and root:
- control.root = root
- elif tipo == 'grid' and columns:
- control.columns = columns
- elif tipo == 'pages' and sheets:
- control.sheets = sheets
- control.events = self.events
-
- setattr(self, name, control)
- self._controls[name] = control
- return control
-
- def center(self, control, x=0, y=0):
- w = self.width
- h = self.height
-
- if isinstance(control, tuple):
- wt = self.SEPARATION * -1
- for c in control:
- wt += c.width + self.SEPARATION
- x = w / 2 - wt / 2
- for c in control:
- c.x = x
- x = c.x + c.width + self.SEPARATION
- return
-
- if x < 0:
- x = w + x - control.width
- elif x == 0:
- x = w / 2 - control.width / 2
- if y < 0:
- y = h + y - control.height
- elif y == 0:
- y = h / 2 - control.height / 2
- control.x = x
- control.y = y
- return
-
- def open(self, modal=True):
- self._modal = modal
- if modal:
- return self.obj.execute()
- else:
- self.visible = True
- return
-
- def close(self, value=0):
- if self._modal:
- value = self.obj.endDialog(value)
- else:
- self.visible = False
- self.obj.dispose()
- return value
-
- def set_values(self, data):
- for k, v in data.items():
- self._controls[k].value = v
- return
-
-
-class LOCells(object):
-
- def __getitem__(self, index):
- return LODocs().active.active[index]
-
-
-class LOWindow(object):
- EMPTY = """
-
-"""
- MODELS = {
- 'label': 'com.sun.star.awt.UnoControlFixedTextModel',
- 'link': 'com.sun.star.awt.UnoControlFixedHyperlinkModel',
- 'button': 'com.sun.star.awt.UnoControlButtonModel',
- 'radio': 'com.sun.star.awt.UnoControlRadioButtonModel',
- 'checkbox': 'com.sun.star.awt.UnoControlCheckBoxModel',
- 'text': 'com.sun.star.awt.UnoControlEditModel',
- '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',
- 'grid': 'com.sun.star.awt.grid.UnoControlGridModel',
- 'pages': 'com.sun.star.awt.UnoMultiPageModel',
- 'groupbox': 'com.sun.star.awt.UnoControlGroupBoxModel',
- 'combobox': 'com.sun.star.awt.UnoControlComboBoxModel',
- }
-
- def __init__(self, args):
- self._events = None
- self._menu = None
- self._container = None
- self._model = None
- self._id = ''
- self._path = ''
- self._obj = self._create(args)
-
- def _create(self, properties):
- ps = (
- properties.get('X', 0),
- properties.get('Y', 0),
- properties.get('Width', 500),
- properties.get('Height', 500),
- )
- self._title = properties.get('Title', TITLE)
- self._create_frame(ps)
- self._create_container(ps)
- self._create_subcontainer(ps)
- # ~ self._create_splitter(ps)
- return
-
- def _create_frame(self, ps):
- service = 'com.sun.star.frame.TaskCreator'
- tc = create_instance(service, True)
- self._frame = tc.createInstanceWithArguments((
- NamedValue('FrameName', 'EasyMacroWin'),
- NamedValue('PosSize', Rectangle(*ps)),
- ))
- self._window = self._frame.getContainerWindow()
- self._toolkit = self._window.getToolkit()
- desktop = get_desktop()
- self._frame.setCreator(desktop)
- desktop.getFrames().append(self._frame)
- self._frame.Title = self._title
- return
-
- def _create_container(self, ps):
- service = 'com.sun.star.awt.UnoControlContainer'
- self._container = create_instance(service, True)
- service = 'com.sun.star.awt.UnoControlContainerModel'
- model = create_instance(service, True)
- model.BackgroundColor = get_color((225, 225, 225))
- self._container.setModel(model)
- self._container.createPeer(self._toolkit, self._window)
- self._container.setPosSize(*ps, POSSIZE)
- self._frame.setComponent(self._container, None)
- return
-
- def _create_subcontainer(self, ps):
- service = 'com.sun.star.awt.ContainerWindowProvider'
- cwp = create_instance(service, True)
-
- path_tmp = _P.save_tmp(self.EMPTY)
- subcont = cwp.createContainerWindow(
- _P.to_url(path_tmp), '', self._container.getPeer(), None)
- _P.kill(path_tmp)
-
- subcont.setPosSize(0, 0, 500, 500, POSSIZE)
- subcont.setVisible(True)
- self._container.addControl('subcont', subcont)
- self._subcont = subcont
- self._model = subcont.Model
- return
-
- def _create_popupmenu(self, menus):
- menu = create_instance('com.sun.star.awt.PopupMenu', True)
- for i, m in enumerate(menus):
- label = m['label']
- cmd = m.get('event', '')
- if not cmd:
- cmd = label.lower().replace(' ', '_')
- if label == '-':
- menu.insertSeparator(i)
- else:
- menu.insertItem(i, label, m.get('style', 0), i)
- menu.setCommand(i, cmd)
- # ~ menu.setItemImage(i, path?, True)
- menu.addMenuListener(EventsMenu(self.events))
- return menu
-
- def _create_menu(self, menus):
- #~ https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1awt_1_1XMenu.html
- #~ nItemId specifies the ID of the menu item to be inserted.
- #~ aText specifies the label of the menu item.
- #~ nItemStyle 0 = Standard, CHECKABLE = 1, RADIOCHECK = 2, AUTOCHECK = 4
- #~ nItemPos specifies the position where the menu item will be inserted.
- self._menu = create_instance('com.sun.star.awt.MenuBar', True)
- for i, m in enumerate(menus):
- self._menu.insertItem(i, m['label'], m.get('style', 0), i)
- cmd = m['label'].lower().replace(' ', '_')
- self._menu.setCommand(i, cmd)
- submenu = self._create_popupmenu(m['submenu'])
- self._menu.setPopupMenu(i, submenu)
-
- self._window.setMenuBar(self._menu)
- return
-
- def _add_listeners(self, control=None):
- if self.events is None:
- return
- controller = EventsWindow(self)
- self._window.addTopWindowListener(controller)
- self._window.addWindowListener(controller)
- # ~ self._container.addKeyListener(EventsKeyWindow(self))
- return
-
- def _set_image_url(self, image):
- if _P.exists(image):
- return _P.to_url(image)
-
- path = _P.join(self._path, DIR['images'], image)
- return _P.to_url(path)
-
- def _special_properties(self, tipo, args):
- if tipo == 'link' and not 'Label' in args:
- args['Label'] = args['URL']
- return args
-
- if tipo == 'button':
- if 'ImageURL' in args:
- args['ImageURL'] = self._set_image_url(args['ImageURL'])
- args['FocusOnClick'] = args.get('FocusOnClick', False)
- return args
-
- if tipo == 'roadmap':
- args['Height'] = args.get('Height', self.height)
- if 'Title' in args:
- args['Text'] = args.pop('Title')
- return args
-
- if tipo == 'tree':
- args['SelectionType'] = args.get('SelectionType', SINGLE)
- return args
-
- if tipo == 'grid':
- args['ShowRowHeader'] = args.get('ShowRowHeader', True)
- return args
-
- if tipo == 'pages':
- args['Width'] = args.get('Width', self.width)
- args['Height'] = args.get('Height', self.height)
-
- return args
-
- def add_control(self, args):
- tipo = args.pop('Type').lower()
- root = args.pop('Root', '')
- sheets = args.pop('Sheets', ())
- columns = args.pop('Columns', ())
-
- args = self._special_properties(tipo, args)
- model = self.model.createInstance(self.MODELS[tipo])
- _set_properties(model, args)
- name = args['Name']
- self.model.insertByName(name, model)
- control = self._subcont.getControl(name)
- _add_listeners(self.events, control, name)
- control = UNO_CLASSES[tipo](control)
-
- # ~ if tipo in ('listbox',):
- # ~ control.path = self.path
-
- if tipo == 'tree' and root:
- control.root = root
- elif tipo == 'grid' and columns:
- control.columns = columns
- elif tipo == 'pages' and sheets:
- control.sheets = sheets
- control.events = self.events
-
- setattr(self, name, control)
- return control
-
- @property
- def events(self):
- return self._events
- @events.setter
- def events(self, controllers):
- self._events = controllers(self)
- self._add_listeners()
-
- @property
- def model(self):
- return self._model
-
- @property
- def width(self):
- return self._container.Size.Width
-
- @property
- def height(self):
- return self._container.Size.Height
-
- @property
- def name(self):
- return self._title.lower().replace(' ', '_')
-
- def add_menu(self, menus):
- self._create_menu(menus)
- return
-
- def open(self):
- self._window.setVisible(True)
- return
-
- def close(self):
- self._window.setMenuBar(None)
- self._window.dispose()
- self._frame.close(True)
- return
-
-
-class LODBServer(object):
- DRIVERS = {
- 'mysql': 'mysqlc',
- 'mariadb': 'mysqlc',
- 'postgres': 'postgresql:postgresql',
- }
- PORTS = {
- 'mysql': 3306,
- 'mariadb': 3306,
- 'postgres': 5432,
- }
-
- def __init__(self):
- self._conn = None
- self._error = 'Not connected'
- self._type = ''
- self._drivers = []
-
- def __str__(self):
- return f'DB type {self._type}'
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- self.disconnet()
-
- @property
- def is_connected(self):
- return not self._conn is None
-
- @property
- def error(self):
- return self._error
-
- @property
- def drivers(self):
- return self._drivers
-
- def disconnet(self):
- if not self._conn is None:
- if not self._conn.isClosed():
- self._conn.close()
- self._conn.dispose()
- return
-
- def connect(self, options={}):
- args = options.copy()
- self._error = ''
- self._type = args.get('type', 'postgres')
- driver = self.DRIVERS[self._type]
- server = args.get('server', 'localhost')
- port = args.get('port', self.PORTS[self._type])
- dbname = args.get('dbname', '')
- user = args['user']
- password = args['password']
-
- data = {'user': user, 'password': password}
- url = f'sdbc:{driver}:{server}:{port}/{dbname}'
-
- # ~ https://downloads.mariadb.com/Connectors/java/
- # ~ data['JavaDriverClass'] = 'org.mariadb.jdbc.Driver'
- # ~ url = f'jdbc:mysql://{server}:{port}/{dbname}'
-
- args = dict_to_property(data)
- manager = create_instance('com.sun.star.sdbc.DriverManager')
- self._drivers = [d.ImplementationName for d in manager]
-
- try:
- self._conn = manager.getConnectionWithInfo(url, args)
- except Exception as e:
- error(e)
- self._error = str(e)
-
- return self
-
- def execute(self, sql):
- query = self._conn.createStatement()
- try:
- query.execute(sql)
- result = True
- except Exception as e:
- error(e)
- self._error = str(e)
- result = False
-
- return result
-
-
-def create_window(args):
- return LOWindow(args)
-
-
-class Paths(object):
-
- @classmethod
- def image(cls, path):
- # ~ sfa = create_instance('com.sun.star.ucb.SimpleFileAccess')
- # ~ stream = sfa.openFileRead(cls.to_url(path))
- gp = create_instance('com.sun.star.graphic.GraphicProvider')
- if isinstance(path, str):
- properties = (PropertyValue(Name='URL', Value=cls.to_url(path)),)
- else:
- properties = (PropertyValue(Name='InputStream', Value=path),)
- image = gp.queryGraphic(properties)
- return image
-
-
-class IOStream(object):
-
- @classmethod
- def qr(cls, data, **kwargs):
- import segno
-
- kwargs['kind'] = kwargs.get('kind', 'svg')
- kwargs['scale'] = kwargs.get('scale', 8)
- kwargs['border'] = kwargs.get('border', 2)
- buffer = cls.buffer()
- segno.make(data).save(buffer, **kwargs)
- stream = cls.input(buffer)
- return stream
-
-
-class SpellChecker(object):
-
- def __init__(self):
- service = 'com.sun.star.linguistic2.SpellChecker'
- self._spellchecker = create_instance(service, True)
- self._locale = LOCALE
-
- @property
- def locale(self):
- slocal = f'{self._locale.Language}-{self._locale.Country}'
- return slocale
- @locale.setter
- def locale(self, value):
- lang = value.split('-')
- self._locale = Locale(lang[0], lang[1], '')
-
- def is_valid(self, word):
- result = self._spellchecker.isValid(word, self._locale, ())
- return result
-
- def spell(self, word):
- result = self._spellchecker.spell(word, self._locale, ())
- if result:
- result = result.getAlternatives()
- if not isinstance(result, tuple):
- result = ()
- return result
-
-
-def spell(word, locale=''):
- sc = SpellChecker()
- if locale:
- sc.locale = locale
- return sc.spell(word)
-
-
-def __getattr__(name):
- if name == 'current_region':
- return LODocs().active.selection.current_region
- if name in ('rectangle', 'pos_size'):
- return Rectangle()
- if name == 'db':
- return LODBServer()
- if name == 'cells':
- return LOCells()
- raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
-
-
-def create_dialog(args={}):
- return LODialog(args)
-
-
-def inputbox(message, default='', title=TITLE, echochar=''):
-
- class ControllersInput(object):
-
- def __init__(self, dlg):
- self.d = dlg
-
- def cmd_ok_action(self, event):
- self.d.close(1)
- return
-
- args = {
- 'Title': title,
- 'Width': 200,
- 'Height': 80,
- }
- dlg = LODialog(args)
- dlg.events = ControllersInput
-
- args = {
- 'Type': 'Label',
- 'Name': 'lbl_msg',
- 'Label': message,
- 'Width': 140,
- 'Height': 50,
- 'X': 5,
- 'Y': 5,
- 'MultiLine': True,
- 'Border': 1,
- }
- dlg.add_control(args)
-
- args = {
- 'Type': 'Text',
- 'Name': 'txt_value',
- 'Text': default,
- 'Width': 190,
- 'Height': 15,
- }
- if echochar:
- args['EchoChar'] = ord(echochar[0])
- dlg.add_control(args)
- dlg.txt_value.move(dlg.lbl_msg)
-
- args = {
- 'Type': 'button',
- 'Name': 'cmd_ok',
- 'Label': _('OK'),
- 'Width': 40,
- 'Height': 15,
- 'DefaultButton': True,
- 'PushButtonType': 1,
- }
- dlg.add_control(args)
- dlg.cmd_ok.move(dlg.lbl_msg, 10, 0)
-
- args = {
- 'Type': 'button',
- 'Name': 'cmd_cancel',
- 'Label': _('Cancel'),
- 'Width': 40,
- 'Height': 15,
- 'PushButtonType': 2,
- }
- dlg.add_control(args)
- dlg.cmd_cancel.move(dlg.cmd_ok)
-
- if dlg.open():
- return dlg.txt_value.value
-
- return ''
diff --git a/source/easymacro.bk1 b/source/easymacro.py
similarity index 70%
rename from source/easymacro.bk1
rename to source/easymacro.py
index e73a7ba..37465d4 100644
--- a/source/easymacro.bk1
+++ b/source/easymacro.py
@@ -18,31 +18,24 @@
# ~ along with easymacro. If not, see .
-import csv
+
import getpass
import hashlib
import io
-import json
+
import logging
import os
import platform
import re
import shlex
-import shutil
import socket
import ssl
-import subprocess
+
import tempfile
import threading
-import time
-
-
-
-from pathlib import Path
from string import Template
-
from socket import timeout
from urllib import parse
from urllib.request import Request, urlopen
@@ -57,7 +50,7 @@ from email.mime.text import MIMEText
from email.utils import formatdate
from email import encoders
-import unohelper
+
from com.sun.star.awt import Rectangle, Size, Point
from com.sun.star.awt import Key, KeyEvent, KeyModifier
@@ -66,10 +59,10 @@ from com.sun.star.awt import Key, KeyEvent, KeyModifier
from com.sun.star.datatransfer import XTransferable, DataFlavor
from com.sun.star.io import IOException, XOutputStream
-from com.sun.star.ui.dialogs import TemplateDescription
+
from com.sun.star.sheet import XRangeSelectionListener
-from com.sun.star.lang import XEventListener
+
from com.sun.star.container import NoSuchElementException
@@ -80,55 +73,12 @@ TIMEOUT = 10
SALT = b'00a1bfb05353bb3fd8e7aa7fe5efdccc'
_EVENTS = {}
-PYTHON = 'python'
-if IS_WIN:
- PYTHON = 'python.exe'
FILES = {
'CONFIG': 'zaz-{}.json',
}
DIRS = {}
-MESSAGES = {
- 'es': {
- 'OK': 'Aceptar',
- 'Cancel': 'Cancelar',
- 'Select path': 'Seleccionar ruta',
- 'Select directory': 'Seleccionar directorio',
- 'Select file': 'Seleccionar archivo',
- 'Incorrect user or password': 'Nombre de usuario o contraseña inválidos',
- 'Allow less secure apps in GMail': 'Activa: Permitir aplicaciones menos segura en GMail',
- }
-}
-
-
-
-
-
-
-
-def _(msg):
- if LANG == 'en':
- return msg
-
- if not LANG in MESSAGES:
- return msg
-
- return MESSAGES[LANG][msg]
-
-
-
-
-
-
-
-
-def sleep(seconds: int):
- """Sleep
- """
- time.sleep(seconds)
- return
-
def run_in_thread(fn):
"""Run any function in thread
@@ -173,160 +123,9 @@ def render(template, data):
s = Template(template)
return s.safe_substitute(**data)
-
-def _set_properties(model, properties):
- # ~ if 'X' in properties:
- # ~ properties['PositionX'] = properties.pop('X')
- # ~ if 'Y' in properties:
- # ~ properties['PositionY'] = properties.pop('Y')
- keys = tuple(properties.keys())
- values = tuple(properties.values())
- model.setPropertyValues(keys, values)
- return
-
-
# Classes
-
-# ~ https://github.com/django/django/blob/main/django/utils/functional.py#L61
-class classproperty:
-
- def __init__(self, method=None):
- self.fget = method
-
- def __get__(self, instance, cls=None):
- return self.fget(cls)
-
- def getter(self, method):
- self.fget = method
- return self
-
-
-class Dates(object):
- """Class for datetimes
- """
- _start = None
-
- @classproperty
- def now(cls):
- """Current local date and time
-
- :return: Return the current local date and time
- :rtype: datetime
- """
- return datetime.datetime.now().replace(microsecond=0)
-
- @classproperty
- def today(cls):
- """Current local date
-
- :return: Return the current local date
- :rtype: date
- """
- return datetime.date.today()
-
- @classproperty
- def time(cls):
- """Current local time
-
- :return: Return the current local time
- :rtype: datetime.time
- """
- t = cls.now.time().replace(microsecond=0)
- return t
-
- @classproperty
- def epoch(cls):
- """Get unix time
-
- :return: Return unix time
- :rtype: int
-
- `See Unix Time `_
- """
- n = cls.now
- e = int(time.mktime(n.timetuple()))
- return e
-
- @classmethod
- def date(cls, year: int, month: int, day: int):
- """Get date from year, month, day
-
- :param year: Year of date
- :type year: int
- :param month: Month of date
- :type month: int
- :param day: Day of day
- :type day: int
- :return: Return the date
- :rtype: date
-
- `See Python date `_
- """
- d = datetime.date(year, month, day)
- return d
-
- @classmethod
- def str_to_date(cls, str_date: str, template: str, to_calc: bool=False):
- """Get date from string
-
- :param str_date: Date in string
- :type str_date: str
- :param template: Formato of date string
- :type template: str
- :param to_calc: If date is for used in Calc cell
- :type to_calc: bool
- :return: Return date or int if used in Calc
- :rtype: date or int
-
- `See Python strptime `_
- """
- d = datetime.datetime.strptime(str_date, template).date()
- if to_calc:
- d = d.toordinal() - DATE_OFFSET
- return d
-
- @classmethod
- def calc_to_date(cls, value: float):
- """Get date from calc value
-
- :param value: Float value from cell
- :type value: float
- :return: Return the current local date
- :rtype: date
-
- `See Python fromordinal `_
- """
- d = datetime.date.fromordinal(int(value) + DATE_OFFSET)
- return d
-
- @classmethod
- def start(cls):
- """Start counter
- """
- cls._start = cls.now
- info('Start: ', cls._start)
- return
-
- @classmethod
- def end(cls, get_seconds: bool=True):
- """End counter
-
- :param get_seconds: If return value in total seconds
- :type get_seconds: bool
- :return: Return the timedelta or total seconds
- :rtype: timedelta or int
- """
- e = cls.now
- td = e - cls._start
- result = str(td)
- if get_seconds:
- result = td.total_seconds()
- info('End: ', e)
- return result
-
-
class Json(object):
"""Class for json data
"""
@@ -588,630 +387,6 @@ class Hash(object):
return result
-class Paths(object):
- """Class for paths
- """
- FILE_PICKER = 'com.sun.star.ui.dialogs.FilePicker'
- FOLDER_PICKER = 'com.sun.star.ui.dialogs.FolderPicker'
- REMOTE_FILE_PICKER = 'com.sun.star.ui.dialogs.RemoteFilePicker'
- OFFICE_FILE_PICKER = 'com.sun.star.ui.dialogs.OfficeFilePicker'
-
- def __init__(self, path=''):
- if path.startswith('file://'):
- path = str(Path(uno.fileUrlToSystemPath(path)).resolve())
- self._path = Path(path)
-
- @property
- def path(self):
- """Get base path"""
- return str(self._path.parent)
-
- @property
- def file_name(self):
- """Get file name"""
- return self._path.name
-
- @property
- def name(self):
- """Get name"""
- return self._path.stem
-
- @property
- def ext(self):
- """Get extension"""
- return self._path.suffix[1:]
-
- @property
- def size(self):
- """Get size"""
- return self._path.stat().st_size
-
- @property
- def url(self):
- """Get like URL"""
- return self._path.as_uri()
-
- @property
- def info(self):
- """Get all info like tuple"""
- i = (self.path, self.file_name, self.name, self.ext, self.size, self.url)
- return i
-
- @property
- def dict(self):
- """Get all info like dict"""
- data = {
- 'path': self.path,
- 'file_name': self.file_name,
- 'name': self.name,
- 'ext': self.ext,
- 'size': self.size,
- 'url': self.url,
- }
- return data
-
- @classproperty
- def home(self):
- """Get user home"""
- return str(Path.home())
-
- @classproperty
- def documents(self):
- """Get user save documents"""
- return self.config()
-
- @classproperty
- def user_profile(self):
- """Get path user profile"""
- path = self.config('UserConfig')
- path = str(Path(path).parent)
- return path
-
- @classproperty
- def user_config(self):
- """Get path config in user profile"""
- path = self.config('UserConfig')
- return path
-
- @classproperty
- def python(self):
- """Get path executable python"""
- if IS_WIN:
- path = self.join(self.config('Module'), PYTHON)
- elif IS_MAC:
- path = self.join(self.config('Module'), '..', 'Resources', PYTHON)
- else:
- path = sys.executable
- return path
-
- @classmethod
- def to_system(cls, path:str) -> str:
- """Convert paths in URL to system
-
- :param path: Path to convert
- :type path: str
- :return: Path system format
- :rtype: str
- """
- if path.startswith('file://'):
- path = str(Path(uno.fileUrlToSystemPath(path)).resolve())
- return path
-
- @classmethod
- def to_url(cls, path: str) -> str:
- """Convert paths in format system to URL
-
- :param path: Path to convert
- :type path: str
- :return: Path in URL
- :rtype: str
- """
- if not path.startswith('file://'):
- path = Path(path).as_uri()
- return path
-
- @classmethod
- def config(cls, name: str='Work') -> Union[str, list]:
- """Return path from config
-
- :param name: Name in service PathSettings, default get path documents
- :type name: str
- :return: Path in config, if exists.
- :rtype: str or list
-
- `See Api XPathSettings `_
- """
- path = create_instance('com.sun.star.util.PathSettings')
- path = cls.to_system(getattr(path, name)).split(';')
- if len(path) == 1:
- path = path[0]
- return path
-
- @classmethod
- def join(cls, *paths: str) -> str:
- """Join paths
-
- :param paths: Paths to join
- :type paths: list
- :return: New path with joins
- :rtype: str
- """
- path = str(Path(paths[0]).joinpath(*paths[1:]))
- return path
-
- @classmethod
- def exists(cls, path: str) -> bool:
- """If exists path
-
- :param path: Path for validate
- :type path: str
- :return: True if path exists, False if not.
- :rtype: bool
- """
- path = cls.to_system(path)
- result = Path(path).exists()
- return result
-
- @classmethod
- def exists_app(cls, name_app: str) -> bool:
- """If exists app in system
-
- :param name_app: Name of application
- :type name_app: str
- :return: True if app exists, False if not.
- :rtype: bool
- """
- result = bool(shutil.which(name_app))
- return result
-
- @classmethod
- def is_dir(cls, path: str):
- """Validate if path is directory
-
- :param path: Path for validate
- :type path: str
- :return: True if path is directory, False if not.
- :rtype: bool
- """
- return Path(path).is_dir()
-
- @classmethod
- def is_file(cls, path: str):
- """Validate if path is a file
-
- :param path: Path for validate
- :type path: str
- :return: True if path is a file, False if not.
- :rtype: bool
- """
- return Path(path).is_file()
-
- @classmethod
- def temp_file(self):
- """Make temporary file"""
- return tempfile.NamedTemporaryFile(mode='w')
-
- @classmethod
- def temp_dir(self):
- """Make temporary directory"""
- return tempfile.TemporaryDirectory(ignore_cleanup_errors=True)
-
- @classmethod
- def get(cls, init_dir: str='', filters: str='') -> str:
- """Get path for save
-
- :param init_dir: Initial default path
- :type init_dir: str
- :param filters: Filter for show type files: 'xml' or 'txt,xml'
- :type filters: str
- :return: Selected path
- :rtype: str
-
- `See API `_
- """
- if not init_dir:
- init_dir = cls.documents
- init_dir = cls.to_url(init_dir)
- file_picker = create_instance(cls.FILE_PICKER)
- file_picker.setTitle(_('Select path'))
- file_picker.setDisplayDirectory(init_dir)
- file_picker.initialize((TemplateDescription.FILEOPEN_SIMPLE,))
- if filters:
- for f in filters.split(','):
- file_picker.appendFilter(f.upper(), f'*.{f.lower()}')
-
- path = ''
- if file_picker.execute():
- path = cls.to_system(file_picker.getSelectedFiles()[0])
- return path
-
- @classmethod
- def get_dir(cls, init_dir: str='') -> str:
- """Get path dir
-
- :param init_dir: Initial default path
- :type init_dir: str
- :return: Selected path
- :rtype: str
- """
- folder_picker = create_instance(cls.FOLDER_PICKER)
- if not init_dir:
- init_dir = cls.documents
- init_dir = cls.to_url(init_dir)
- folder_picker.setTitle(_('Select directory'))
- folder_picker.setDisplayDirectory(init_dir)
-
- path = ''
- if folder_picker.execute():
- path = cls.to_system(folder_picker.getDirectory())
- return path
-
- @classmethod
- def get_file(cls, init_dir: str='', filters: str='', multiple: bool=False):
- """Get path exists file
-
- :param init_dir: Initial default path
- :type init_dir: str
- :param filters: Filter for show type files: 'xml' or 'txt,xml'
- :type filters: str
- :param multiple: If user can selected multiple files
- :type multiple: bool
- :return: Selected path
- :rtype: str
- """
- if not init_dir:
- init_dir = cls.documents
- init_dir = cls.to_url(init_dir)
-
- file_picker = create_instance(cls.FILE_PICKER)
- file_picker.setTitle(_('Select file'))
- file_picker.setDisplayDirectory(init_dir)
- file_picker.setMultiSelectionMode(multiple)
-
- if filters:
- for f in filters.split(','):
- file_picker.appendFilter(f.upper(), f'*.{f.lower()}')
-
- path = ''
- if file_picker.execute():
- files = file_picker.getSelectedFiles()
- path = [cls.to_system(f) for f in files]
- if not multiple:
- path = path[0]
- return path
-
- @classmethod
- def files(cls, path: str, pattern: str='*'):
- """Get all files in path
-
- :param path: Path with files
- :type path: str
- :param pattern: For filter files, default get all.
- :type pattern: str
- :return: Files in path
- :rtype: list
- """
- files = [str(p) for p in Path(path).glob(pattern) if p.is_file()]
- return files
-
- @classmethod
- def walk(cls, path, filters=''):
- """Get all files in path recursively
-
- :param path: Path with files
- :type path: str
- :param filters: For filter files, default get all.
- :type filters: str
- :return: Files in path
- :rtype: list
- """
- paths = []
- for folder, _, files in os.walk(path):
- if filters:
- pattern = re.compile(r'\.(?:{})$'.format(filters), re.IGNORECASE)
- paths += [cls.join(folder, f) for f in files if pattern.search(f)]
- else:
- paths += [cls.join(folder, f) for f in files]
- return paths
-
- @classmethod
- def dirs(cls, path):
- """Get directories in path
-
- :param path: Path to scan
- :type path: str
- :return: Directories in path
- :rtype: list
- """
- dirs = [str(p) for p in Path(path).iterdir() if p.is_dir()]
- return dirs
-
- @classmethod
- def walk_dirs(cls, path, tree=False):
- """Get directories recursively
-
- :param path: Path to scan
- :type path: str
- :param tree: get info in a tuple (ID_FOLDER, ID_PARENT, NAME)
- :type tree: bool
- :return: Directories in path
- :rtype: list
- """
- folders = []
- if tree:
- i = 0
- parents = {path: 0}
- for root, dirs, _ in os.walk(path):
- for name in dirs:
- i += 1
- rn = cls.join(root, name)
- if not rn in parents:
- parents[rn] = i
- folders.append((i, parents[root], name))
- else:
- for root, dirs, _ in os.walk(path):
- folders += [cls.join(root, name) for name in dirs]
- return folders
-
- @classmethod
- def extension(cls, id_ext: str):
- """Get path extension install from id
-
- :param id_ext: ID extension
- :type id_ext: str
- :return: Path extension
- :rtype: str
- """
- pip = CTX.getValueByName('/singletons/com.sun.star.deployment.PackageInformationProvider')
- path = Paths.to_system(pip.getPackageLocation(id_ext))
- return path
-
- @classmethod
- def replace_ext(cls, path: str, new_ext: str):
- """Replace extension in file path
-
- :param path: Path to file
- :type path: str
- :param new_ext: New extension
- :type new_ext: str
- :return: Path with new extension
- :rtype: str
- """
- p = Paths(path)
- name = f'{p.name}.{new_ext}'
- path = cls.join(p.path, name)
- return path
-
- @classmethod
- def open(cls, path: str):
- """Open any file with default program in systema
-
- :param path: Path to file
- :type path: str
- :return: PID file, only Linux
- :rtype: int
- """
- pid = 0
- if IS_WIN:
- os.startfile(path)
- else:
- pid = subprocess.Popen(['xdg-open', path]).pid
- return pid
-
- # ~ Save/read data
-
- @classmethod
- def save(cls, path: str, data: str, encoding: str='utf-8') -> bool:
- """Save data in path with encoding
-
- :param path: Path to file save
- :type path: str
- :param data: Data to save
- :type data: str
- :param encoding: Encoding for save data, default utf-8
- :type encoding: str
- :return: True, if save corrrectly
- :rtype: bool
- """
- result = bool(Path(path).write_text(data, encoding=encoding))
- return result
-
- @classmethod
- def save_bin(cls, path: str, data: bytes) -> bool:
- """Save binary data in path
-
- :param path: Path to file save
- :type path: str
- :param data: Data to save
- :type data: bytes
- :return: True, if save corrrectly
- :rtype: bool
- """
- result = bool(Path(path).write_bytes(data))
- return result
-
- @classmethod
- def read(cls, path: str, get_lines: bool=False, encoding: str='utf-8') -> Union[str, list]:
- """Read data in path
-
- :param path: Path to file read
- :type path: str
- :param get_lines: If read file line by line
- :type get_lines: bool
- :return: File content
- :rtype: str or list
- """
- if get_lines:
- with Path(path).open(encoding=encoding) as f:
- data = f.readlines()
- else:
- data = Path(path).read_text(encoding=encoding)
- return data
-
- @classmethod
- def read_bin(cls, path: str) -> bytes:
- """Read binary data in path
-
- :param path: Path to file read
- :type path: str
- :return: File content
- :rtype: bytes
- """
- data = Path(path).read_bytes()
- return data
-
- # ~ Import/export data
-
- @classmethod
- def from_json(cls, path: str) -> Any:
- """Read path file and load json data
-
- :param path: Path to file
- :type path: str
- :return: Any data
- :rtype: Any
- """
- data = json.loads(cls.read(path))
- return data
-
- @classmethod
- def to_json(cls, path: str, data: str):
- """Save data in path file like json
-
- :param path: Path to file
- :type path: str
- :return: True if save correctly
- :rtype: bool
- """
- data = json.dumps(data, indent=4, ensure_ascii=False, sort_keys=True)
- return cls.save(path, data)
-
- @classmethod
- def from_csv(cls, path: str, args: dict={}) -> tuple:
- """Read CSV
-
- :param path: Path to file csv
- :type path: str
- :param args: Any argument support for Python library
- :type args: dict
- :return: Data csv like tuple
- :rtype: tuple
-
- `See CSV Reader `_
- """
- with open(path) as f:
- rows = tuple(csv.reader(f, **args))
- return rows
-
- @classmethod
- def to_csv(cls, path: str, data: Any, args: dict={}):
- """Write CSV
-
- :param path: Path to file write csv
- :type path: str
- :param data: Data to write
- :type data: Iterable
- :param args: Any argument support for Python library
- :type args: dict
-
- `See CSV Writer `_
- """
- with open(path, 'w') as f:
- writer = csv.writer(f, **args)
- writer.writerows(data)
- return
-
- @classmethod
- def zip(cls, source: Union[str, tuple, list], target='') -> str:
- path_zip = target
- if not isinstance(source, (tuple, list)):
- path, _, name, _ = _P(source).info
- start = len(path) + 1
- if not target:
- path_zip = f'{path}/{name}.zip'
-
- if isinstance(source, (tuple, list)):
- files = [(f, f[len(_P(f).path)+1:]) for f in source]
- elif _P.is_file(source):
- files = ((source, source[start:]),)
- else:
- files = [(f, f[start:]) for f in _P.walk(source)]
-
- compression = zipfile.ZIP_DEFLATED
- with zipfile.ZipFile(path_zip, 'w', compression=compression) as z:
- for f in files:
- z.write(f[0], f[1])
- return path_zip
-
- @classmethod
- def unzip(cls, source, target='', members=None, pwd=None):
- path = target
- if not target:
- path = _P(source).path
- with zipfile.ZipFile(source) as z:
- if not pwd is None:
- pwd = pwd.encode()
- if isinstance(members, str):
- members = (members,)
- z.extractall(path, members=members, pwd=pwd)
- return
-
- @classmethod
- def zip_content(cls, path: str):
- with zipfile.ZipFile(path) as z:
- names = z.namelist()
- return names
-
- @classmethod
- def merge_zip(cls, target, zips):
- try:
- with zipfile.ZipFile(target, 'w', compression=zipfile.ZIP_DEFLATED) as t:
- for path in zips:
- with zipfile.ZipFile(path, compression=zipfile.ZIP_DEFLATED) as s:
- for name in s.namelist():
- t.writestr(name, s.open(name).read())
- except Exception as e:
- error(e)
- return False
-
- return True
-
- @classmethod
- def kill(cls, path: str):
- """Delete path
-
- :param path: Path to file or directory
- :type path: str
- :return: True if delete correctly
- :rtype: bool
- """
- p = Path(path)
- try:
- if p.is_file():
- p.unlink()
- elif p.is_dir():
- shutil.rmtree(path)
- result = True
- except OSError as e:
- log.error(e)
- result = False
-
- return result
-
- @classmethod
- def copy(cls, source: str, target: str='', name: str=''):
- p, f, n, e, _, _ = Paths(source).info
- if target:
- p = target
- e = f'.{e}'
- if name:
- e = ''
- n = name
- path_new = cls.join(p, f'{n}{e}')
- shutil.copy(source, path_new)
- return path_new
-
-
class Config(object):
"""Class for set and get configurations
"""
@@ -1329,158 +504,6 @@ class Url(object):
return cls._open(url, data, headers, verify, json, timeout, 'POST')
-class Email(object):
- """Class for send email
- """
- class _SmtpServer(object):
-
- def __init__(self, config):
- self._server = None
- self._error = ''
- self._sender = ''
- self._is_connect = self._login(config)
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- self.close()
-
- @property
- def is_connect(self):
- return self._is_connect
-
- @property
- def error(self):
- return self._error
-
- def _login(self, config):
- name = config['server']
- port = config['port']
- is_ssl = config['ssl']
- starttls = config.get('starttls', False)
- self._sender = config['user']
- try:
- if starttls:
- self._server = smtplib.SMTP(name, port, timeout=TIMEOUT)
- self._server.ehlo()
- self._server.starttls()
- self._server.ehlo()
- elif is_ssl:
- self._server = smtplib.SMTP_SSL(name, port, timeout=TIMEOUT)
- self._server.ehlo()
- else:
- self._server = smtplib.SMTP(name, port, timeout=TIMEOUT)
-
- self._server.login(self._sender, config['password'])
- msg = 'Connect to: {}'.format(name)
- debug(msg)
- return True
- except smtplib.SMTPAuthenticationError as e:
- if '535' in str(e):
- self._error = _('Incorrect user or password')
- return False
- if '534' in str(e) and 'gmail' in name:
- self._error = _('Allow less secure apps in GMail')
- return False
- except smtplib.SMTPException as e:
- self._error = str(e)
- return False
- except Exception as e:
- self._error = str(e)
- return False
- return False
-
- def _body(self, msg):
- body = msg.replace('\n', '
')
- return body
-
- def send(self, message):
- # ~ file_name = 'attachment; filename={}'
- email = MIMEMultipart()
- email['From'] = self._sender
- email['To'] = message['to']
- email['Cc'] = message.get('cc', '')
- email['Subject'] = message['subject']
- email['Date'] = formatdate(localtime=True)
- if message.get('confirm', False):
- email['Disposition-Notification-To'] = email['From']
- email.attach(MIMEText(self._body(message['body']), 'html'))
-
- paths = message.get('files', ())
- if isinstance(paths, str):
- paths = (paths,)
- for path in paths:
- fn = _P(path).file_name
- # ~ print('NAME', fn)
- part = MIMEBase('application', 'octet-stream')
- part.set_payload(_P.read_bin(path))
- encoders.encode_base64(part)
- part.add_header('Content-Disposition', f'attachment; filename="{fn}"')
- email.attach(part)
-
- receivers = (
- email['To'].split(',') +
- email['CC'].split(',') +
- message.get('bcc', '').split(','))
- try:
- self._server.sendmail(self._sender, receivers, email.as_string())
- msg = 'Email sent...'
- debug(msg)
- if message.get('path', ''):
- self.save_message(email, message['path'])
- return True
- except Exception as e:
- self._error = str(e)
- return False
- return False
-
- def save_message(self, email, path):
- mbox = mailbox.mbox(path, create=True)
- mbox.lock()
- try:
- msg = mailbox.mboxMessage(email)
- mbox.add(msg)
- mbox.flush()
- finally:
- mbox.unlock()
- return
-
- def close(self):
- try:
- self._server.quit()
- msg = 'Close connection...'
- debug(msg)
- except:
- pass
- return
-
- @classmethod
- def _send_email(cls, server, messages):
- with cls._SmtpServer(server) as server:
- if server.is_connect:
- for msg in messages:
- server.send(msg)
- else:
- error(server.error)
- return server.error
-
- @classmethod
- def send(cls, server: dict, messages: Union[dict, tuple, list]):
- """Send email with config server, emails send in thread.
-
- :param server: Configuration for send emails
- :type server: dict
- :param server: Dictionary con message or list of messages
- :type server: dict or iterator
- """
- if isinstance(messages, dict):
- messages = (messages,)
- t = threading.Thread(target=cls._send_email, args=(server, messages))
- t.start()
- return
-
-
class Color(object):
"""Class for colors
@@ -1737,23 +760,6 @@ class IOStream(object):
return cls.OutputStream()
-class EventsListenerBase(unohelper.Base, XEventListener):
-
- def __init__(self, controller, name, window=None):
- self._controller = controller
- self._name = name
- self._window = window
-
- @property
- def name(self):
- return self._name
-
- def disposing(self, event):
- self._controller = None
- if not self._window is None:
- self._window.setMenuBar(None)
-
-
class EventsRangeSelectionListener(EventsListenerBase, XRangeSelectionListener):
def __init__(self, controller):
@@ -3384,16 +2390,13 @@ class LODocIDE(LODocument):
def __getattr__(name):
classes = {
- 'dates': Dates,
'json': Json,
'macro': Macro,
'shell': Shell,
'timer': Timer,
'hash': Hash,
- 'path': Paths,
'config': Config,
'url': Url,
- 'email': Email,
'color': Color(),
'io': IOStream,
'clipboard': ClipBoard,
@@ -3402,7 +2405,6 @@ def __getattr__(name):
'lo': LOMain,
'command': LOMain.cmd,
'docs': LODocuments(),
- 'active': LODocuments().active,
}
if name in classes:
return classes[name]
diff --git a/source/easymacro/__init__.py b/source/easymacro/__init__.py
index d6dbcfa..8bf878e 100644
--- a/source/easymacro/__init__.py
+++ b/source/easymacro/__init__.py
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
from .easymain import *
+from .easydialog import *
from .easytools import *
from .easydocs import LODocuments
@@ -8,7 +9,11 @@ from .easydocs import LODocuments
def __getattr__(name):
classes = {
'active': LODocuments().active,
- 'inspect': LOInspect
+ 'dates': Dates,
+ 'dialog': LODialog,
+ 'email': Email,
+ 'inspect': LOInspect,
+ 'paths': Paths,
}
if name in classes:
diff --git a/source/easymacro/easydialog.py b/source/easymacro/easydialog.py
new file mode 100644
index 0000000..9be3578
--- /dev/null
+++ b/source/easymacro/easydialog.py
@@ -0,0 +1,741 @@
+#!/usr/bin/env python3
+
+from typing import Any
+
+from com.sun.star.view.SelectionType import SINGLE, MULTI, RANGE
+
+from .easyevents import *
+from .easydocs import LODocuments
+from .easymain import log, TITLE, create_instance, BaseObject
+from .easytools import Paths, Services, LOInspect
+from .easytools import _, set_properties
+
+
+__all__ = [
+ 'LODialog',
+ 'inputbox',
+]
+
+
+SEPARATION = 5
+MODELS = {
+ 'button': 'com.sun.star.awt.UnoControlButtonModel',
+ 'label': 'com.sun.star.awt.UnoControlFixedTextModel',
+ 'link': 'com.sun.star.awt.UnoControlFixedHyperlinkModel',
+ 'radio': 'com.sun.star.awt.UnoControlRadioButtonModel',
+ 'checkbox': 'com.sun.star.awt.UnoControlCheckBoxModel',
+ 'text': 'com.sun.star.awt.UnoControlEditModel',
+ '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',
+ 'grid': 'com.sun.star.awt.grid.UnoControlGridModel',
+ 'pages': 'com.sun.star.awt.UnoMultiPageModel',
+ 'groupbox': 'com.sun.star.awt.UnoControlGroupBoxModel',
+ 'combobox': 'com.sun.star.awt.UnoControlComboBoxModel',
+ 'spinbutton': 'com.sun.star.awt.UnoControlSpinButtonModel',
+ 'numeric': 'com.sun.star.awt.UnoControlNumericFieldModel',
+}
+
+TYPE_CONTROL = {
+ 'stardiv.Toolkit.UnoFixedTextControl': 'label',
+ 'stardiv.Toolkit.UnoEditControl': 'text',
+ 'stardiv.Toolkit.UnoButtonControl': 'button',
+}
+
+IMPLEMENTATIONS = {
+ 'grid': 'stardiv.Toolkit.GridControl',
+ 'link': 'stardiv.Toolkit.UnoFixedHyperlinkControl',
+ 'roadmap': 'stardiv.Toolkit.UnoRoadmapControl',
+ 'pages': 'stardiv.Toolkit.UnoMultiPageControl',
+}
+
+
+def add_listeners(events, control):
+ name = control.Model.Name
+ listeners = {
+ 'addActionListener': EventsButton,
+ # ~ 'addMouseListener': EventsMouse,
+ # ~ 'addFocusListener': EventsFocus,
+ # ~ 'addItemListener': EventsItem,
+ # ~ 'addKeyListener': EventsKey,
+ # ~ 'addTabListener': EventsTab,
+ # ~ 'addSpinListener': EventsSpin,
+ }
+ if hasattr(control, 'obj'):
+ control = control.obj
+ # ~ log.debug(control.ImplementationName)
+ is_grid = control.ImplementationName == IMPLEMENTATIONS['grid']
+ is_link = control.ImplementationName == IMPLEMENTATIONS['link']
+ is_roadmap = control.ImplementationName == IMPLEMENTATIONS['roadmap']
+ is_pages = control.ImplementationName == IMPLEMENTATIONS['pages']
+
+ for key, value in listeners.items():
+ if hasattr(control, key):
+ if is_grid and key == 'addMouseListener':
+ control.addMouseListener(EventsMouseGrid(events, name))
+ continue
+ if is_link and key == 'addMouseListener':
+ control.addMouseListener(EventsMouseLink(events, name))
+ continue
+ if is_roadmap and key == 'addItemListener':
+ control.addItemListener(EventsItemRoadmap(events, name))
+ continue
+
+ getattr(control, key)(listeners[key](events, name))
+
+ if is_grid:
+ controllers = EventsGrid(events, name)
+ control.addSelectionListener(controllers)
+ control.Model.GridDataModel.addGridDataListener(controllers)
+ return
+
+
+class UnoBaseObject(object):
+
+ def __init__(self, obj: Any):
+ self._obj = obj
+ self._model = obj.Model
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ pass
+
+ @property
+ def obj(self):
+ return self._obj
+
+ @property
+ def model(self):
+ return self._model
+ @property
+ def m(self):
+ return self._model
+
+ @property
+ def properties(self):
+ data = LOInspect(self.obj).properties
+ return data
+ @properties.setter
+ def properties(self, properties: dict):
+ set_properties(self.model, properties)
+
+ @property
+ def name(self):
+ return self.model.Name
+
+ @property
+ def parent(self):
+ return self.obj.Context
+
+ @property
+ def tag(self):
+ return self.model.Tag
+ @tag.setter
+ def tag(self, value):
+ self.model.Tag = value
+
+ @property
+ def visible(self):
+ return self.obj.Visible
+ @visible.setter
+ def visible(self, value):
+ self.obj.setVisible(value)
+
+ @property
+ def enabled(self):
+ return self.model.Enabled
+ @enabled.setter
+ def enabled(self, value):
+ self.model.Enabled = value
+
+ @property
+ def step(self):
+ return self.model.Step
+ @step.setter
+ def step(self, value):
+ self.model.Step = value
+
+ @property
+ def align(self):
+ return self.model.Align
+ @align.setter
+ def align(self, value):
+ self.model.Align = value
+
+ @property
+ def valign(self):
+ return self.model.VerticalAlign
+ @valign.setter
+ def valign(self, value):
+ self.model.VerticalAlign = value
+
+ @property
+ def font_weight(self):
+ return self.model.FontWeight
+ @font_weight.setter
+ def font_weight(self, value):
+ self.model.FontWeight = value
+
+ @property
+ def font_height(self):
+ return self.model.FontHeight
+ @font_height.setter
+ def font_height(self, value):
+ self.model.FontHeight = value
+
+ @property
+ def font_name(self):
+ return self.model.FontName
+ @font_name.setter
+ def font_name(self, value):
+ self.model.FontName = value
+
+ @property
+ def font_underline(self):
+ return self.model.FontUnderline
+ @font_underline.setter
+ def font_underline(self, value):
+ self.model.FontUnderline = value
+
+ @property
+ def text_color(self):
+ return self.model.TextColor
+ @text_color.setter
+ def text_color(self, value):
+ self.model.TextColor = value
+
+ @property
+ def back_color(self):
+ return self.model.BackgroundColor
+ @back_color.setter
+ def back_color(self, value):
+ self.model.BackgroundColor = value
+
+ @property
+ def multi_line(self):
+ return self.model.MultiLine
+ @multi_line.setter
+ def multi_line(self, value):
+ self.model.MultiLine = value
+
+ @property
+ def help_text(self):
+ return self.model.HelpText
+ @help_text.setter
+ def help_text(self, value):
+ self.model.HelpText = value
+
+ @property
+ def border(self):
+ return self.model.Border
+ @border.setter
+ def border(self, value):
+ # ~ Bug for report
+ self.model.Border = value
+
+ @property
+ def width(self):
+ return self._model.Width
+ @width.setter
+ def width(self, value):
+ self.model.Width = value
+
+ @property
+ def height(self):
+ return self.model.Height
+ @height.setter
+ def height(self, value):
+ self.model.Height = value
+
+ def _get_possize(self, name):
+ ps = self.obj.getPosSize()
+ return getattr(ps, name)
+
+ def _set_possize(self, name, value):
+ ps = self.obj.getPosSize()
+ setattr(ps, name, value)
+ self.obj.setPosSize(ps.X, ps.Y, ps.Width, ps.Height, POSSIZE)
+ return
+
+ @property
+ def x(self):
+ if hasattr(self.model, 'PositionX'):
+ return self.model.PositionX
+ return self._get_possize('X')
+ @x.setter
+ def x(self, value):
+ if hasattr(self.model, 'PositionX'):
+ self.model.PositionX = value
+ else:
+ self._set_possize('X', value)
+
+ @property
+ def y(self):
+ if hasattr(self.model, 'PositionY'):
+ return self.model.PositionY
+ return self._get_possize('Y')
+ @y.setter
+ def y(self, value):
+ if hasattr(self.model, 'PositionY'):
+ self.model.PositionY = value
+ else:
+ self._set_possize('Y', value)
+
+ @property
+ def tab_index(self):
+ return self._model.TabIndex
+ @tab_index.setter
+ def tab_index(self, value):
+ self.model.TabIndex = value
+
+ @property
+ def tab_stop(self):
+ return self._model.Tabstop
+ @tab_stop.setter
+ def tab_stop(self, value):
+ self.model.Tabstop = value
+
+ @property
+ def ps(self):
+ ps = self.obj.getPosSize()
+ return ps
+ @ps.setter
+ def ps(self, ps):
+ self.obj.setPosSize(ps.X, ps.Y, ps.Width, ps.Height, POSSIZE)
+
+ def set_focus(self):
+ self.obj.setFocus()
+ return
+
+ def ps_from(self, source):
+ self.ps = source.ps
+ return
+
+ def center(self, horizontal=True, vertical=False):
+ p = self.parent.Model
+ w = p.Width
+ h = p.Height
+ if horizontal:
+ x = w / 2 - self.width / 2
+ self.x = x
+ if vertical:
+ y = h / 2 - self.height / 2
+ self.y = y
+ return
+
+ def move(self, origin, x=0, y=5, center=False):
+ if x:
+ self.x = origin.x + origin.width + x
+ else:
+ self.x = origin.x
+ if y:
+ h = origin.height
+ if y < 0:
+ h = 0
+ self.y = origin.y + h + y
+ else:
+ self.y = origin.y
+
+ if center:
+ self.center()
+ return
+
+
+class UnoLabel(UnoBaseObject):
+
+ def __init__(self, obj):
+ super().__init__(obj)
+
+ @property
+ def type(self):
+ return 'label'
+
+ @property
+ def value(self):
+ return self.model.Label
+ @value.setter
+ def value(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):
+
+ def __init__(self, obj):
+ super().__init__(obj)
+
+ @property
+ def type(self):
+ return 'text'
+
+ @property
+ def value(self):
+ return self.model.Text
+ @value.setter
+ def value(self, value):
+ self.model.Text = value
+
+ @property
+ def echochar(self):
+ return chr(self.model.EchoChar)
+ @echochar.setter
+ def echochar(self, value):
+ if value:
+ self.model.EchoChar = ord(value[0])
+ else:
+ self.model.EchoChar = 0
+
+ def validate(self):
+ return
+
+
+class UnoButton(UnoBaseObject):
+
+ def __init__(self, obj):
+ super().__init__(obj)
+
+ @property
+ def type(self):
+ return 'button'
+
+ @property
+ def value(self):
+ return self.model.Label
+ @value.setter
+ def value(self, value):
+ self.model.Label = value
+
+ @property
+ def image(self):
+ return self.model.ImageURL
+ @image.setter
+ def image(self, value):
+ self.model.ImageURL = Paths.to_url(value)
+
+
+UNO_CLASSES = {
+ 'label': UnoLabel,
+ 'text': UnoText,
+ 'button': UnoButton,
+ # ~ 'link': UnoLabelLink,
+ # ~ 'radio': UnoRadio,
+ # ~ 'checkbox': UnoCheckBox,
+ # ~ 'image': UnoImage,
+ # ~ 'listbox': UnoListBox,
+ # ~ 'roadmap': UnoRoadmap,
+ # ~ 'tree': UnoTree,
+ # ~ 'grid': UnoGrid,
+ # ~ 'pages': UnoPages,
+ # ~ 'spinbutton': UnoSpinButton,
+ # ~ 'numeric': UnoNumericField,
+}
+
+
+class DialogBox(BaseObject):
+ SERVICE = 'com.sun.star.awt.DialogProvider'
+ SERVICE_DIALOG = 'com.sun.star.awt.UnoControlDialog'
+
+ def __init__(self, properties: dict={}):
+ self._controls = {}
+ obj = self._create(properties)
+ super().__init__(obj)
+ self._init_controls()
+
+ def _create_from_path(self, path: str):
+ dp = create_instance(self.SERVICE, True)
+ dialog = dp.createDialog(Paths.to_url(path))
+ return dialog
+
+ def _create_from_location(self, properties: dict):
+ # ~ uid = docs.active.uid
+ # ~ url = f'vnd.sun.star.tdoc:/{uid}/Dialogs/{library}/{name}.xml'
+
+ 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
+ dp = create_instance(self.SERVICE, arguments=doc)
+ else:
+ dp = create_instance(self.SERVICE, True)
+
+ dialog = dp.createDialog(url)
+ return dialog
+
+ def _create_from_properties(self, properties: dict):
+ dialog = create_instance(self.SERVICE_DIALOG, True)
+ model = create_instance(f'{self.SERVICE_DIALOG}Model', True)
+
+ properties['Width'] = properties.get('Width', 200)
+ properties['Height'] = properties.get('Height', 150)
+
+ set_properties(model, properties)
+ dialog.setModel(model)
+ dialog.setVisible(False)
+ dialog.createPeer(Services.toolkit, None)
+ return dialog
+
+ def _create(self, properties: dict):
+ path = properties.pop('Path', '')
+ if path:
+ dialog = self._create_from_path(path)
+ elif 'Location' in properties:
+ dialog = self._create_from_location(properties)
+ else:
+ dialog = self._create_from_properties(properties)
+ return dialog
+
+ def _init_controls(self):
+ for control in self.obj.Controls:
+ tipo = control.ImplementationName
+ name = control.Model.Name
+ if not tipo in TYPE_CONTROL:
+ log.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):
+ return self.execute()
+
+ def close(self, value=0):
+ 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
+
+ @property
+ def name(self):
+ return self.model.Name
+
+ @property
+ def height(self):
+ return self.model.Height
+ @height.setter
+ def height(self, value):
+ self.model.Height = value
+
+ @property
+ def width(self):
+ return self.model.Width
+ @width.setter
+ def width(self, value):
+ self.model.Width = value
+
+ @property
+ def visible(self):
+ return self.obj.Visible
+ @visible.setter
+ def visible(self, value):
+ self.obj.Visible = value
+
+ @property
+ def step(self):
+ return self.model.Step
+ @step.setter
+ def step(self, value):
+ self.model.Step = value
+
+ @property
+ def color_on_focus(self):
+ return self._color_on_focus
+ @color_on_focus.setter
+ def color_on_focus(self, value):
+ self._color_on_focus = get_color(value)
+
+ @property
+ def events(self):
+ return self._events
+ @events.setter
+ def events(self, controllers):
+ self._events = controllers(self)
+ self._connect_listeners()
+
+ def _connect_listeners(self):
+ for control in self.obj.Controls:
+ add_listeners(self.events, control)
+ return
+
+ def _set_image_url(self, image: str):
+ if Paths.exists(image):
+ return Paths.to_url(image)
+
+ path = Paths.join(self._path, DIR['images'], image)
+ return Paths.to_url(path)
+
+ def _special_properties(self, tipo, properties):
+ if tipo == 'link' and not 'Label' in properties:
+ properties['Label'] = properties['URL']
+ return properties
+
+ if tipo == 'button':
+ if 'ImageURL' in properties:
+ properties['ImageURL'] = self._set_image_url(properties['ImageURL'])
+ properties['FocusOnClick'] = properties.get('FocusOnClick', False)
+ return properties
+
+ if tipo == 'roadmap':
+ properties['Height'] = properties.get('Height', self.height)
+ if 'Title' in properties:
+ properties['Text'] = properties.pop('Title')
+ return properties
+
+ if tipo == 'tree':
+ properties['SelectionType'] = properties.get('SelectionType', SINGLE)
+ return properties
+
+ if tipo == 'grid':
+ properties['X'] = properties.get('X', SEPARATION)
+ properties['Y'] = properties.get('Y', SEPARATION)
+ properties['Width'] = properties.get('Width', self.width - SEPARATION * 2)
+ properties['Height'] = properties.get('Height', self.height - SEPARATION * 2)
+ properties['ShowRowHeader'] = properties.get('ShowRowHeader', True)
+ return properties
+
+ if tipo == 'pages':
+ properties['Width'] = properties.get('Width', self.width)
+ properties['Height'] = properties.get('Height', self.height)
+
+ return properties
+
+ def add_control(self, properties):
+ tipo = properties.pop('Type').lower()
+ root = properties.pop('Root', '')
+ sheets = properties.pop('Sheets', ())
+ columns = properties.pop('Columns', ())
+
+ properties = self._special_properties(tipo, properties)
+ model = self.model.createInstance(MODELS[tipo])
+
+ set_properties(model, properties)
+ name = properties['Name']
+ self.model.insertByName(name, model)
+ control = self.obj.getControl(name)
+ add_listeners(self.events, control)
+ control = UNO_CLASSES[tipo](control)
+
+ if tipo in ('listbox',):
+ control.path = self.path
+
+ if tipo == 'tree' and root:
+ control.root = root
+ elif tipo == 'grid' and columns:
+ control.columns = columns
+ elif tipo == 'pages' and sheets:
+ control.sheets = sheets
+ control.events = self.events
+
+ setattr(self, name, control)
+ self._controls[name] = control
+ return control
+
+
+class LODialog():
+ @classmethod
+ def create(cls, properties: dict={}):
+ return DialogBox(properties)
+
+
+def inputbox(message, default='', title=TITLE, echochar=''):
+
+ class ControllersInput(object):
+
+ def __init__(self, dialog):
+ self.d = dialog
+
+ def cmd_ok_action(self, event):
+ self.d.close(1)
+ return
+
+ properties = {
+ 'Title': title,
+ 'Width': 200,
+ 'Height': 80,
+ }
+ dlg = DialogBox(properties)
+ dlg.events = ControllersInput
+
+ properties = {
+ 'Type': 'Label',
+ 'Name': 'lbl_msg',
+ 'Label': message,
+ 'Width': 140,
+ 'Height': 50,
+ 'X': 5,
+ 'Y': 5,
+ 'MultiLine': True,
+ 'Border': 1,
+ }
+ dlg.add_control(properties)
+
+ properties = {
+ 'Type': 'Text',
+ 'Name': 'txt_value',
+ 'Text': default,
+ 'Width': 190,
+ 'Height': 15,
+ }
+ if echochar:
+ properties['EchoChar'] = ord(echochar[0])
+ dlg.add_control(properties)
+ dlg.txt_value.move(dlg.lbl_msg)
+
+ properties = {
+ 'Type': 'button',
+ 'Name': 'cmd_ok',
+ 'Label': _('OK'),
+ 'Width': 40,
+ 'Height': 15,
+ 'DefaultButton': True,
+ 'PushButtonType': 1,
+ }
+ dlg.add_control(properties)
+ dlg.cmd_ok.move(dlg.lbl_msg, 10, 0)
+
+ properties = {
+ 'Type': 'button',
+ 'Name': 'cmd_cancel',
+ 'Label': _('Cancel'),
+ 'Width': 40,
+ 'Height': 15,
+ 'PushButtonType': 2,
+ }
+ dlg.add_control(properties)
+ dlg.cmd_cancel.move(dlg.cmd_ok)
+
+ value = ''
+ if dlg.open():
+ value = dlg.txt_value.value
+
+ return value
diff --git a/source/easymacro/easyevents.py b/source/easymacro/easyevents.py
new file mode 100644
index 0000000..e660d6f
--- /dev/null
+++ b/source/easymacro/easyevents.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python3
+
+import unohelper
+
+from com.sun.star.awt import XActionListener
+from com.sun.star.lang import XEventListener
+
+
+__all__ = [
+ 'EventsButton',
+]
+
+
+class EventsListenerBase(unohelper.Base, XEventListener):
+
+ def __init__(self, controller, name, window=None):
+ self._controller = controller
+ self._name = name
+ self._window = window
+
+ @property
+ def name(self):
+ return self._name
+
+ def disposing(self, event):
+ self._controller = None
+ if not self._window is None:
+ self._window.setMenuBar(None)
+
+
+class EventsButton(EventsListenerBase, XActionListener):
+
+ def __init__(self, controller, name):
+ super().__init__(controller, name)
+
+ def actionPerformed(self, event):
+ event_name = f'{self.name}_action'
+ if hasattr(self._controller, event_name):
+ getattr(self._controller, event_name)(event)
+ return
diff --git a/source/easymacro/easymain.py b/source/easymacro/easymain.py
index 590e8c8..b643805 100644
--- a/source/easymacro/easymain.py
+++ b/source/easymacro/easymain.py
@@ -57,7 +57,7 @@ logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT, datefmt=LOG_DATE)
log = logging.getLogger(__name__)
-def create_instance(name: str, with_context: bool=False, argument: Any=None) -> Any:
+def create_instance(name: str, with_context: bool=False, arguments: Any=None) -> Any:
"""Create a service instance
:param name: Name of service
@@ -72,8 +72,8 @@ def create_instance(name: str, with_context: bool=False, argument: Any=None) ->
if with_context:
instance = SM.createInstanceWithContext(name, CTX)
- elif argument:
- instance = SM.createInstanceWithArguments(name, (argument,))
+ elif arguments:
+ instance = SM.createInstanceWithArguments(name, (arguments,))
else:
instance = SM.createInstance(name)
@@ -155,3 +155,4 @@ class BaseObject():
def obj(self):
"""Return original pyUno object"""
return self._obj
+
diff --git a/source/easymacro/easytools.py b/source/easymacro/easytools.py
index d8a217c..59410cd 100644
--- a/source/easymacro/easytools.py
+++ b/source/easymacro/easytools.py
@@ -1,23 +1,51 @@
#!/usr/bin/env python3
+import csv
import datetime
+import json
+import os
+import re
+import shutil
+import subprocess
+import sys
+import tempfile
+import time
from functools import wraps
-from typing import Any
+from pathlib import Path
from pprint import pprint
+from typing import Any, Union
+import mailbox
+import smtplib
+from smtplib import SMTPException, SMTPAuthenticationError
+from email.mime.multipart import MIMEMultipart
+from email.mime.base import MIMEBase
+from email.mime.text import MIMEText
+from email.utils import formatdate
+from email import encoders
+
+import uno
from com.sun.star.awt import MessageBoxButtons
from com.sun.star.awt.MessageBoxResults import YES
from com.sun.star.beans import PropertyValue
from com.sun.star.beans.PropertyConcept import ALL
+from com.sun.star.ui.dialogs import TemplateDescription
-from .easymain import IS_WIN, TITLE, log, create_instance
+from .easymain import (
+ CTX, DATE_OFFSET, IS_MAC, IS_WIN, LANG, TITLE,
+ log, create_instance
+ )
from .easyuno import MessageBoxType
from .easydocs import LODocuments
+from .messages import MESSAGES
__all__ = [
+ 'Dates',
+ 'Email',
'LOInspect',
+ 'Paths',
'catch_exception',
'debug',
'error',
@@ -25,9 +53,25 @@ __all__ = [
'mri',
'msgbox',
'save_log',
+ 'sleep',
]
+PYTHON = 'python'
+if IS_WIN:
+ PYTHON = 'python.exe'
+
+
+def _(msg):
+ if LANG == 'en':
+ return msg
+
+ if not LANG in MESSAGES:
+ return msg
+
+ return MESSAGES[LANG][msg]
+
+
def debug(*messages) -> None:
"""Show messages debug
@@ -223,6 +267,46 @@ def errorbox(message: Any, title: str=TITLE) -> int:
return msgbox(message, title, type_message_box=MessageBoxType.ERRORBOX)
+def sleep(seconds: int):
+ """Sleep
+ """
+ time.sleep(seconds)
+ return
+
+
+def set_properties(model, properties):
+ if 'X' in properties:
+ properties['PositionX'] = properties.pop('X')
+ if 'Y' in properties:
+ properties['PositionY'] = properties.pop('Y')
+ keys = tuple(properties.keys())
+ values = tuple(properties.values())
+ model.setPropertyValues(keys, values)
+ return
+
+
+# ~ https://github.com/django/django/blob/main/django/utils/functional.py#L61
+class classproperty:
+
+ def __init__(self, method=None):
+ self.fget = method
+
+ def __get__(self, instance, cls=None):
+ return self.fget(cls)
+
+ def getter(self, method):
+ self.fget = method
+ return self
+
+
+class Services():
+
+ @classproperty
+ def toolkit(cls):
+ instance = create_instance('com.sun.star.awt.Toolkit', True)
+ return instance
+
+
class LOInspect():
"""Class inspect
@@ -386,11 +470,937 @@ class Dates(object):
"""
_start = None
- @classmethod
+ @classproperty
def now(cls):
"""Current local date and time
- :return: Return the current local date and time, remove microsecond
+ :return: Return the current local date and time, remove microseconds.
:rtype: datetime
"""
return datetime.datetime.now().replace(microsecond=0)
+
+ @classproperty
+ def today(cls):
+ """Current local date
+
+ :return: Return the current local date
+ :rtype: date
+ """
+ return datetime.date.today()
+
+ @classproperty
+ def epoch(cls):
+ """Get unix time
+
+ :return: Return unix time
+ :rtype: int
+
+ `See Unix Time `_
+ """
+ n = cls.now
+ e = int(time.mktime(n.timetuple()))
+ return e
+
+ @classmethod
+ def date(cls, year: int, month: int, day: int):
+ """Get date from year, month, day
+
+ :param year: Year of date
+ :type year: int
+ :param month: Month of date
+ :type month: int
+ :param day: Day of day
+ :type day: int
+ :return: Return the date
+ :rtype: date
+
+ `See Python date `_
+ """
+ d = datetime.date(year, month, day)
+ return d
+
+ @classmethod
+ def time(cls, hours: int, minutes: int, seconds: int):
+ """Get time from hour, minutes, seconds
+
+ :param hours: Hours of time
+ :type hours: int
+ :param minutes: Minutes of time
+ :type minutes: int
+ :param seconds: Seconds of time
+ :type seconds: int
+ :return: Return the time
+ :rtype: datetime.time
+ """
+ t = datetime.time(hours, minutes, seconds)
+ return t
+
+ @classmethod
+ def datetime(cls, year: int, month: int, day: int, hours: int, minutes: int, seconds: int):
+ """Get datetime from year, month, day, hours, minutes and seconds
+
+ :param year: Year of date
+ :type year: int
+ :param month: Month of date
+ :type month: int
+ :param day: Day of day
+ :type day: int
+ :param hours: Hours of time
+ :type hours: int
+ :param minutes: Minutes of time
+ :type minutes: int
+ :param seconds: Seconds of time
+ :type seconds: int
+ :return: Return datetime
+ :rtype: datetime
+ """
+ dt = datetime.datetime(year, month, day, hours, minutes, seconds)
+ return dt
+
+ @classmethod
+ def str_to_date(cls, str_date: str, template: str, to_calc: bool=False):
+ """Get date from string
+
+ :param str_date: Date in string
+ :type str_date: str
+ :param template: Formato of date string
+ :type template: str
+ :param to_calc: If date is for used in Calc cell
+ :type to_calc: bool
+ :return: Return date or int if used in Calc
+ :rtype: date or int
+
+ `See Python strptime `_
+ """
+ d = datetime.datetime.strptime(str_date, template).date()
+ if to_calc:
+ d = d.toordinal() - DATE_OFFSET
+ return d
+
+ @classmethod
+ def calc_to_date(cls, value: float):
+ """Get date from calc value
+
+ :param value: Float value from cell
+ :type value: float
+ :return: Return the current local date
+ :rtype: date
+
+ `See Python fromordinal `_
+ """
+ d = datetime.date.fromordinal(int(value) + DATE_OFFSET)
+ return d
+
+ @classmethod
+ def start(cls):
+ """Start counter
+ """
+ cls._start = cls.now
+ info('Start: ', cls._start)
+ return
+
+ @classmethod
+ def end(cls, get_seconds: bool=True):
+ """End counter
+
+ :param get_seconds: If return value in total seconds
+ :type get_seconds: bool
+ :return: Return the timedelta or total seconds
+ :rtype: timedelta or int
+ """
+ e = cls.now
+ td = e - cls._start
+ result = str(td)
+ if get_seconds:
+ result = td.total_seconds()
+ info('End: ', e)
+ return result
+
+
+class Paths(object):
+ """Class for paths
+ """
+ FILE_PICKER = 'com.sun.star.ui.dialogs.FilePicker'
+ FOLDER_PICKER = 'com.sun.star.ui.dialogs.FolderPicker'
+ REMOTE_FILE_PICKER = 'com.sun.star.ui.dialogs.RemoteFilePicker'
+ OFFICE_FILE_PICKER = 'com.sun.star.ui.dialogs.OfficeFilePicker'
+
+ def __init__(self, path=''):
+ if path.startswith('file://'):
+ path = str(Path(uno.fileUrlToSystemPath(path)).resolve())
+ self._path = Path(path)
+
+ @property
+ def path(self):
+ """Get base path"""
+ return str(self._path.parent)
+
+ @property
+ def file_name(self):
+ """Get file name"""
+ return self._path.name
+
+ @property
+ def name(self):
+ """Get name"""
+ return self._path.stem
+
+ @property
+ def ext(self):
+ """Get extension"""
+ return self._path.suffix[1:]
+
+ @property
+ def size(self):
+ """Get size"""
+ return self._path.stat().st_size
+
+ @property
+ def url(self):
+ """Get like URL"""
+ return self._path.as_uri()
+
+ @property
+ def info(self):
+ """Get all info like tuple"""
+ i = (self.path, self.file_name, self.name, self.ext, self.size, self.url)
+ return i
+
+ @property
+ def dict(self):
+ """Get all info like dict"""
+ data = {
+ 'path': self.path,
+ 'file_name': self.file_name,
+ 'name': self.name,
+ 'ext': self.ext,
+ 'size': self.size,
+ 'url': self.url,
+ }
+ return data
+
+ @classproperty
+ def home(self):
+ """Get user home"""
+ return str(Path.home())
+
+ @classproperty
+ def documents(self):
+ """Get user save documents"""
+ return self.config()
+
+ @classproperty
+ def user_profile(self):
+ """Get path user profile"""
+ path = self.config('UserConfig')
+ path = str(Path(path).parent)
+ return path
+
+ @classproperty
+ def user_config(self):
+ """Get path config in user profile"""
+ path = self.config('UserConfig')
+ return path
+
+ @classproperty
+ def python(self):
+ """Get path executable python"""
+ if IS_WIN:
+ path = self.join(self.config('Module'), PYTHON)
+ elif IS_MAC:
+ path = self.join(self.config('Module'), '..', 'Resources', PYTHON)
+ else:
+ path = sys.executable
+ return path
+
+ @classmethod
+ def to_url(cls, path: str) -> str:
+ """Convert paths in format system to URL
+
+ :param path: Path to convert
+ :type path: str
+ :return: Path in URL
+ :rtype: str
+ """
+ if not path.startswith('file://'):
+ path = Path(path).as_uri()
+ return path
+
+ @classmethod
+ def to_system(cls, path:str) -> str:
+ """Convert paths in URL to system
+
+ :param path: Path to convert
+ :type path: str
+ :return: Path system format
+ :rtype: str
+ """
+ if path.startswith('file://'):
+ path = str(Path(uno.fileUrlToSystemPath(path)).resolve())
+ return path
+
+ @classmethod
+ def config(cls, name: str='Work') -> Union[str, list]:
+ """Return path from config
+
+ :param name: Name in service PathSettings, default get path documents
+ :type name: str
+ :return: Path in config, if exists.
+ :rtype: str or list
+
+ `See Api XPathSettings `_
+ """
+ path = create_instance('com.sun.star.util.PathSettings')
+ path = cls.to_system(getattr(path, name)).split(';')
+ if len(path) == 1:
+ path = path[0]
+ return path
+
+ @classmethod
+ def join(cls, *paths: str) -> str:
+ """Join paths
+
+ :param paths: Paths to join
+ :type paths: list
+ :return: New path with joins
+ :rtype: str
+ """
+ path = str(Path(paths[0]).joinpath(*paths[1:]))
+ return path
+
+ @classmethod
+ def exists(cls, path: str) -> bool:
+ """If exists path
+
+ :param path: Path for validate
+ :type path: str
+ :return: True if path exists, False if not.
+ :rtype: bool
+ """
+ path = cls.to_system(path)
+ result = Path(path).exists()
+ return result
+
+ @classmethod
+ def exists_app(cls, name_app: str) -> bool:
+ """If exists app in system
+
+ :param name_app: Name of application
+ :type name_app: str
+ :return: True if app exists, False if not.
+ :rtype: bool
+ """
+ result = bool(shutil.which(name_app))
+ return result
+
+ @classmethod
+ def is_dir(cls, path: str):
+ """Validate if path is directory
+
+ :param path: Path for validate
+ :type path: str
+ :return: True if path is directory, False if not.
+ :rtype: bool
+ """
+ return Path(path).is_dir()
+
+ @classmethod
+ def is_file(cls, path: str):
+ """Validate if path is a file
+
+ :param path: Path for validate
+ :type path: str
+ :return: True if path is a file, False if not.
+ :rtype: bool
+ """
+ return Path(path).is_file()
+
+ @classmethod
+ def temp_file(self):
+ """Make temporary file"""
+ return tempfile.NamedTemporaryFile(mode='w')
+
+ @classmethod
+ def temp_dir(self):
+ """Make temporary directory"""
+ return tempfile.TemporaryDirectory(ignore_cleanup_errors=True)
+
+ @classmethod
+ def get(cls, init_dir: str='', filters: str='', multiple: bool=False) -> Union[str, list]:
+ """Get path for open
+
+ :param init_dir: Initial default path
+ :type init_dir: str
+ :param filters: Filter for show type files: 'xml' or 'txt,xml'
+ :type filters: str
+ :param multiple: If user can selected multiple files
+ :type multiple: bool
+ :return: Selected path or paths
+ :rtype: str or list
+
+ `See API `_
+ """
+ if not init_dir:
+ init_dir = cls.documents
+ init_dir = cls.to_url(init_dir)
+ file_picker = create_instance(cls.FILE_PICKER)
+ file_picker.setTitle(_('Select path'))
+ file_picker.setDisplayDirectory(init_dir)
+ file_picker.initialize((TemplateDescription.FILEOPEN_SIMPLE,))
+ file_picker.setMultiSelectionMode(multiple)
+ if filters:
+ for f in filters.split(','):
+ file_picker.appendFilter(f.upper(), f'*.{f.lower()}')
+
+ if file_picker.execute():
+ paths = [cls.to_system(p) for p in file_picker.getSelectedFiles()]
+ if not multiple:
+ paths = paths[0]
+ return paths
+
+ @classmethod
+ def get_dir(cls, init_dir: str='') -> str:
+ """Get path dir
+
+ :param init_dir: Initial default path
+ :type init_dir: str
+ :return: Selected path
+ :rtype: str
+ """
+ folder_picker = create_instance(cls.FOLDER_PICKER)
+ if not init_dir:
+ init_dir = cls.documents
+ init_dir = cls.to_url(init_dir)
+ folder_picker.setTitle(_('Select directory'))
+ folder_picker.setDisplayDirectory(init_dir)
+
+ path = ''
+ if folder_picker.execute():
+ path = cls.to_system(folder_picker.getDirectory())
+ return path
+
+ @classmethod
+ def get_for_save(cls, init_dir: str='', filters: str=''):
+ """Get path for save
+
+ :param init_dir: Initial default path
+ :type init_dir: str
+ :param filters: Filter for show type files: 'xml' or 'txt,xml'
+ :type filters: str
+ :return: Selected path
+ :rtype: str
+ """
+ if not init_dir:
+ init_dir = cls.documents
+ init_dir = cls.to_url(init_dir)
+
+ file_picker = create_instance(cls.FILE_PICKER)
+ file_picker.setTitle(_('Select file'))
+ file_picker.setDisplayDirectory(init_dir)
+ file_picker.initialize((TemplateDescription.FILESAVE_SIMPLE,))
+
+ if filters:
+ for f in filters.split(','):
+ file_picker.appendFilter(f.upper(), f'*.{f.lower()}')
+
+ path = ''
+ if file_picker.execute():
+ files = file_picker.getSelectedFiles()
+ path = [cls.to_system(f) for f in files][0]
+ return path
+
+ @classmethod
+ def files(cls, path: str, pattern: str='*'):
+ """Get all files in path
+
+ :param path: Path with files
+ :type path: str
+ :param pattern: For filter files, default get all.
+ :type pattern: str
+ :return: Files in path
+ :rtype: list
+ """
+ files = [str(p) for p in Path(path).glob(pattern) if p.is_file()]
+ return files
+
+ @classmethod
+ def walk(cls, path, filters=''):
+ """Get all files in path recursively
+
+ :param path: Path with files
+ :type path: str
+ :param filters: For filter files, default get all.
+ :type filters: str
+ :return: Files in path
+ :rtype: list
+ """
+ paths = []
+ for folder, _, files in os.walk(path):
+ if filters:
+ pattern = re.compile(r'\.(?:{})$'.format(filters), re.IGNORECASE)
+ paths += [cls.join(folder, f) for f in files if pattern.search(f)]
+ else:
+ paths += [cls.join(folder, f) for f in files]
+ return paths
+
+ @classmethod
+ def dirs(cls, path):
+ """Get directories in path
+
+ :param path: Path to scan
+ :type path: str
+ :return: Directories in path
+ :rtype: list
+ """
+ dirs = [str(p) for p in Path(path).iterdir() if p.is_dir()]
+ return dirs
+
+ @classmethod
+ def walk_dirs(cls, path, tree=False):
+ """Get directories recursively
+
+ :param path: Path to scan
+ :type path: str
+ :param tree: get info in a tuple (ID_FOLDER, ID_PARENT, NAME)
+ :type tree: bool
+ :return: Directories in path
+ :rtype: list
+ """
+ folders = []
+ if tree:
+ i = 0
+ parents = {path: 0}
+ for root, dirs, _ in os.walk(path):
+ for name in dirs:
+ i += 1
+ rn = cls.join(root, name)
+ if not rn in parents:
+ parents[rn] = i
+ folders.append((i, parents[root], name))
+ else:
+ for root, dirs, _ in os.walk(path):
+ folders += [cls.join(root, name) for name in dirs]
+ return folders
+
+ @classmethod
+ def extension(cls, id_ext: str):
+ """Get path extension install from id
+
+ :param id_ext: ID extension
+ :type id_ext: str
+ :return: Path extension
+ :rtype: str
+ """
+ pip = CTX.getValueByName('/singletons/com.sun.star.deployment.PackageInformationProvider')
+ path = Paths.to_system(pip.getPackageLocation(id_ext))
+ return path
+
+ @classmethod
+ def replace_ext(cls, path: str, new_ext: str):
+ """Replace extension in file path
+
+ :param path: Path to file
+ :type path: str
+ :param new_ext: New extension
+ :type new_ext: str
+ :return: Path with new extension
+ :rtype: str
+ """
+ p = Paths(path)
+ name = f'{p.name}.{new_ext}'
+ path = cls.join(p.path, name)
+ return path
+
+ @classmethod
+ def open(cls, path: str):
+ """Open any file with default program in system
+
+ :param path: Path to file
+ :type path: str
+ :return: PID file, only Linux
+ :rtype: int
+ """
+ pid = 0
+ if IS_WIN:
+ os.startfile(path)
+ else:
+ pid = subprocess.Popen(['xdg-open', path]).pid
+ return pid
+
+ # ~ Save/read data
+
+ @classmethod
+ def save(cls, path: str, data: str, encoding: str='utf-8') -> bool:
+ """Save data in path with encoding
+
+ :param path: Path to file save
+ :type path: str
+ :param data: Data to save
+ :type data: str
+ :param encoding: Encoding for save data, default utf-8
+ :type encoding: str
+ :return: True, if save corrrectly
+ :rtype: bool
+ """
+ result = bool(Path(path).write_text(data, encoding=encoding))
+ return result
+
+ @classmethod
+ def save_bin(cls, path: str, data: bytes) -> bool:
+ """Save binary data in path
+
+ :param path: Path to file save
+ :type path: str
+ :param data: Data to save
+ :type data: bytes
+ :return: True, if save corrrectly
+ :rtype: bool
+ """
+ result = bool(Path(path).write_bytes(data))
+ return result
+
+ @classmethod
+ def read(cls, path: str, get_lines: bool=False, encoding: str='utf-8') -> Union[str, list]:
+ """Read data in path
+
+ :param path: Path to file read
+ :type path: str
+ :param get_lines: If read file line by line
+ :type get_lines: bool
+ :return: File content
+ :rtype: str or list
+ """
+ if get_lines:
+ with Path(path).open(encoding=encoding) as f:
+ data = f.readlines()
+ else:
+ data = Path(path).read_text(encoding=encoding)
+ return data
+
+ @classmethod
+ def read_bin(cls, path: str) -> bytes:
+ """Read binary data in path
+
+ :param path: Path to file read
+ :type path: str
+ :return: File content
+ :rtype: bytes
+ """
+ data = Path(path).read_bytes()
+ return data
+
+ # ~ Import/export data
+
+ @classmethod
+ def save_json(cls, path: str, data: str):
+ """Save data in path file like json
+
+ :param path: Path to file
+ :type path: str
+ :return: True if save correctly
+ :rtype: bool
+ """
+ data = json.dumps(data, indent=4, ensure_ascii=False, sort_keys=True)
+ return cls.save(path, data)
+
+ @classmethod
+ def read_json(cls, path: str) -> Any:
+ """Read path file and load json data
+
+ :param path: Path to file
+ :type path: str
+ :return: Any data
+ :rtype: Any
+ """
+ data = json.loads(cls.read(path))
+ return data
+
+ @classmethod
+ def save_csv(cls, path: str, data: Any, args: dict={}):
+ """Write CSV
+
+ :param path: Path to file write csv
+ :type path: str
+ :param data: Data to write
+ :type data: Iterable
+ :param args: Any argument support for Python library
+ :type args: dict
+
+ `See CSV Writer `_
+ """
+ with open(path, 'w') as f:
+ writer = csv.writer(f, **args)
+ writer.writerows(data)
+ return
+
+ @classmethod
+ def read_csv(cls, path: str, args: dict={}) -> list:
+ """Read CSV
+
+ :param path: Path to file csv
+ :type path: str
+ :param args: Any argument support for Python library
+ :type args: dict
+ :return: Data csv like tuple
+ :rtype: tuple
+
+ `See CSV Reader `_
+ """
+ with open(path) as f:
+ rows = list(csv.reader(f, **args))
+ return rows
+
+ @classmethod
+ def kill(cls, path: str) -> bool:
+ """Delete path
+
+ :param path: Path to file or directory
+ :type path: str
+ :return: True if delete correctly
+ :rtype: bool
+ """
+ result = False
+
+ p = Path(path)
+ try:
+ if p.is_file():
+ p.unlink()
+ result = True
+ elif p.is_dir():
+ shutil.rmtree(path)
+ result = True
+ except OSError as e:
+ log.error(e)
+
+ return result
+
+ @classmethod
+ def copy(cls, source: str, target: str='', name: str='') -> str:
+ """Copy files
+
+ :param source: Path source
+ :type source: str
+ :param target: Path target
+ :type target: str
+ :param name: New name in target
+ :type name: str
+ :return: Path target
+ :rtype: str
+ """
+ p, f, n, e, _, _ = Paths(source).info
+ if target:
+ p = target
+ e = f'.{e}'
+ if name:
+ e = ''
+ n = name
+ path_new = cls.join(p, f'{n}{e}')
+ shutil.copy(source, path_new)
+ return path_new
+
+ @classmethod
+ def zip(cls, source: Union[str, tuple, list], target: str='') -> str:
+ path_zip = target
+ if not isinstance(source, (tuple, list)):
+ path, _, name, _ = _P(source).info
+ start = len(path) + 1
+ if not target:
+ path_zip = f'{path}/{name}.zip'
+
+ if isinstance(source, (tuple, list)):
+ files = [(f, f[len(_P(f).path)+1:]) for f in source]
+ elif _P.is_file(source):
+ files = ((source, source[start:]),)
+ else:
+ files = [(f, f[start:]) for f in _P.walk(source)]
+
+ compression = zipfile.ZIP_DEFLATED
+ with zipfile.ZipFile(path_zip, 'w', compression=compression) as z:
+ for f in files:
+ z.write(f[0], f[1])
+ return path_zip
+
+ @classmethod
+ def unzip(cls, source: str, target: str='', members=None, pwd=None):
+ path = target
+ if not target:
+ path = _P(source).path
+ with zipfile.ZipFile(source) as z:
+ if not pwd is None:
+ pwd = pwd.encode()
+ if isinstance(members, str):
+ members = (members,)
+ z.extractall(path, members=members, pwd=pwd)
+ return
+
+ @classmethod
+ def zip_content(cls, path: str):
+ with zipfile.ZipFile(path) as z:
+ names = z.namelist()
+ return names
+
+ @classmethod
+ def merge_zip(cls, target, zips):
+ try:
+ with zipfile.ZipFile(target, 'w', compression=zipfile.ZIP_DEFLATED) as t:
+ for path in zips:
+ with zipfile.ZipFile(path, compression=zipfile.ZIP_DEFLATED) as s:
+ for name in s.namelist():
+ t.writestr(name, s.open(name).read())
+ except Exception as e:
+ error(e)
+ return False
+
+ return True
+
+
+class Email():
+ """Class for send email
+ """
+ class SmtpServer(object):
+
+ def __init__(self, config):
+ self._server = None
+ self._error = ''
+ self._sender = ''
+ self._is_connect = self._login(config)
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ self.close()
+
+ @property
+ def is_connect(self):
+ return self._is_connect
+
+ @property
+ def error(self):
+ return self._error
+
+ def _login(self, config):
+ name = config['server']
+ port = config['port']
+ is_ssl = config['ssl']
+ starttls = config.get('starttls', False)
+ self._sender = config['user']
+ try:
+ if starttls:
+ self._server = smtplib.SMTP(name, port, timeout=TIMEOUT)
+ self._server.ehlo()
+ self._server.starttls()
+ self._server.ehlo()
+ elif is_ssl:
+ self._server = smtplib.SMTP_SSL(name, port, timeout=TIMEOUT)
+ self._server.ehlo()
+ else:
+ self._server = smtplib.SMTP(name, port, timeout=TIMEOUT)
+
+ self._server.login(self._sender, config['password'])
+ msg = 'Connect to: {}'.format(name)
+ debug(msg)
+ return True
+ except smtplib.SMTPAuthenticationError as e:
+ if '535' in str(e):
+ self._error = _('Incorrect user or password')
+ return False
+ if '534' in str(e) and 'gmail' in name:
+ self._error = _('Allow less secure apps in GMail')
+ return False
+ except smtplib.SMTPException as e:
+ self._error = str(e)
+ return False
+ except Exception as e:
+ self._error = str(e)
+ return False
+ return False
+
+ def _body(self, msg):
+ body = msg.replace('\n', '
')
+ return body
+
+ def send(self, message):
+ # ~ file_name = 'attachment; filename={}'
+ email = MIMEMultipart()
+ email['From'] = self._sender
+ email['To'] = message['to']
+ email['Cc'] = message.get('cc', '')
+ email['Subject'] = message['subject']
+ email['Date'] = formatdate(localtime=True)
+ if message.get('confirm', False):
+ email['Disposition-Notification-To'] = email['From']
+ email.attach(MIMEText(self._body(message['body']), 'html'))
+
+ paths = message.get('files', ())
+ if isinstance(paths, str):
+ paths = (paths,)
+ for path in paths:
+ fn = _P(path).file_name
+ # ~ print('NAME', fn)
+ part = MIMEBase('application', 'octet-stream')
+ part.set_payload(_P.read_bin(path))
+ encoders.encode_base64(part)
+ part.add_header('Content-Disposition', f'attachment; filename="{fn}"')
+ email.attach(part)
+
+ receivers = (
+ email['To'].split(',') +
+ email['CC'].split(',') +
+ message.get('bcc', '').split(','))
+ try:
+ self._server.sendmail(self._sender, receivers, email.as_string())
+ msg = 'Email sent...'
+ debug(msg)
+ if message.get('path', ''):
+ self.save_message(email, message['path'])
+ return True
+ except Exception as e:
+ self._error = str(e)
+ return False
+ return False
+
+ def save_message(self, email, path):
+ mbox = mailbox.mbox(path, create=True)
+ mbox.lock()
+ try:
+ msg = mailbox.mboxMessage(email)
+ mbox.add(msg)
+ mbox.flush()
+ finally:
+ mbox.unlock()
+ return
+
+ def close(self):
+ try:
+ self._server.quit()
+ msg = 'Close connection...'
+ debug(msg)
+ except:
+ pass
+ return
+
+ @classmethod
+ def _send_email(cls, server, messages):
+ with cls.SmtpServer(server) as server:
+ if server.is_connect:
+ for msg in messages:
+ server.send(msg)
+ else:
+ log.error(server.error)
+ return server.error
+
+ @classmethod
+ def send(cls, server: dict, messages: Union[dict, tuple, list]):
+ """Send email with config server, emails send in thread.
+
+ :param server: Configuration for send emails
+ :type server: dict
+ :param server: Dictionary con message or list of messages
+ :type server: dict or iterator
+ """
+ if isinstance(messages, dict):
+ messages = (messages,)
+ t = threading.Thread(target=cls._send_email, args=(server, messages))
+ t.start()
+ return
diff --git a/source/easymacro/messages.py b/source/easymacro/messages.py
new file mode 100644
index 0000000..9302dc1
--- /dev/null
+++ b/source/easymacro/messages.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env python3
+
+MESSAGES = {
+ 'es': {
+ 'OK': 'Aceptar',
+ 'Cancel': 'Cancelar',
+ 'Select path': 'Seleccionar ruta',
+ 'Select directory': 'Seleccionar directorio',
+ 'Select file': 'Seleccionar archivo',
+ 'Incorrect user or password': 'Nombre de usuario o contraseña inválidos',
+ 'Allow less secure apps in GMail': 'Activa: Permitir aplicaciones menos segura en GMail',
+ }
+}