Diferencia entre revisiones de «Usuario:Luis.perez/pyunoaytozgz/pyuno»
m |
|||
(No se muestran 9 ediciones intermedias de 2 usuarios) | |||
Línea 1: | Línea 1: | ||
{{MiTitulo|PyUNO: Macros y componentes para OpenOffice/LibreOffice con Python}} | {{MiTitulo|PyUNO: Macros y componentes para OpenOffice/LibreOffice con Python}} | ||
+ | <br /> | ||
== Documentación == | == Documentación == | ||
{{Recursos de la Web |Title=Documentación y recursos generales| | {{Recursos de la Web |Title=Documentación y recursos generales| | ||
Línea 6: | Línea 7: | ||
* Guía oficial de desarrollo de OpenOffice: http://wiki.openoffice.org/wiki/Documentation/DevGuide | * Guía oficial de desarrollo de OpenOffice: http://wiki.openoffice.org/wiki/Documentation/DevGuide | ||
* Recopilación de enlaces a docs sobre PyUNO http://wiki.openoffice.org/wiki/Python | * Recopilación de enlaces a docs sobre PyUNO http://wiki.openoffice.org/wiki/Python | ||
+ | * Información del bridge para python http://wiki.openoffice.org/wiki/PyUNO_bridge | ||
* En castellano: http://wiki.openoffice.org/wiki/OOoES/Desarrollo/PyUNO_ES | * En castellano: http://wiki.openoffice.org/wiki/OOoES/Desarrollo/PyUNO_ES | ||
Línea 15: | Línea 17: | ||
* Ejemplos de la guia de desarrollo: http://api.libreoffice.org/examples/DevelopersGuide/examples.html | * Ejemplos de la guia de desarrollo: http://api.libreoffice.org/examples/DevelopersGuide/examples.html | ||
* Snippets de código: http://codesnippets.services.openoffice.org/index.xml | * Snippets de código: http://codesnippets.services.openoffice.org/index.xml | ||
+ | * Ejemplos de macros: http://www.pitonyak.org/oo.php | ||
}} | }} | ||
Línea 24: | Línea 27: | ||
$ apt-get install libreoffice-dev | $ apt-get install libreoffice-dev | ||
</source> | </source> | ||
+ | * Vamos a utilizar libreoffice 3.5, que usa python 2.7 | ||
== Introducción al API de Openoffice: conceptos == | == Introducción al API de Openoffice: conceptos == | ||
Línea 177: | Línea 181: | ||
/description.xml | /description.xml | ||
/Scripts/python/holamundo.py | /Scripts/python/holamundo.py | ||
− | /META-INF/manifest. | + | /META-INF/manifest.xml |
</source> | </source> | ||
Línea 219: | Línea 223: | ||
</source> | </source> | ||
+ | == Conectando con libreoffice == | ||
+ | |||
+ | Podemos ejecutar scripts python para que se conecten a libreoffice. | ||
+ | |||
+ | ; Arrancar libreoffice escuchando en un puerto | ||
+ | <source lang="bash"> | ||
+ | $ soffice --norestore "-accept=socket,host=localhost,port=2002;urp;" | ||
+ | </source> | ||
+ | |||
+ | ; Conectarse a libreoffice y obtener objetos necesarios | ||
+ | |||
+ | <source lang="python"> | ||
+ | localContext = uno.getComponentContext() | ||
+ | resolver = localContext.ServiceManager.createInstanceWithContext( | ||
+ | "com.sun.star.bridge.UnoUrlResolver", localContext ) | ||
+ | ctx = resolver.resolve( "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext" ) | ||
+ | smgr = ctx.ServiceManager | ||
+ | desktop = smgr.createInstanceWithContext( "com.sun.star.frame.Desktop",ctx) | ||
+ | |||
+ | model = desktop.getCurrentComponent() | ||
+ | text = model.Text | ||
+ | cursor = text.createTextCursor() | ||
+ | text.insertString( cursor, "Hello World", 0 ) | ||
+ | </source> | ||
== Calc: Api == | == Calc: Api == | ||
+ | : | ||
+ | {{Recursos de la Web |Title=Documentación| | ||
+ | * http://wiki.openoffice.org/wiki/Documentation/DevGuide/Spreadsheets/Spreadsheet_Documents | ||
+ | * http://wiki.openoffice.org/w/images/thumb/b/be/SpreadsheetOverview.png/800px-SpreadsheetOverview.png | ||
− | |||
− | |||
}} | }} | ||
+ | |||
+ | * La mayor parte del api que nos interesa está bajo: | ||
+ | ** com.sun.star.sheet | ||
+ | ** com.sun.star.table | ||
+ | |||
+ | {{Ejemplo|Title=Ejemplos del api de calc| | ||
+ | Leer y ejecutar paso a paso la macro del ejemplo 5_calc_api. | ||
+ | }} | ||
+ | ; Filtros de exportación / Importacion: | ||
+ | * http://wiki.openoffice.org/wiki/Documentation/DevGuide/Spreadsheets/Filter_Options | ||
+ | |||
+ | |||
== Componentes y extensiones en PyUNO == | == Componentes y extensiones en PyUNO == | ||
+ | * Se encuentra en: /usr/lib/libreoffice/program/unopkg | ||
: | : | ||
Línea 237: | Línea 280: | ||
http://www.biochemfusion.com/doc/Calc_addin_howto.html | http://www.biochemfusion.com/doc/Calc_addin_howto.html | ||
}} | }} | ||
+ | |||
+ | * Los componentes pueden crearse en python extendiendo de unohelper.Base, e implementando los interfaces necesarios. | ||
+ | * Pueden implementar interfaces ya existentes en el api de UNO, o crear nuevos | ||
+ | * Los nuevos interfaces y servicios se declaran mediante UNOIDL en un fichero .idl | ||
+ | |||
+ | === Herramienta unopkg === | ||
+ | |||
+ | Es la herramienta con la que se pueden instalar, listar, y desinstalar paquetes | ||
+ | |||
+ | ; Instalar extensión: | ||
+ | <source lang="bash"> | ||
+ | $ unopkg add myextension.oxt | ||
+ | </source> | ||
+ | |||
+ | ; Instalar extensión para todos los usuarios: | ||
+ | <source lang="bash"> | ||
+ | $ unopkg add --shared myextension.oxt | ||
+ | </source> | ||
+ | |||
+ | ; Listar las extensiones instaladas | ||
+ | <source lang="bash"> | ||
+ | $ unopkg list | ||
+ | </source> | ||
+ | |||
+ | ; Eliminar una extensión | ||
+ | <source lang="bash"> | ||
+ | $ unopkg remove <identifier> | ||
+ | </source> | ||
+ | |||
+ | |||
+ | === Creación de extensión que exponga algún servicio/interfaz === | ||
+ | |||
+ | # Compilar .idl a .urd: <source lang="bash">/usr/lib/libreoffice/sdk/bin/idlc -w -I /usr/lib/libreoffice/sdk/idl XMyComp.idl</source> | ||
+ | # meter .urd en .rdb con regmerge <source lang="bash">/usr/lib/ure/bin/regmerge XMyComp.rdb /UCR XMyComp.urd</source> | ||
+ | # Implementación del servicio/interfaz en python | ||
+ | # Crear description.xml | ||
+ | # crear .xcu | ||
+ | # crear META-INF/MANIFEST.MF | ||
+ | # Empaquetar todo en zip, nombrandolo como myextension.oxt | ||
+ | # Instalar con unopkg <source lang="bash">/usr/lib/libreoffice/program/unopkg add myextension.oxt</source>, ó abriendo la extensión con libreoffice. | ||
+ | |||
+ | {{Ejemplo|Title=Añadiendo fórmulas a Calc| | ||
+ | Empaquetar la extensión del ejemplo 6_calc_formulas_addin | ||
+ | }} | ||
+ | |||
+ | == Interacción con componentes == | ||
+ | |||
+ | : | ||
+ | {{Recursos de la Web |Title=Documentación| | ||
+ | ;Documentación | ||
+ | http://wiki.openoffice.org/wiki/Documentation/DevGuide/WritingUNO/Accessing_Dialogs | ||
+ | http://wiki.openoffice.org/wiki/Documentation/DevGuide/WritingUNO/Using_Dialogs_in_Components | ||
+ | http://wiki.openoffice.org/wiki/Documentation/DevGuide/WritingUNO/Jobs | ||
+ | }} | ||
+ | |||
+ | === Jobs === | ||
+ | |||
+ | * Componentes que pueden ser ejecutados como respuesta a un evento en el sistema, o también directamente | ||
+ | * Han de implementar com.sun.star.task.XJob | ||
+ | * Son ejecutados por el servicio com.sun.star.task.JobExecutor | ||
+ | * Pueden ejecutarse mediante eventos estandar del sistema, o mediante eventos personalizados | ||
+ | |||
+ | ; Lista de eventos estándar del sistema | ||
+ | http://wiki.openoffice.org/wiki/Documentation/DevGuide/WritingUNO/Jobs/List_of_Supported_Events | ||
+ | |||
+ | {{Ejemplo|Title=Ejecutando un job| | ||
+ | Empaquetar y ejecutar el componente del ejemplo 8_writer_menutoolbar_job | ||
+ | }} | ||
+ | |||
+ | === Dialogos === | ||
+ | |||
+ | Se pueden crear de dos modos: | ||
+ | * Directamente con el api de com.sun.star.awt | ||
+ | * Usando diálogos creados con el IDE básico de dialogos de openoffice, e instanciandolos desde el componente o macro python: | ||
+ | <source lang="python"> | ||
+ | args = (doc,) | ||
+ | dialogprov = serviceManager.createInstanceWithArgumentsAndContext("com.sun.star.awt.DialogProvider2", args, ctx) | ||
+ | #dialog = dialogprov.createDialogWithHandler("file:///tmp/NameDialog.xdl", Handler()) # También se puede referenciar el archivo del diálogo | ||
+ | dialog = dialogprov.createDialogWithHandler("vnd.sun.star.script:NeoLibrary.NameDialog?location=application", Handler()) | ||
+ | dialog.execute() | ||
+ | </source> | ||
+ | |||
+ | {{Ejemplo|Title=Un diálogo para insertar texto| | ||
+ | Instalar y ejecutar como macro el ejemplo 11_using_basicdialog | ||
+ | }} | ||
+ | |||
+ | == Tips == | ||
+ | |||
+ | ===== Aumentar nivel de logs ===== | ||
+ | * Modificar: | ||
+ | /usr/lib/libreoffice/share/extensions/script-provider-for-python/pythonscript.py | ||
+ | <source lang="python"> | ||
+ | LogLevel.use = LogLevel.NONE # alternavly use LogLevel.ERROR or LogLevel.DEBUG | ||
+ | LOG_STDOUT = False # True, writes to stdout | ||
+ | # False, writes to user/Scripts/python/log.txt | ||
+ | </source> | ||
+ | |||
+ | ===== Obtener ruta de un paquete desplegado ===== | ||
+ | * útil para acceder a ficheros de la extensión desplegada, ver ejemplo de toolpanel | ||
+ | <source lang="python"> | ||
+ | pip = self.ctx.getValueByName("/singletons/com.sun.star.deployment.PackageInformationProvider" ) | ||
+ | s = pip.getPackageLocation(extensionID) # s es una string con la ruta absoluta al paquete | ||
+ | </source> |
Última revisión de 23:00 5 feb 2013
Contenido
- 1 Documentación
- 2 Documentación y recursos generales
- 3 Reflexión
- 4 Documentación
- 5 Ejemplos del api de calc
- 6 Documentación
- 7 Añadiendo fórmulas a Calc
- 8 Documentación
- 9 Ejecutando un job
- 10 Un diálogo para insertar texto
Documentación
|
Instalación de Libreoffice (Ubuntu)
- Basta con tener instalado libreoffice si solo se van a hacer macros.
- Si se van a crear componentes instalar adicionalmente el paquete libreoffice-dev
$ apt-get install libreoffice-dev
- Vamos a utilizar libreoffice 3.5, que usa python 2.7
Introducción al API de Openoffice: conceptos
Interfaces
- Un conjunto de métodos y/o atributos que exponen funcionalidad. Aspectos de la implementación de un objeto.
- En el api de UNO, todos los nombres de interfaces empiezan con X
- Todos los interfaces extienden XInterface
Servicios
- Servicios "New-style": Especifica que objetos que implementan un determinado interfaz (ej: com.sun.star.bridge.XUnoUrlResolver), estarán disponibles bajo un determinado nombre (ej: com.sun.star.bridge.UnoUrlResolver) en el service manager del component context (ver más adelante). Para que un objeto que implementa un servicio pueda implementar varios aspectos o interfaces, el interfaz que implementa heredará de varios otros interfaces.
- Servicios "Old-style": Los servicios oldstyle pueden verse como un conjunto de interfaces y/o propiedades.
- Pueden o no exponerse a través del service manager, servir como base de otros servicios (en el new-style, el mecanismo preferido para proporcionar servicios base es implementar interfaces que heredan de varios interfaces), o simplemente servir para agrupar un conjunto de propiedades.
- Pueden implementar interfaces opcionales
- Pueden incluir otros servicios, lo que significa que expondrán el conjunto de sus interfaces y de los otros servicios.
Obtener un servicio a través del serviceManager:
desktop = serviceManager.createInstanceWithContext("com.sun.star.frame.Desktop", context)
Más adelante se hablará del serviceManager y del component context
Obtener los nombres de servicio que soporta un objeto
obj.getSupportedServiceNames()
Debido a la existencia de servicios old-style, hay que tener cuidado a la hora de interpretar el api: Algunos servicios no pueden ser instanciados directamente con el service manager
|
Propiedades
- Pares de nombre-valor que expone un servicio.
- Normalmente suelen ser utilizados para atributos no estructurales (ej: color, tamaño, pero no objetos padre o hijos)
- Se suelen acceder mediante com.sun.star.beans.XPropertySet, pero también en algunos casos, a través de com.sun.star.beans.XPropertyAccess ó com.sun.star.beans.XMultiPropertySet
Obtener propiedad (XPropertySet):
sheet.getPropertyValue("IsVisible")
Establecer propiedad:
sheet.setPropertyValue("IsVisible", False)
Obtener propiedades de un objeto:
propsinfo = sheet.getPropertySetInfo() props = propsinfo.getProperties() for prop in props: print(prop)
Singletons
- Implementación de interface del que solo existe una instancia accesible desde el component context.
Componentes / extensiones
- Son librerías que contienen implementaciones de uno o varios servicios en cualquiera de los lenguajes que soporta UNO
Estructuras, Constantes, y Enumeraciones
- Estructuras: Conjunto de miembros, similar a la estructuras en C. Soportan herencia simple.
- Instanciando una estructura:
import uno uno.createUnoStruct("com.sun.star.beans.PropertyValue")
- Constants: tipo que agrupa varios valores constantes
- Obteniendo el valor de una constante
import uno uno.getConstantByName("com.sun.star.sheet.ConditionOperator.GREATER")
- Enum: Similar a una enumeración en c++
Módulos
- Espacios de nombres, similares a los namespaces de C++ o a los paquetes en Java. Agrupan servicios, interfaces, structs...
ComponentContext
- Objeto con el que se obtene el singleton de ServiceManager
- Puede obtenerse dependiendo de si el código a ejecutar va a ser una macro o un componente.
ServiceManager
- Objeto con el que se instancian servicios
serviceManager = ctx.ServiceManager
Macros
Son pequeños programas que usan el api UNO para automatizar tareas en documentos
Ejemplo:
import uno def holaMundoCalc(): # Accedemos al modelo del documento actual model = XSCRIPTCONTEXT.getDocument() # Accedemos a la primer hoja del documento hoja = model.getSheets().getByIndex(0) # Accedemos a la celda A1 de la hoja celda = hoja.getCellRangeByName("A1") # Escribimos en la celda celda.setString("Hola Mundo en Python") return None
- XSCRIPTCONTEXT: variable global en el script que contiene Document (el objeto que representa un documento), Desktop y ComponentContext
- Por defecto todas las funciones del script se exportan como macros. Para limitarlos:
g_exportedScripts = (holaMundoCalc, ) # solo expondrá holaMundoCalc, aunque haya otras funciones
- El comentario de una función se muestra como descripción de la macro en el diálogo de macros
Ejecución de macros
Puede hacerse a través del díalogo tools -> macros -> run macro... o a través de tools -> macros -> organize macros -> python...
Distribución de macros
Existen varios modos de distribuir una macro
- En directorio de usuario:
- ~/.openoffice.org/3/:user/Scripts/python
- ~/.config/libreoffice/3/user/Scripts/python
- En directorio compartido (para todos los usuarios)
- /usr/lib/libreoffice/share/Scripts/python
- /usr/lib/openoffice/share/Scripts/python
- Embebido en documentos:
- Descomprimir documento con unzip
- Incluir el script en cualquier ruta bajo el directorio Scripts
- Modificar META-INF/manifest.mf, incluyendo referencia al script:
<manifest:file-entry manifest:media-type="" manifest:full-path="Scripts/python/mostrarversion.py"/>
- Volver a comprimir el documento
- Empaquetado
- Se creará una estructura de archivos:
/description.xml /Scripts/python/holamundo.py /META-INF/manifest.xml
- Donde description.xml sirve para dar información sobre el paquete (ver 2_simplemacropkg), y manifest.mf referencia la ruta base de los scripts del siguiente modo:
<manifest:manifest> <manifest:file-entry manifest:media-type="application/vnd.sun.star.framework-script" manifest:full-path="Scripts"/> </manifest:manifest>
Transformando código Java a Python: diferencias
Una buena forma de aprender sobre UNO es mediante los ejemplos que hay disponibles. Lamentablemente la mayoría de código es Java o C++.
- La primera diferencia es obvia: python no es estáticamente tipado
- Para obtener y usar un servicio, no es necesario instanciar el servicio y luego hacer un UnoRuntime.queryInterface para obtener el interface deseado del tipo correcto. Basta con instanciar el servicio o componente, y usarlo directamente. Ej:
// doc es un objeto documento XSpreadsheetDocument xSpreadsheetDocument = (XSpreadsheetDocument) UnoRuntime.queryInterface(XSpreadsheetDocument.class, doc); XSpreadsheets xSpreadsheets = xSpreadsheetDocument.getSheets(); xSpreadsheets.insertNewByName("MySheet", (short)0);
Esto se traduce a:
xSpreadsheets = doc.getSheets() xSpreadsheets.insertNewByName("MySheet", 0)
- Los arrays y listas se transforman de y hacia python como tuplas, NO como listas!.
com.sun.star.beans.PropertyValue[] conditions = new com.sun.star.beans.PropertyValue[1]; conditions[0] = condition1 conditions[1] = condition2 obj.metodoquerecibeprops(conditions)
- Lo anterior en python sería:
conditions = (condition1, condition2) obj.metodoquerecibeprops(conditions)
Conectando con libreoffice
Podemos ejecutar scripts python para que se conecten a libreoffice.
- Arrancar libreoffice escuchando en un puerto
$ soffice --norestore "-accept=socket,host=localhost,port=2002;urp;"
- Conectarse a libreoffice y obtener objetos necesarios
localContext = uno.getComponentContext() resolver = localContext.ServiceManager.createInstanceWithContext( "com.sun.star.bridge.UnoUrlResolver", localContext ) ctx = resolver.resolve( "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext" ) smgr = ctx.ServiceManager desktop = smgr.createInstanceWithContext( "com.sun.star.frame.Desktop",ctx) model = desktop.getCurrentComponent() text = model.Text cursor = text.createTextCursor() text.insertString( cursor, "Hello World", 0 )
Calc: Api
- La mayor parte del api que nos interesa está bajo:
- com.sun.star.sheet
- com.sun.star.table
- Filtros de exportación / Importacion
Componentes y extensiones en PyUNO
- Se encuentra en: /usr/lib/libreoffice/program/unopkg
http://wiki.openoffice.org/wiki/Documentation/DevGuide/WritingUNO/Writing_UNO_Components http://wiki.openoffice.org/wiki/Documentation/DevGuide/Extensions/ http://wiki.openoffice.org/wiki/Documentation/DevGuide/WritingUNO/Writing_the_Specification -> especificación de UNO IDL
|
- Los componentes pueden crearse en python extendiendo de unohelper.Base, e implementando los interfaces necesarios.
- Pueden implementar interfaces ya existentes en el api de UNO, o crear nuevos
- Los nuevos interfaces y servicios se declaran mediante UNOIDL en un fichero .idl
Herramienta unopkg
Es la herramienta con la que se pueden instalar, listar, y desinstalar paquetes
- Instalar extensión
$ unopkg add myextension.oxt
- Instalar extensión para todos los usuarios
$ unopkg add --shared myextension.oxt
- Listar las extensiones instaladas
$ unopkg list
- Eliminar una extensión
$ unopkg remove <identifier>
Creación de extensión que exponga algún servicio/interfaz
- Compilar .idl a .urd:
/usr/lib/libreoffice/sdk/bin/idlc -w -I /usr/lib/libreoffice/sdk/idl XMyComp.idl
- meter .urd en .rdb con regmerge
/usr/lib/ure/bin/regmerge XMyComp.rdb /UCR XMyComp.urd
- Implementación del servicio/interfaz en python
- Crear description.xml
- crear .xcu
- crear META-INF/MANIFEST.MF
- Empaquetar todo en zip, nombrandolo como myextension.oxt
- Instalar con unopkg , ó abriendo la extensión con libreoffice.
/usr/lib/libreoffice/program/unopkg add myextension.oxt
Interacción con componentes
Jobs
- Componentes que pueden ser ejecutados como respuesta a un evento en el sistema, o también directamente
- Han de implementar com.sun.star.task.XJob
- Son ejecutados por el servicio com.sun.star.task.JobExecutor
- Pueden ejecutarse mediante eventos estandar del sistema, o mediante eventos personalizados
- Lista de eventos estándar del sistema
http://wiki.openoffice.org/wiki/Documentation/DevGuide/WritingUNO/Jobs/List_of_Supported_Events
Dialogos
Se pueden crear de dos modos:
- Directamente con el api de com.sun.star.awt
- Usando diálogos creados con el IDE básico de dialogos de openoffice, e instanciandolos desde el componente o macro python:
args = (doc,) dialogprov = serviceManager.createInstanceWithArgumentsAndContext("com.sun.star.awt.DialogProvider2", args, ctx) #dialog = dialogprov.createDialogWithHandler("file:///tmp/NameDialog.xdl", Handler()) # También se puede referenciar el archivo del diálogo dialog = dialogprov.createDialogWithHandler("vnd.sun.star.script:NeoLibrary.NameDialog?location=application", Handler()) dialog.execute()
Tips
Aumentar nivel de logs
- Modificar:
/usr/lib/libreoffice/share/extensions/script-provider-for-python/pythonscript.py
LogLevel.use = LogLevel.NONE # alternavly use LogLevel.ERROR or LogLevel.DEBUG LOG_STDOUT = False # True, writes to stdout # False, writes to user/Scripts/python/log.txt
Obtener ruta de un paquete desplegado
- útil para acceder a ficheros de la extensión desplegada, ver ejemplo de toolpanel
pip = self.ctx.getValueByName("/singletons/com.sun.star.deployment.PackageInformationProvider" ) s = pip.getPackageLocation(extensionID) # s es una string con la ruta absoluta al paquete