LibreOffice Basic

Uit De Vliegende Brigade
Ga naar: navigatie, zoeken

Interface

  • Net als in Microsoftproducten, heb je macro's en volwaardige code. Deze laatste heet LibreOffice Basic, en is te bereiken via Tools » Macros » Organise macros » LibreOffice Basic
  • Ook de shortcut is hetzelfde: ALT-F11
  • Programmeercode heet altijd macros. Ook als het in LO-Basic is geschreven
  • Je kunt code opslaan in een geopend document, in een standaard-bibliotheek, of in een nieuwe bibliotheek. In dat laatste geval weet ik niet waar dat opgeslagen wordt
  • Eigen routines kun je vervolgens aanroepen in functies in Calc
  • Handig om de beveiligingsinstellingen te verlagen, als je met code aan de slag gaat: Tools » Options » Security » Macro security

Line wrapping

Line wrapping gaat op dezelfde manier als in VBA, en kent dus z'n beperkingen.

Simpel voorbeeld

function get_number_of_sheets()
'
' ======================================================
' Line wrapping: <spatie><underscore>
' ======================================================	
'
print ThisComponent. _
		sheets. _
		count

end function

Realistischer

' Dit werkt, maar de underscores maken het er niet mooier op
'
ThisComponent.sheets(0).copyRange _
( _
	Cell_dest.CellAddress, _
	Cell_org.RangeAddress _
)

' Dit ook: Weer: De underscores helpen weinig
'
ThisComponent.sheets(0).copyRange _
( _
 _
	Cell_dest.CellAddress, _
	Cell_org.RangeAddress _
)

' Misschien is dit de beste optie, als ik de input-argumenten graag onder elkaar wil afbeelden:
'
ThisComponent.sheets(0).copyRange( _
	Cell_dest.CellAddress, _
	Cell_org.RangeAddress _
)

Commentaar-regels

De apostrophe (') zorgt voor commentaarregels

Functie-aanroepen

Functies kunnen andere functies aanroepen. Voorbeeld:

function call_another_function()

	call get_number_of_rows()
	call get_number_of_rows()

end function		

Immediate Windows & interactie

  • Er is geen Immediate Windows zoals in VBA
  • Wel kun je functies direct uitvoeren (F5) of via Alt-F11
  • Output gaat naar een dialoogvenster, met het commando print.

API: Copy & paste cell ranges - copyRange

De copyRange-method lijkt de meest voor de hand liggende manier te zijn om een range van cellen te kopiëren - Werkt super!

Voorbeeld: 1 cel kopiëren

Cell_org = ThisComponent.sheets(0).getCellByPosition(0,0)
Cell_dest = ThisComponent.sheets(0).getCellByPosition(0,1)
ThisComponent.sheets(0).copyRange(Cell_dest.CellAddress,Cell_org.RangeAddress)

Wat dit voorbeeld aangeeft tav. de syntaxis van copyRange

copyRange(   destination cell address                         , source cell range address                           )
copyRange(   <cel-object>.CellAddress                         , <cel-object>.RangeAddress                           )
copyRange(   ThisComponent.sheets(0).<cel-object>.CellAddress , ThisComponent.sheets(0).<cel-object>.RangeAddress   )

Wat dit voorbeeld verder aangeeft

  • Cell_org en Cell_dest zijn cel-objecten
  • copyRange heeft van het cell_org-object de .RangeAddress-eigenschap nodig
  • copyRange heeft van het cell_dest-object de .CellAddress-eigenschap nodig

Voorbeeld 2: Range kopiëren

Spreekt voor zich:

function copy_and_paste_copyrange_3()
'
' ===============================================================================================================
' copyRange + Range - Dit is 'm!
' ===============================================================================================================

cell_org=ThisComponent.sheets(0).getCellRangeByPosition(0,0,0,2)
cell_dest=ThisComponent.sheets(0).getCellByPosition(0,3)

ThisComponent.sheets(0).copyRange _
( _
	cell_dest.CellAddress, _
	cell_org.RangeAddress _
)	

end function

API: Copy & paste cell ranges - getDataArray & setDataArray

Kopiëren en plakken van cell ranges kan op verschillende manieren, waaronder met getDataArray en setDataArray:

  • getDataArray en setDatArray zijn allebei methodes van een sheet- of cell-range-object
  • Ze schijnen hier thuis te horen: :: com :: sun :: star :: sheet :: - interface XCellRangeData
  • getDataArray vult een array met de aangegeven waardes, die worden opgeslagen als double of als string.

Voorbeeld

source_array=ThisComponent.Sheets(0).getCellRangeByName("A1:C1").getDataArray()
ThisComponent.Sheets(0).getCellRangeByName("A3:C3").setDataArray(source_array)

getDataArray is hier een methode van sheets.getCellRangeByName:

ThisComponent.Sheets(0).getCellRangeByName("A1:C1")

Dit object bevat behalve de waardes van de cellen, tevens zaken zoals cel-opmaak. Vandaar de getDataArra-methode.

API: Export naar TSV-bestand

Dit werkt prima:

function export_csv()

Dim Propval(1) as New com.sun.star.beans.PropertyValue
Propval(0).Name = "FilterName"
Propval(0).Value = "Text - txt - csv (StarCalc)"
Propval(1).Name = "FilterOptions"
Propval(1).Value ="9,34,0,1,1" ' Eerste & tweede veld zijn ASCII-codes
FileName = "/var/export/calc.tsv"  'Change to whatever file name you want
FileURL = convertToURL(FileName)
ThisComponent.StoreAsURL(FileURL, Propval())

end function

FilterOptions

Deze instellingen worden (deels?) opgeslagen in de FilterOptions-string
  • Eerste getal: Scheidingsteken tussen velden (ASCII-code)
  • Tweede getal: Omsluitingsteken voor tekstvelden (ASCII-code). Op het moment dat tab wordt gebruikt als scheidingsteken, lijkt dit veld niet meer te worden gebruikt
  • Derde getal: ?
  • Vierde getal: ?
  • Vijfde getal: ?

ASCII-codes voor FilterOptions

<tab> -  9
"     - 34 
;     - 59

Casus (okt. 2016)

Ik wil een rekenblad met zo'n 30 tabbladen importeren in MySQL. Daarbij wil ik die tabbladen samenvoegen, waarbij de naam van het tabblad als extra kolom wordt opgevoerd. Het liefst wordt dat geaggregeerde tabblad volautomatisch geëxporteerd in een passen formaat (TSV, etc.).

Gezochte functionaliteiten:

  • Voeg een nieuw tabblad toe
  • Voeg een nieuwe kolom toe aan een tabblad

Voeg een nieuw tabblad toe

function insert_sheet()

' ======================================================
' Insert sheet 'Aggregate' (if not exists)
' ======================================================	

dim doc as object
dim sheet as object

doc = ThisComponent

if ThisComponent.sheets.hasByName("Aggregate") then
	' Do nothing: Sheet "Aggregate" bestaat al
else

	sheet = doc.createInstance("com.sun.star.sheet.Spreadsheet")
	doc.sheets.insertByName("Aggregate",sheet)	

end if	

Voeg een kolom toe

function insert_column()

' ======================================================
' Insert column on current sheet
' ======================================================	
'
dim doc as object
dim sheet as object
dim new_column as object

doc = Thiscomponent
sheet = doc.Sheets(0)	' Select first sheet (counting starts at 0)

sheet.Columns.insertByIndex(0,1)	' At position 0, insert 1 column

end function

Get number of sheets

function get_number_of_sheets()

' ======================================================
' Get the number of sheets in the current file
' ======================================================	
'
' In dit geval niet gewerkt met tussenliggende variableen
' Op zich handig, zo'n abstractielaag, maar hier overkill
'
print ThisComponent.sheets.count

end function

Insert column on each sheet

function insert_column_on_each_sheet()
'
' ======================================================
' Insert column on each sheet
' ======================================================	
'
for i=0 to ThisComponent.sheets.count-1

	Thiscomponent.sheets(i).columns.insertbyindex(0,1)

next

end function

Get number of rows

function get_number_of_rows()
'
' ======================================================
' Get number of rows
' ======================================================	
'
' * Dat gaat alleen indirect, mbv. een cursor & gotEndOfUsedArea
' * Base=0. Dus het getal is 1 cijfer te laag
'
dim o_cursor as object

o_cursor = ThisComponent.Sheets(0).createCursor()
o_cursor.gotoEndOfUsedArea(false)

print o_cursor.getRangeAddress().EndRow

end function

Get sheet name

function get_sheet_name()

	' ======================================================
	' Get sheet name
	' ======================================================	
	' 
	get_sheet_name = thiscomponent.getcurrentcontroller.activesheet.GetName

end function

Insert sheet name on each row

function insert_sheet_name_on_each_row()

' ======================================================
' Insert sheet name on each row in new column
' ======================================================	

dim o_cursor as object
dim s_sheetname as string
dim i_rows as integer

' Get number of rows » i_rows
' ===================================
'
o_cursor = ThisComponent.Sheets(0).createCursor()
o_cursor.gotoEndOfUsedArea(false)
i_rows = o_cursor.getRangeAddress().EndRow+1

' Sheet name » s_sheetname
' ===================================
'
s_sheetname = ThisComponent.sheets(0).Getname

' Insert column
' =============
'
Thiscomponent.sheets(0).columns.insertbyindex(0,1)

' Insert sheet name on each row
' ============================

for i = 0 to i_rows-1

	ThisComponent.sheets(0).getCellByPosition(0,i).String=s_sheetname

next	

' Debug
' =====
'
' print s_sheetname & " " & i_rows

end function

Copy & paste cell ranges

Zie aparte hoofdstuk voor details:

function copy_and_paste_copyrange_3()
'
' ===============================================================================================================
' copyRange + Range - Dit is 'm!
' ===============================================================================================================

cell_org=ThisComponent.sheets(0).getCellRangeByPosition(0,0,0,2)
cell_dest=ThisComponent.sheets(0).getCellByPosition(0,3)

ThisComponent.sheets(0).copyRange _
( _
	cell_dest.CellAddress, _
	cell_org.RangeAddress _
)	

end function

Bronnen

Algemeen

Code

Copy & paste cell ranges