OOoES/Desarrollo/PyUNO ES bak

From Apache OpenOffice Wiki
< OOoES
Revision as of 21:45, 29 October 2012 by RGB (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
Documentation exclamation.png
Página obsoleta

El conector de Python-Uno

Traducido por: Alexandro Colorado

Introducción

El conector de python-Uno o PyUno te permite usar el API estandard de OpenOffice.org desde el tan bien conocido lenguaje llamado python. Tambié te permite desarrollar componentes en python, aunque los componentes de PyUno pueden ser corridos durante el proceso dentro de la suite, este tambien puede ser conjugado desde Java, C++ o ser construido desde el lenguaje StarBasic.


La versión original de este documento lo puedes encontrar en http://udk.openoffice.org/python/python-bridge.html

Descargas

Puedes descargar esta documentaci&oacuten incluyendo sus ejemplos y archivos de apoyo para este nuevo modo de trabajo basado en scripting para trabajos no en linea.


Descarga pyuno-docs.zip


Estado

El conector de PyUno esta completado, pero no ha sido empleado por suficiente gente, así que puede contener algunos errores. Ahora esta integrado a la fuente de OpenOffice.org 1.1. Aunque no corre con el la fuente de las versiones 1.0 para atras.

Actualmente la documentación esta enfocada a desarrolladores, que cuentan con la suficiente experiencia en el API nativo de OpenOffice.org y que lo han usado bajo otro lenguaje(Java/C++/StarBasic). Si eres un novato y sientes que alguna información es requerida, te sugerimos buscarla en la "La Guía del Desarrollador".


El Tutorial PyUno para OpenOffice.org

En este tutorial, mostramos como se puede usar el conector PyUNO para automatizar tareas dentro de OpenOffice.org. Este documento no es un tutorial de OpenOffice.org existen ya muchos documentos enfocados a esta actividad. A continuación mostraremos los pasos a seguir dentro de PyUNO


Instalación de PyUNO

Desde la versión OpenOffice.org 1.1 RC4 para arriba, PyUNO es incluido en la instalación por default. Si tienes una versió anterior a esta puedes saltar el proximo parrafo.


Al Usar OpenOffice.org 1.1RC3 o antes, el conector de PyUNO debe ser seleccionado manualmente para su instalación. Escoga entonces la instalación personalizada.


y activa el conector de Python-Uno en los componentes opcionales


Tambien puedes instalar PyUNO despues de la instalación inicial, corriendo la instalación de OpenOffice.org sombre si misa y seleccionando Modificar en el siguiente dialogo.


Nota: Cuando inicias el script de comienzo (eg. install --prefix=/usr/local) para instalación, solo puedes comenzar python despues de la instalación, solo puedes instalar pyuno despues de haber ejecutado el script setup /net y escogido Modify. Nota, que OOo Rc2 el cual no instala dos archivos ini como deviera.


Modo del conector PyUNO

PyUNO puede usar dos metodologías diferentes: Dentro del ejecutable de Python(y fuera del proceso de OpenOffice.org).


Usa esta metodología cuando: Comienzes a usar python (tiene un enfoque mas intuitivo. quieres apuntar a ejecuciones del script comenzando procesos separados (ie. cgi-script dentro de un http-server. Quieres un retorno rapido de codigo-ejecución codigo-ejecución.

  Hola Mundo

Asegurate, de que OOo no este corriendo(cierra el QuickStarter tambien). Activa una terminal ya sea cmd en WinNT+, command en Win9x o bash en *nix. Ve al directorio donde reside el programa de OpenOffice.org y activa OOo con los siguientes parametros.

\Program Files\OpenOffice1.1\program> soffice "-accept=socket,host=localhost,port=2002;urp;"

Ahora comienza el archivo hello_world.py con tu editor de texto favorito e incluye el siguiente codigo. [python] import uno

  1. get the uno component context from the PyUNO runtime

localContext = uno.getComponentContext()

  1. create the UnoUrlResolver

resolver = localContext.ServiceManager.createInstanceWithContext( "com.sun.star.bridge.UnoUrlResolver", localContext )

  1. connect to the running office

ctx = resolver.resolve( "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext" ) smgr = ctx.ServiceManager

  1. get the central desktop object

desktop = smgr.createInstanceWithContext( "com.sun.star.frame.Desktop",ctx)

  1. access the current writer document

model = desktop.getCurrentComponent()

  1. access the document's text property

text = model.Text

  1. create a cursor

cursor = text.createTextCursor()

  1. insert the text into the document

text.insertString( cursor, "Hello World", 0 )

  1. Do a nasty thing before exiting the python process. In case the
  2. last call is a oneway call (e.g. see idl-spec of insertString),
  3. it must be forced out of the remote-bridge caches before python
  4. exits the process. Otherwise, the oneway call may or may not reach
  5. the target object.
  6. I do this here by calling a cheap synchronous call (getPropertyValue).

ctx.ServiceManager

Ejecuta el script pasado con el siguiente comando

  c:\Program Files\OpenOffice1.1\program> .\python hello_world.py 

Este script escribe la cadena Hello World dentro del documento OpenWriter

Dentro del proceso de OpenOffice.org

Usa este modo cuando:

  • Quieras facilmente ejecutar tu codigo en multiples maquinas
  • Tu codigo sea necesitado por llamadas de otros programas
  • Tienes mas experiencia con el funcionamiento de PyUNO.
  • Quieres que tu codigo corra con el mejor desempeño

Hola Mundo

El ejemplo siguiente esta guardado como un componente de UNO, esto significa, que el codigo que ejecuta la insersion necesita ser embedido en una class de python. Aparte, el codigo-de-conexion a la suite necesita ser remplazado por un punto clave, el cual usa el cargador de python para iniciar una clase de python. hello_world.py [python]import uno import unohelper

from com.sun.star.task import XJobExecutor

  1. implement a UNO component by deriving from the standard unohelper.Base class
  2. and from the interface(s) you want to implement.

class HelloWorldJob( unohelper.Base, XJobExecutor ):

   def __init__( self, ctx ):
       # store the component context for later use
       self.ctx = ctx
       
   def trigger( self, args ):
       # note: args[0] == "HelloWorld", see below config settings
   
       # retrieve the desktop object
       desktop = self.ctx.ServiceManager.createInstanceWithContext(
           "com.sun.star.frame.Desktop", self.ctx )
   
       # get current document model
       model = desktop.getCurrentComponent()
       # access the document's text property
       text = model.Text
       # create a cursor
       cursor = text.createTextCursor()
       # insert the text into the document
       text.insertString( cursor, "Hello World", 0 )
  1. pythonloader looks for a static g_ImplementationHelper variable

g_ImplementationHelper = unohelper.ImplementationHelper()

g_ImplementationHelper.addImplementation( \

       HelloWorldJob,                        # UNO object class
       "org.openoffice.comp.pyuno.demo.HelloWorld", # implemenation name
                                     # Change this name for your own
     # script 
       ("com.sun.star.task.Job",),)          # list of implemented services
                                     # (the only service)

El codigo necesita ser enlazado a un evento. Esto puede hacerce configurando estas opciones. Addons.xcu:


Both files must be packaged up into a single zip file by using your favourite zip utility, e.g. infozip [bash]zip hello_world.zip Addons.xcu hello_world_comp.py

 adding: Addons.xcu (deflated 55%)
 adding: hello_world_comp.py (deflated 55%)

Estos paquetes pueden ser implementados dentro de una instalación de OpenOffice.org usando la herramienta pkgchk , el cual esta ubicada en el directorio de program dentro de OOo. Se advierte que OOo debe de estar completamente cerrado.

 c:\Program Files\OpenOffice.org1.1\program> pkgchk hello_world.zip
 c:\Program Files\OpenOffice.org1.1\program>

Si el proceso se realiza con exito, no tendras ningun reotorno de la herramienta. Y cuando comienzes de nuevo OOo, encontraras una nueva entrada del menu (ver Tools / Aditional Components / Insert Hello World).

Como puedes ver las lineas de los scripts bases son identicas, pero la forma para extraer los componentes de office difieren. Ejemplos

  • ooextract.py- Una herramienta de linea de comando la cual extrae el contenido de un archivo creado con OpenOffice.org en un modo stout.
  • swriter.py- Herramienta de linea de comando la cual crea y llena un documento de Writer con cierto texto y tablas con formato.
  • swritercomp.py y swritercompclient.pyIgual que la anterior pero esta esta en modo componente UNO, asi que corre dentro de OpenOffice.org. Uno puede ver la mejora en el desempeño corriendo el componente como un proceso.

biblioaccess.py- Herramienta de linea de comando, que presenta el contenido del la base de datos, biblio, que incluye OpenOffice.org

Conexión al lenguaje UNO

A continuación encontrarás la completa descripción de las herramientas UNO se conectan con el lenguaje de Python. El typo de mapeo UNOEl tipo de dato IDL


tipos de enteros (byte, short, unsigned long, hyper, unsigned hyper Representación en Python


Python solo conoce los datatypes encontrados en C long y long long como tipos de enteros. En la mayor parte de las maquinas, un long es un valor de 32 bit mientras long long es de 64 bits.

  • Valores de UNO (por ejemplo, retorno del valor de un metodo UNO)
  • Valores que tiene los tipos byte, short, unsigned short, long o unsigned, long son convertidos a un valor python long.
  • Valores que tienen un tipo de hyper u unsigned hyper son convertidos a python como long long.
  • Valores enviados a UNO (por ejemplo, el argumento de un metodo UNO)


Si hay un tipo concreto en el metodo IDL, el valor es convertido a un tipo concreto (de hecho el servicio de invoación hace este trabajo). Si el metodo solo contiene un any, cada valor del entero será convertido al typo de dato mas pequeño, donde el valor se ajusta y lo manda al objeto UNO (así que 5 se convierte en un byte, 150 se vuelve short, 0x1f023 un long y los valores mayores a 0xffffffff se convierten en un hyper. boolean


Python tiene, internamente un datatype buleano, el cual es derivado del tipo de entero (ver http://python.org/peps/pep-0285.html ). Ahí existe el True y False, el cual PyUNO usa para distinguir entre valores integrales o buleanos.


Mientras el buleano sea especificado en el metodo de interfaz, podrás seguir usando numeros. En los ejemplos siguientes, todas las llamadas son validas: [python]#idl signature void takeBool( [in] boolean bool ) unoObject.takeBool( 1 ) # valid, passing true (PyUNO runtime

  1. does the conversion

unoObject.takeBool( True) ) # valid, passing true unoObject.takeBool( False ) # valid, passing false

Si embargo, cuando quieres especificamente pasar un buleano, cuando solo any es especificado, tu debes usar True y False. idl signature void foo( [in] any value )

[python]# implementation expects a boolean (which is separately documented

  1. e.g. in the service specification.

unoObject.foo( True ) # valid, pass a true unoObject.foo( 1 ) # bad, just passing a 1, implementation will

                     # probably not be able to deal with it correctly.

Nota: Tambien existe el uso de la clase uno.Bool, el cual es no es usado desde pyuno 0.9.2 aunque se sigue soportando. Ya no lo usen. string En general, la cadena esta mapeada dentro de la cadena-unicodigo de python. Sin embargo, quizas pases una cadena de 8-bits cuando se espere una cade de UNO, el conector convierte los 8-bits a una cadena-unicodigo usando el locale del sistema. [python]# idl signatore foo( [in] string value )

  1. both lines are valid

unoObject.foo( u'my foo string' ) unoObject.foo( 'my foo string' ) char Un char es mapeado a un uno.Char. Tiene el miembro de una cadena-unicodigo publico con lenght 1 como contenedor del char unicodigo.

  1. idl signature foo( [in] char c)

unoObject.foo( uno.Char( u'h' ) ) #valid unoObject.foo( 'h' ) #wrong

enum


Un valor enum es representado concretamente por una instancia de la clase uno.Enum. Tiene dos miembros, typeName es una string conteniendo el nombre del tipo enum y value contiene el valor de enum.

Puedes concretar los valores de enum de dos formas:


1.(suggested) by importing

 from enumname import enumvalue. 

E.g. [code]from com.sun.star.uno.TypeClass import UNSIGNED_LONG . . . unoObject.setValue( UNSIGNED_LONG ) if unoObject.getValue() == UNSIGNED_LONG:[/code]


2.(in rare situations)


[python]import uno

unoObject.setValue( uno.Enum( "com.sun.star.uno.TypeClass", "UNSIGNED_LONG") ) if unoObject.getValue() == uno.Enum( "com.sun.star.uno.TypeClass", "UNSIGNED_LONG"):

La primera soluci&ooacute;n tiene la ventaja de que en caso de el fallo en enum manda una RunTimeException o excepción del tiempo de proceso al momento que la fuente de python es importada.


type Un tipo se mapea a uno.type . Tiene miembros publicos llamados typeName (cadenas) y typeClass (el valor enum de com.sun.star.uno.TypeClass). Ahi existe una función uno.getTypeByName() para facilmente crear una instancia de type, las funciones crean un RunTimeException en caso que el type no se conosca.

Puedes crear valores concretos de dos formas: 1. from module-where-type-lives-in import typeOfTypeName.

E.g. to create XComponent's type, use


from com.sun.star.lang import typeOfXComponent . . . unoObject.setType( typeOfXComponent ) if unoObject.getType() == typeOfXComponent:

2.(in rare situations, e.g. for types of simple values)

[python] import uno

unoObject.setType( uno.getTypeByName( "com.sun.star.uno.XComponent" ) ) if unoObject.getType() == uno.getTypeByName( "com.sun.star.uno.XComponent"): struct (and exception) Para cada uno struct (o excepción), una nueva clase de python es generada al vuelo. La clase generada refleja la jerarquia heredada del tipo UNO (es importante para el administrador de excepciones, ver abajo).

Uno puede generar una clase struct usando el mecanismo de importación. Una instanacia del struct puede entonces ser instanciada usando el constructor de python. El constructor soporta el argumento cero (miembros obtienen el constructor default), 1 argumento con el mismo tipo (copia el constructor), y n argumentos, donde n es el nombre de elementos que contiene el struct. El struct soporta el operador de equality, dos struct son iguales, si son del mismo type y cada miembro es igual.

Ejemplo. [python] from com.sun.star.beans import PropertyValue from com.sun.star.uno import Exception,RuntimeException

propVal = PropertyValue() # Default constructor propVal.Name = "foo" propVal.Value = 2

if propVal == PropertyValue( "foo", 2 ): # Memberwise constructor

  # true !
  pass

if propVal == PropertyValue( propVal ): # Copy Constructor

  # true 

Una instancia del struct UNO puede ser inicialmente construida de la función uno.createUnoStruct() y pasando el nombre de la struct como el primer parametro y el argumento opcional del constructor (ver arriba para un ejemplo de un posible ctors).

ATENCION: En UNO, struct tiene el valor semantico, sin embargo, la manera como se maneja en python no refleja esto. Cuando el struct es pasado de un parametro a una funcion, el valor es pasado la funcion llamada. Las siguientes modificaciones a la instancia de la struct no afectaran a la funcion llamada. Sin embargo, la simple asignaci&oactue;n de una struct a otra variable local no crea una copia, simplemente crea un alias de la instancia original. [python] struct = uno.createUnoStruct( "com.sun.star.beans.PropertyValue" )

struct.Name = "foo" struct2 = struct struct2.Name = "python" # modifies also struct, probably not desired ! unoObject.call( struct, struct2 ) # passes the same struct 2 times !

struct.Name = "doobidooo" # even worse style. If the UNO object is implemented

                                 # in python, you possibly modify the callee's value.
 # Don't do this !

sequence Una secuencia generalmente esta trazada a una tupla de python. Una lista de de python (!) no es aceptada. [python]# idl signature XInterface createInstanceWithArguments(

  1. [in] string servicename, [in] sequence )

doc = smgr.createInstanceWithArguments( "foo.service", ("arg1",2))

Atención desde la version 0.9.2 El idl sequence<byte> es trazado a la clase uno.ByteSquence. Tiene un miembro cadena llamado value, el cual contiene los datos de la secuencia de byte. Tal como la bytesequence es generalmente un contenedor para datos binarios, esta clase permite manejar binarios eficientemente. Esto tambien embede pyuno a python, ya que python mantiene datos binarios en cadenas. [python]# idl signature writeBytes( [in] sequence%lt; byte > data )

out.writeBytes( uno.ByteSequence( "abc" ) )

  1. you could also write the following

begin = uno.ByteSequence( "ab" ) out.writeBytes( begin + "c" )

  1. but this does not work !

out.writeBytes( "abc" ) # ERROR, no implict conversion supported by the runtime !


  1. idl signature long readBytes( [out] sequence , [in] length )

len,seq = in.readBytes( dummy, 3 )

  1. the statements do the same thing

print seq == "abc": print seq == uno.ByteSequence( "abc" ) constants Una constante UNO idl puede ser dada por diferentes maneras: Usa el valor concreto especificado en el archivo idl Una constante es su valor y solo su valor. Ya que las modificaciones de los valores de la contacia es incompatible. Usa el mecanismo de importación para crear variables con un nombre constante La solución es la mas leida. Usauno.getConstatByName() puede ser util de vez en cuando. Las funciones RunTimeException se ejecutan en caso que la constante sea desconocida. [python]from com.sun.star.beans.PropertyConcept import ATTRIBUTES . . .

  1. the following 3 lines are equivalent

unoObject.setConcept( ATTRIBUTES ) unoObject.setConcept( 4 ) unoObject.setConcept( uno.getConstantByName( "com.sun.star.beans.PropertyConcept.ATTRIBUTES" ) ) any En general el programador en python nunca llega a tener contacto con anys. Si alguna vez saliera any en un metodo, el programador puede tan solo pasar un valor concreto. Consecuentemente, los retornos de valores o parametros tambien nunca contiene un any concreto.

Sin embargo, hay ciertas circumstancias, donde el programdor de python quiera pasar un valor type concreto a una funcion que llame (nota, esto es solo posible para llamadas del 'conector', no puedes pasar un valor de type a ningun otro objeto python-uno).

Puedes crear un uno.Any( ), pasando el type ( como typename o como uno.Type) y su valor. [python]# constructs a uno.Any, that contains a byte byteAny = uno.Any( "byte" , 5 )

  1. constructs a sequences of shorts

byteAny = uno.Any( "[]short", (4,5))


Estas anys solo pueden ser usadas junto con el uno.invoke, el cual permite invocar un metodo dentro de un objeto arbitrario con un type any. [python]# the normal call uno.setPropertyValue( "foo", (4,5))

  1. the uno.invoke call

uno.invoke( obj, "setPropertyValue" , ("foo",uno.Any( "[]short", (4,5))) )

Cuando obj es un objeto conectado, la funcion que se llama obtiene la secuencia como una sequence(short). Cuando obj es un objeto local de python, solo obtiene el (4,5) como si lo hubiera obtenido con la llamada normal.

Implementando los objetos UNO

Podras usar clases de python para implementar los objetos UNO. Instancias de python pueden ser pasadas como argumentos a llamados de UNO donde los anys o interfaces concretas hayan sido especificadas.

Para ser un objeto UNO, una clase de python DEBE implementar la interface com.sun.star.lang.XTypeProvider implementando los metodos getTypes() y getImplementationID(), el cual informa el bridge de python, sobre cual UNO concreta la interfaz con la clase que implementa python. La funci&ooacute;n de getTypes() define que interfaz son definidas por las clases.

Para hacer esto mas fácil, existe una clase unohelper.Base, donde uno objeto UNO de python se originara de el. Tu puedes entonces implementar una interfaz UNO simpmente originandolo de la interfaz preferida. El siguiente ejemplo implementa la interfaz com.sun.star.lang.XTypeProvider, el cual almacena toda la información escrita en el espacion de una ByteSequence(). (Nota: esta implementació es bastante pobre y es solo con propositos de demostración. [python]import unohelper from com.sun.star.io import XOutputStream class SequenceOutputStream( unohelper.Base, XOutputStream ):

     def __init__( self ):
         self.s = uno.ByteSequence("")
         self.closed = 0
         
     def closeOutput(self):
         self.closed = 1
     def writeBytes( self, seq ):
         self.s = self.s + seq
     def flush( self ):
         pass
     def getSequence( self ):
         return self.s

Desde la lista de clases bases, la implementación unohelper.Base implementa correctamente la interfaz XTypeProvider. Implementando componentes de Python UNO.

Existe un cargador de componentes a python. Esto permite crear instancias de clases de python no solo en el proceso de python pero en cual quialquier proceso arbitrario de UNO incluyendo OpenOffice.org. El cargador de python, carga el runtime de python bajo demanda en caso de no estar cargado y ejecutando codigo de python desde el interprete root de python.

Si el lector no esta familiarizado con el proceso de registro de componentes, entonces se sugiere que vaya el maual de desarrollo de OpenOffice.org para una amplia explicación.

El cargador de Python actualmente soporta los siguientes protocolos para urls recibidas:Nombre del Protocolo Descripción vnd.openoffice.pymodule El protocolo depende en parte interpretado por el nombre del modulo de python, el cual es importado usando el componente de importación comun en python (el cual usa la variable de ambiente de PYTHONPATH).

Ejemplo. vnd.openoffice.pymodule:MyPythonComponent

Usando esta url ej. en XLoader.activate() intentara correr al archivo MyPythonComponent.py desde los directorios, los cuales son listados dentro de la variable de ambiente PYTHONPATH. Toma nota que no debes usar el prefijo *.py en este caso.

El modulo mostrado es agregado al mapeo de sys.modules. file Obligatoriamente el url debe ser absoluto al archivo componente de python. El archivo mismo no necesita ser contenido en el PYTHONPATH, simplemente importar los archivos que estan contenidos en el PYTHONPATH. El modulo no es adherido al sys.modules.

Ejemplo: file:///path/to/MyPythonComponent.py vnd.sun.star.expand El cargador de python soporta el mecanismo de expansion de macros tal como los cargadores de Java y C++.

Ejemplo: vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/MyPythonComponent.py


Despues de que el modulo esta importado, el cargador de python busca una variable del modulo-global con el nombre g_ImplementationHelper el cual es esperado ser una intancia del unohelper.ImplementationHelper. El siguiente ejemplo hace que un componente de uno fuera de los objetsos UNO ( no es que el componente sea tan útil, por que en esta ocasion no existe un metodo UNO que reciba una tupla tampoco existe especificaci&ooacute;n de servicio com.sun.star.io.OutputStream, solo estan aquí como ejemplo. ) [python]import unohelper from com.sun.star.io import XOutputStream

g_ImplementationHelper = unohelper.ImplementationHelper()

class TupleOutputStream( unohelper.Base, XOutputStream ):

     # The component must have a ctor with the component context as argument.
     def __init__( self, ctx ):
 self.t = ()
 self.closed = 0
     # idl void closeOutput();
     def closeOutput(self):
 self.closed = 1
     # idl void writeBytes( [in] sequence seq );
     def writeBytes( self, seq ):
 self.t = self.t + seq      # simply add the incoming tuple to the member
     # idl void flush();
     def flush( self ):
 pass
     # convenience function to retrieve the tuple later (no UNO function, may
     # only be called from python )
     def getTuple( self ):
 return self.t
  1. add the TupleOutputStream class to the implementation container,
  2. which the loader uses to register/instantiate the component.

g_ImplementationHelper.addImplementation( \ TupleOutputStream,"org.openoffice.pyuno.PythonOutputStream",

                   ("com.sun.star.io.OutputStream",),)

Asumamos, que este codigo es guardado en un archivo llamado tuplestrm.py y el archivo existe en algun lugar dentro de la variable PYTHONPATH, puede ser registrado al OO1beta construido del siguiente formato:

   regcomp -register -br types.rdb -br services.rdb -r services.rdb -c vnd.openoffice.pymodule:tuplestrm 

Claro que tambien puedes usar el pkgchk del tutorial en el capitulo

   pkgchk tuplestrm.py 

aunque este comando crea una copia del archivo (cuando el script cambia, debe ser reimplementado usando el comando que sigue. )

Este componente puede ser llamado desde OpenOffice.org Basic con: [python]tupleStrm = createUnoService( "com.sun.star.io.OutputStream" ) tupleStrm.flush()

Personal tools