diumenge, 22 d’agost del 2010

Mapa conceptual: què és el Programari Lliure (PL)?

Què és el Programari Lliure? a la Viquipèdia hom pot trobar aquesta mapa conceptual que ho explica gràficament:

Com invocar una funció de calc des d'una macro?

La idea central és invocar la funció a través del servei UNO com.sun.star.sheet.FunctionAccess. Cal passar dos arguments: el nom de la funció, en anglès, que volem executar; i un Array amb els paràmetres que demana la funció.


la llista de funcions en anglès la podem trobar a la wiki: http://wiki.services.openoffice.org/wiki/Documentation/How_Tos/Calc:_Functions_listed_by_category


En l'exemple invocarem la funció BDEXTREU (DGET en anglès) que permet trobar en un rang de cel·les aquella que verifica unes determinades condicions. Els paràmetres de BDEXTREU (DGET) també els trobem a la wiki:
http://wiki.services.openoffice.org/wiki/Documentation/How_Tos/Calc:_DGET_function

En una fulla de Calc posaré els següents valors en el rang B1:D22. Aquesta serà la taula de dades:





id nom valor
1 a aa
2 b bb
3 c cc
4 d dd
5 e ee
6 f ff
7 g gg
8 h hh
9 i ii
10 j jj
11 k kk
12 l ll
13 m mm
14 n nn
15 o oo
16 p pp
17 q qq
18 r rr
19 s ss
20 t tt
21 u uu


I en el rang B24:D25 hi poso les condicions de cerca. Els títols han de coincidir amb els de la taula de dades





id nom valor
8




Poso que em cerqui el valor ID=8


Aleshores, en una altre cel·la verifico que la funció troba el valor posant:


=BDEXTREU(B1:D22;C1;B24:D25)


Que ho llegeixo així, en el rang de dades B1:D22, obté el valor de la columna de títol C1, que verifica les condicions expressades al rang B24:D25.


Les condicions que hi ha a B24:D25 són (ID=8) i (NOM pot ser qualsevol valor) i (VALOR pot ser qualsevol valor).


La funció retorna "h" com era d'esperar.


Aquesta és una funció molt útil per a fer cerques en fulles. Com puc utilitzar-la en una macro? Primer de tot, creo un nou mòdul de macros  i hi afegeixo el següent codi:



function MacroBDExtreu as String


   Dim oFuncio As Object
   Dim oFull As Object
   Dim oRangTaula As Object
   Dim oColumnaSelect As Object
   Dim oColumnaCondicions as Object
   Dim sRetorn As String

   oFull = ThisComponent.Sheets.getByName("Full1")


   oRangTaula = oFull.getCellRangeByName("B1:D22")
   oColumnaSelect = oFull.getCellByPosition(2, 0)
   oColumnaCondicions = oFull.getCellRangeByName("B24:D25")


   oFuncio = createUnoService("com.sun.star.sheet.FunctionAccess") 
   
   ' Invoca la funció pel seu nom en anglès: BDEXTREU --> DGET
   sRetorn = oFuncio.callFunction("DGET", _
                                  Array(oRangTaula, _
                                        oColumnaSelect.String, _
                                        oColumnaCondicions)) 
   
   MsgBox(sRetorn)


   MacroBDExtreu = sRetorn
End Function


I invoquem la macro que ens mostra un missatge amb la dada trobada.

divendres, 20 d’agost del 2010

Macros OOoBasic com funcions de Calc.

OpenOffice.org Calc aporta una llarga llista de funcions incorporades.

Aquesta llista, amb els noms de les funcions en anglès, la podem obtenir aquí:
http://wiki.services.openoffice.org/wiki/Documentation/How_Tos/Calc:_Functions_listed_by_category

 En alguna ocasió ens interessarà poder fer servir alguna d'aquestes funcions dins del codi d'una macro. Igualment es pot donar el cas que ens interessi invocar una macro com si fos una funció icroporada del calc.

Comencem per això últim. Invocar una macro com si fos una funció de Calc.

Per a poder invocar una macro com si fos una funció de Calc és necessari, primer de tot, que la macro estigui carregada. Per defecte es carreguen les macros que estan a la biblioteca "Standard".

Si la macro que vull invocar es troba a un mòdul de la llibreria Standard (ja sigui del Document, o de l'usuari a "Les-meves-macros") aleshores es pot invocar directament.

A les macros del document, a la llibreria Standar, creo un  mòdul nou Module1 i hi poso el següent codi:

Function HelloWorld()
    HelloWorld = "macro invocada com una funció"
End Function

Aleshores, a qualsevol cel·la de la fulla de càlcul hi escric

=helloworld()

en  invocar les funcions no és distingeix majúscules i minúscules

apareixerà immediatament

 macro invocada com una funció

No té gaire ciència. La clau és que la macro es trobi a la llibreria "Standard".

Per a que quedi clar aquest punt, quan fem Eines-Macros-Organitza les macros-OpenOffice.org Basic obtenim això:



Veiem a la imatge com hi ha dues biblioteques Standard: una a "Les meves macros" i l'altre al document, que en aquest cas s'anomena "macro-funció-calc.ods".

En tot moment puc crear noves biblioteques de macros (libraries) fent click al botó "Organitzador...":


Des de la pestanya Biblioteques:


En la que puc triar on vull posar la biblioteca: De l'usuari, general, o del document:


En aquest document de la wiki de l'OpenOffice.org  proposen la següent tècnica per al cas que en que tinguem moltes macros i molt de codi  a les  macros. En aquesta situació, en comptes de sobrecarregar la biblioteca estàndard amb molts mòduls molt grossos, el que proposen es crear biblioteques auxiliars, aleshores, en la biblioteca Standard el que posaríem serien funcions de pont que l'únic que farien seria carregar la llibreria corresponent i invocar a la funció d'utilitat desitjada.   

dijous, 19 d’agost del 2010

Com dibuixar una escala semi-logarítmica amb OpenOffice.org Draw?

En aquest exercici dibuixo una escala semi-logarítmica. Aquesta mena de gràfiques es fan servir en tecnologia, per exemple a l'electrònica es fa servir per representar la resposta en freqüència dels circuits (els diagrames de Bode).

Aquest exercici ens servirà per aprendre com  dibuixar línies amb macros de OOoBasic.

Primer de tot, creo un nou document de Draw. I amb l'organitzador de macros creo un mòdul al document.

Faig servir aquest codi


Sub DibuixaLinia
    const cMarge = 1000
  
    Dim oPagina as Object
    Dim oLinea as Object
    Dim oPosition as Object
    Dim oSize as Object
    Dim iPagWidth as Integer
    Dim iPagHeight as Integer  
    Dim iWidth as Integer
    Dim iHeight as Integer  
    Dim v as Double
    Dim w as Double
    Dim x as Double
    Dim xx as Integer
    Dim y as Double
    Dim yy as Integer
    Dim iNumDecades as Integer
    Dim iNumVerticals as Integer

    iNumDecades = 7
    iNumVerticals = 10

    ' obté la pàgina de dibuix actual
    oPagina = ThisComponent.getDrawPages().getByName("page1")

    ' dimensions de la pàgina
    iPagWidth = oPagina.Width
    iPagHeight = oPagina.Height

    ' dimensions de l'àrea de dibuix, amb un marge de 1000px
    iWidth = oPagina.Width - (2 * cMarge)
    iHeight = oPagina.Height - (2 * cMarge)
  
    ' pinta escala logarítmica
    for w = 0 to (iNumDecades - 1)
        for v = 1 to 10
    x = log(v * (10 ^ w)) / log(10)
    xx = ((iWidth / iNumDecades) * x) + cMarge
     
    oLinea = ThisComponent.createInstance("com.sun.star.drawing.LineShape")
    oLinea.LineColor = RGB(0, 0, 255)
    oLinea.LineWidth = 2      
     
    oPosition = oLinea.Position
    oPosition.X = xx
    oPosition.Y = cMarge
    oLinea.Position = oPosition

    oSize = oLinea.Size
    oSize.Width = 0
    oSize.Height = iHeight
    oLinea.Size = oSize


    oPagina.add(oLinea)
 next
next

' dibuixa les divisions verticals
for w=0 to iNumVerticals
 oLinea = ThisComponent.createInstance("com.sun.star.drawing.LineShape")
oLinea.LineColor = RGB(0, 0, 255)
oLinea.LineWidth = 2

yy = ((iHeight / iNumVerticals) * w) + cMarge


oPosition = oLinea.Position
oPosition.X = cMarge
oPosition.Y = yy
oLinea.Position = oPosition

oSize = oLinea.Size
oSize.Width = iWidth
oSize.Height = 0
oLinea.Size = oSize

oPagina.add(oLinea)  
next
End Sub

Si executem aquesta macro, obtindrem el paper semi-logarítmic.

Una millora evident és factoritzar la funció que dibuixa la línia:

function Linea(int x, int y, int llarg, int ample, int vermell, int verd,  int blau, int gruix)
 dim oLinea as Object
 dim oPosition as Object
 dim oSize as Object

 oLinea = ThisComponent.createInstance("com.sun.star.drawing.LineShape")


oLinea.LineColor = RGB(vermell, verd, blau)
oLinea.LineWidth = gruix 

oPosition = oLinea.Position
oPosition.X = x
oPosition.Y = y
oLinea.Position = oPosition
oSize = oLinea.Size
oSize.Width = llarg
oSize.Height = ample
oLinea.Size = oSize

 Linea = oLinea


end sub

El resultat del programa anterior és:




dimarts, 17 d’agost del 2010

ListBox a formulari de l'OpenOffice.org

El control listbox dels formularis de l'OpenOffice.org permet sel·leccionar un o diversos valors d'una llista. La llista de valors es pot carregar des d'una taula, o des d'una consulta SQL, o bé amb valors arbitraris.

Carregar la listbox des d'una taula o amb un SQL és tan senzill com ajustar les propietats corresponents del control. En canvi, per carregar valors arbitraris cal fer-ho amb BASIC.

Com sempre, estic parlant de OpenOffice 3.2 en català sobre Ubuntu Lucid Lynx 10.04.

Vet aquí l'exemple. Primer de tot obro un nou document del Writer. Hi poso els següents components:

Amb l'organitzador de macros creo una macro nova dins del document, l'edito i hi afegeixo el codi per respondre als diferents events.

En prémer el botó Afegeix>> el que faré serà que el text que està a la caixa de text Afegeix opció s'afegeixi a la listbox.

Això ho faig amb el codi següent que associo a l'event de fer click al botó.

sub AfegeixALlista
Dim oDoc as object
Dim oText as Object
Dim oLlista as Object
Dim sLlista() as String

oDoc = ThisComponent.getDrawPage().Forms().getByIndex(0)
oText = oDoc.getByName("Text1")
oLlista = oDoc.getByName("Llista1")
sLlista = oLlista.StringItemList
bas_PushArray(sLlista, oText.Text)
oLlista.StringItemList = sLlista
Deselecciona
End sub

La funció bas_pushArray redimensiona un array per incrementar-ne el nombre d'elements en u, i afegeix el nou element.



' La següent sub extreta de
' http://www.oooforum.org/forum/viewtopic.phtml?t=38721
' very simple routine appending a single element to an array which
' may be undimensioned (LBound > UBound)

Sub bas_PushArray(xArray(),vNextElement)
Dim iUB,iLB as Integer
iLB = lBound(xArray())
iUB = uBound(xArray())
If iLB > iUB then
iUB = iLB
redim xArray(iLB To iUB)
else
iUB = iUB +1
redim preserve xArray(iLB To iUB)
endif
xArray(iUB) = vNextElement
End Sub


Per associar la sub d'afegir element a la llista a l'event de picar Return sobre el botó, faig el següent, associo la següent sub a l'event de"en prémer una tecla", i faig que la sub rebi com argument l'event. De l'event puc determinar quina tecla s'ha premut. Si la tecla és Return, aleshores executa la sub d'afegir a la listbox.

Sub AfegeixALListaKeyPressed(Event as Object)

' si està fet amb return, Ok
if Event.KeyCode = com.sun.star.awt.Key.RETURN then
AfegeixALlista
end if
end sub

Un cop he afegit l'element a la listbox el que faig és deseleccionar elements. També hauria pogut fer que l'element afegit fos l'element actualment seleccionat.

Per deseleccionar faig:


Sub Deselecciona
Dim oDoc as object
Dim oLlista as Object
Dim oLlista2 as Object
Dim EmptyList()

oDoc = ThisComponent.getDrawPage().Forms().getByIndex(0)

oLlista2 = oDoc.getByName("Llista2")
oLlista = oDoc.getByName("Llista1")

oLlista.SelectedItems = EmptyList()

oLLista2.SelectedItems = EmptyList()
End Sub

Per sel·leccionar un o molts valors (en aquest cas he de posar a true la propietat de múltiples valors) puc fer servir el següent codi. que he d'associar a l'event "L'estat de l'element ha canviat" de la listbox:

Sub ObteValors
Dim oDoc as object
Dim ollista2 as Object
Dim oLlista as Object
Dim iIndex() as Integer
Dim i as integer
Dim LlistaSeleccionades()

oDoc = ThisComponent.getDrawPage().Forms().getByIndex(0)
oLlista = oDoc.getByName("Llista1")
ollista2 = oDoc.getByName("Llista2")

' obté els índexos de les seleccionades

iIndex = oLlista.SelectedItems

' buida la listbox de seleccionades

ollista2.StringItemList() = LlistaSeleccionades()

' Redimensiona l'array auxiliar per a poder encabir-hi els nous valors

Redim LlistaSeleccionades(uBound(iIndex()))

' El plena amb les noves seleccionades

for i=0 to uBound(iIndex())
LlistaSeleccionades(i) = oLlista.StringItemList(iIndex(i))
next i

' l'assigna a la listbox

ollista2.StringItemList = LlistaSeleccionades
End sub


Si vull eliminar algun dels valors de la llista, primer he de tenir-lo seleccionat. Si no hi ha res seleccionat, ignora la crida (Sí, hi ha un goto!). El que fa és redimensionar (redim) a un element menys, preservant-ne el contingut, i deseleccionant al final.

Sub EliminarValor
Dim oDoc as object
Dim oText as Object
Dim oLlista as Object
Dim sValorObtingut as String
Dim iIndex() as Integer
Dim sNovaLlista() as String
Dim iDimInicial as Integer
Dim iDimFinal as Integer
Dim i,j as Integer

oDoc = ThisComponent.getDrawPage().Forms().getByIndex(0)
oLlista = oDoc.getByName("Llista1")
'xRay oLlista
iIndex = oLlista.SelectedItems
if UBound(iIndex()) = -1 and LBound(iIndex()) = 0 then goto SORTIR

iDimInicial = UBound(oLlista.StringItemList())

iDimFinal = iDimInicial - 1
if iDimFinal < idimfinal =" 0" j="0" i="0"> iIndex(0) then
sNovaLlista(j) = oLlista.StringItemList(i)
j= j + 1
end if
next i
oLlista.StringItemList() = sNovaLlista()
iIndex = oLlista.SelectedItems
Deselecciona
SORTIR:
End sub

Per eliminar tots, Empty Array a tot arreu i deseleccionar.


Sub EliminaTots
Dim oDoc as object
Dim oLlista as Object
Dim oLlista2 as Object
Dim EmptyList()


oDoc = ThisComponent.getDrawPage().Forms().getByIndex(0)
oLlista = oDoc.getByName("Llista1")
oLLista2 = oDoc.getByName("Llista2")

oLlista.StringItemList = EmptyList
oLLista2.StringItemList = EmptyList
Deselecciona
End sub

Finalment, puc ordenar (botons Puja i Baixa) la llista. Cal tenir un element selccionat, i si no, ignora la crida. Es tracta d'intercanviar les posicions dels elements de l'array que van seguits, i recarrregar l'array StringItemList amb el nou array reordenat.

Sub Puja
Dim oDoc as object
Dim oText as Object
Dim oLlista as Object
Dim iIndex() as Integer
Dim sAux as String
Dim EmptyList()
Dim i,j,k as Integer
Dim sNovaLlista() as String
dim sNovaLlistaSeleccionat(0) as Integer
Dim iDimLlista as integer

oDoc = ThisComponent.getDrawPage().Forms().getByIndex(0)
oLlista = oDoc.getByName("Llista1")
iIndex = oLlista.SelectedItems
' primer mira si té un element sel·leccionat
if UBound(iIndex()) = -1 and LBound(iIndex()) = 0 then goto SORTIR
' si el té, mira que no sigui el primer
if iIndex(0) > 0 then
' prepara un Array auxiliar
iDimLlista = UBound(oLlista.StringItemList())
Redim sNovaLlista(iDimLlista)

' obté els índexos a intercanviar

i = iIndex(0)
j = i - 1

' itera per carregar l'array auxiliar

for k=0 to iDimLlista
sNovaLlista(k) = oLlista.StringItemList(k)
next k

' intercanvia les posicions en l'array auxiliar


sAux = sNovaLlista(j)
sNovaLlista(j) = sNovaLlista(i)
sNovaLlista(i) = sAux

' carrega la llista

oLlista.StringItemList() = sNovaLlista()

' selecciona l'anterior

sNovaLlistaSeleccionat(0) = j
oLlista.SelectedItems = sNovaLlistaSeleccionat

end if

oLlista.Refresh()
SORTIR:
End sub

Sub Baixa
Dim oDoc as object
Dim oText as Object
Dim oLlista as Object
Dim iIndex() as Integer
Dim sAux as String
Dim EmptyList()
Dim i,j,k as Integer
Dim sNovaLlista() as String
dim sNovaLlistaSeleccionat(0) as Integer
Dim iDimLlista as integer

oDoc = ThisComponent.getDrawPage().Forms().getByIndex(0)
oLlista = oDoc.getByName("Llista1")
iIndex = oLlista.SelectedItems

' primer mira si té un element sel·leccionat
if UBound(iIndex()) = -1 and LBound(iIndex()) = 0 then goto SORTIR

' si el té, mira que no sigui l'últim
iDimLlista = UBound(oLlista.StringItemList())

if iIndex(0) < iDimLlista then ' prepara un Array auxiliar 

Redim sNovaLlista(iDimLlista) ' obté els índexos a intercanviar 
i = iIndex(0) 
j = i + 1 ' itera per carregar l'array auxiliar 
for k=0 to iDimLlista 
sNovaLlista(k) = oLlista.StringItemList(k) 
next k ' intercanvia les posicions en l'array auxiliar 
sAux = sNovaLlista(j) 
sNovaLlista(j) = sNovaLlista(i) 
sNovaLlista(i) = sAux ' carrega la llista 
oLlista.StringItemList() = sNovaLlista() ' selecciona l'anterior sNovaLlistaSeleccionat(0) = j 
oLlista.SelectedItems = sNovaLlistaSeleccionat 
end if oLlista.Refresh() 
SORTIR: 
End sub