tirsdag den 25. oktober 2016

Regneark i Calc (Årskalender)

Efter at vi tidligere har gennemgået en række dato-funktioner i Calc regneark, vil jeg vise et praktisk eksempel. Makroen herunder danner en to-sidet kalender med helligdage, med seks måneder på hver side. Der er forklarende kommentarer i koden.

Eksempel:




REM  *****  BASIC  *****

Sub Main

this_year = InputBox ("Indtast årstal mellem 1583 og 3000", "Vælg årstal", year(date))

'Kontroller at årstallet brugbart
If (1583 > val(this_year) OR val(this_year) > 3000) then
MsgBox("Årstallet skal være mellem 1583 og 3000", 48, "Fejl")
Stop
End If

Cal_name="Kalender " & this_year

'Kontroller at dokumentet er et regneark
my_doc = ThisComponent
If not my_doc.SupportsService("com.sun.star.sheet.SpreadsheetDocument") then
MsgBox("Dokumentet skal være et regneark", 48, "Fejl")
Stop
End if

my_sheets = my_doc.Sheets
antal=my_sheets.count

If NOT my_sheets.hasbyName(Cal_name) Then

my_sheets.insertNewByName(Cal_name, antal)

End If

the_sheet = my_sheets.getByName(Cal_name)

'Formatér siden
oStyles = my_doc.StyleFamilies.getByName("PageStyles")
    oPstyle = oStyles.getByName(the_sheet.PageStyle)
    oPstyle.FooterIsOn = False
    oPstyle.FooterIsOn = False
    oPstyle.TopMargin=500

'Første linje   
  oRange = the_sheet.getCellRangeByName("A1:L1")
  oRange.merge(True)
  oCell= the_sheet.getCellByPosition(0, 0)
  oCell.String = Cal_name
  FormatDark(oCell)

  oRange = the_sheet.getCellRangeByName("M1:X1")
  oRange.merge(True)
  oCell= the_sheet.getCellByPosition(12, 0)
  oCell.String = Cal_name
  FormatDark(oCell)
 
'Nederste linje    
  oRange = the_sheet.getCellRangeByName("A34:X34")
  oRange.merge(True)
  oCell = the_sheet.getCellRangeByName("A34")
  FormatDark(oCell)

For m = 1 to 12
'Overskriften

  oRange = the_sheet.getCellRangeByPosition(m*2-1-1,1,m*2-1,1)
  oRange.merge(True)
  the_cell = the_sheet.getCellByPosition(m*2-1-1, 1)
  the_cell.String=TheMonthName(m)
  the_cell.HoriJustify = 2
  the_cell.CellBackColor=rgb(150,150,150)
  the_cell.CharColor=rgb(255,255,255)

'Justerer kolonnebredder
  Column= the_sheet.Columns(m*2-1-1)
  Column.Width=550
  Column= the_sheet.Columns(m*2-1)
  Column.Width=4000

Next m 

'For hver måned...
For m = 1 to 12

'Dag for dag...
For d= 1 to Day(LastDayOfMonth(DateValue("1-" & m & "-" & this_year))
the_date=DateValue(d & "-" & m & "-" & this_year)

'Skriv datoen i første kolonne
the_cell = the_sheet.getCellByPosition(m*2-1-1, d + 1)
the_cell.Value=the_date
the_cell.NumberFormat=109
'Formatter cellen
FormatLight(the_cell)

'Skriv ugedagens bogstav i anden kolonne
the_Othercell = the_sheet.getCellByPosition(m*2-1, d + 1)

'Lørdag og søndag formateres
    Select Case WeekDay(the_date)
    Case 1
        FormatLight(the_Othercell)
        the_Othercell.String="S"
    Case 2
        the_Othercell.String="M"
    Case 3
        the_Othercell.String="T"
    Case 4
        the_Othercell.String="O"
    Case 5
        the_Othercell.String="T"                       
    Case 6
        the_Othercell.String="F"
    Case 7
        FormatLight(the_Othercell)
        the_Othercell.String="L"               
End Select

'Tilføj helligdage
generic_date= left(STR(the_date),5)
oFA = createUnoService( "com.sun.star.sheet.FunctionAccess" )

'Faste helligdage
Select Case generic_date

case "01-01"
        FormatLight(the_Othercell)
        the_Othercell.String=the_Othercell.String & " Nytårsdag"
case "05-06"
        FormatLight(the_Othercell)
        the_Othercell.String=the_Othercell.String &" Grundlovsdag"
case "24-12"
        FormatLight(the_Othercell)
        the_Othercell.String=the_Othercell.String &" Juleaften"
case "25-12"
        FormatLight(the_Othercell)
        the_Othercell.String=the_Othercell.String &" Juledag"
case "26-12"
        FormatLight(the_Othercell)
        the_Othercell.String=the_Othercell.String &" 2. Juledag"               

'Skæve helligdage
'Der findes ingen påske-beregning i Basic, men vi kan eksekvere regnearksfunktionen =Påskedag)
        the_Othercell.String=the_Othercell.String &" Påskedag"
case Left(STR(CDate(oFA.callFunction( "EASTERSUNDAY", array( this_year ) )+1 ) ),5)
        FormatLight(the_Othercell)
        the_Othercell.String=the_Othercell.String &" 2. Påskedag"     
case Left(STR(CDate(oFA.callFunction( "EASTERSUNDAY", array( this_year ) )-2 ) ),5)
        FormatLight(the_Othercell)
        the_Othercell.String=the_Othercell.String &" Langfredag"              
case Left(STR(CDate(oFA.callFunction( "EASTERSUNDAY", array( this_year ) )-3 ) ),5)
        FormatLight(the_Othercell)
        the_Othercell.String=the_Othercell.String &" Skærtorsdag"
case Left(STR(CDate(oFA.callFunction( "EASTERSUNDAY", array( this_year ) )+49 ) ),5)
        FormatLight(the_Othercell)
        the_Othercell.String=the_Othercell.String &" Pinsedag"      
case Left(STR(CDate(oFA.callFunction( "EASTERSUNDAY", array( this_year ) )+50 ) ),5)
        FormatLight(the_Othercell)
        the_Othercell.String=the_Othercell.String &" 2. Pinsedag"   
case Left(STR(CDate(oFA.callFunction( "EASTERSUNDAY", array( this_year ) )+26 ) ),5)
        FormatLight(the_Othercell)
        the_Othercell.String=the_Othercell.String &" St. Bededag"   
        case Left(STR(CDate(oFA.callFunction( "EASTERSUNDAY", array( this_year ) )+39 ) ),5)
        FormatLight(the_Othercell)
        the_Othercell.String=the_Othercell.String &" Kr. Himmelfartsdag"            
End Select

Next d    'Dags-loopet slutter
Next m    'Måneds-loopet slutter

End Sub

Function LastDayOfMonth(d As Date) As Date
'Beregn hvor mange dage i måneden
  Dim nYear As Integer
  Dim nMonth As Integer
  nYear = Year(d)        'Current year
  nMonth = Month(d) + 1  'Next month, unless it was December.
  If nMonth > 12 Then    'If it is December then nMonth is now 13
    nMonth = 1           'Roll the month back to 1
    nYear = nYear + 1    'but increment the year
  End If
  LastDayOfMonth = CDate(DateSerial(nYear, nMonth, 1)-1)
End Function

Function TheMonthName(m)
'Omsætter månedstal til månedsnavn
CompatibilityMode(True)
TheMonthName=MonthName(m)
End Function

Sub FormatLight(oCell)
'Formatér lys grå
        oCell.CellBackColor=rgb(200,200,200)
        oCell.CharColor=rgb(255,255,255)
End Sub

Sub FormatDark(oCell)
'Formatér overskrift
  oCell.CharHeight=18
  oCell.HoriJustify = 2
  oCell.CellBackColor=rgb(100,100,100)
  oCell.CharColor=rgb(255,255,255)
End Sub 

fredag den 21. oktober 2016

Datoer i Calc med makroer

I makroen hvor vi importerede CSV-filen ved at indlæse linje for linje, anvendte vi the_cell.Value = [datoværdi]. Lad os kige lidt mere på, hvordan datoer håndteres i LibreOffice i makroer.

Vi skal se dato-format problemet i to perspektiver:
1) Datoformatet i variable i makroen
2) Datoen, når den indsættes i eller læses fra en celle.

Det er vigtigt at skelne tydeligt mellem de to perspektiver, da resultatet ellers kan vise sig overraskende (forkert).

Denne artikel omhandler andet punkt, nemlig håndtering af datoer i regnearkets celler.

I eksemplet importerede vi datoen fra en CSV-fil, og derfor var udgangspuntket en tekststreng, som vi så behandlerede således:
t= split(s, "-", 3)
s=DateSerial(t(2), t(1), t(0))


Og vi indsatte så resultatet i cellen sådan:
my_cell.String=s

Det betyder at resultatet er en tekststreng (.String).

Vi burde have gjort det anderledes, nemlig med
my_cell.Value=s
Problemet er dog at resultatet vises i regnearket som datoværdier, f.eks. 42659

Det håndterer vi ved at formatere cellen således:
my_cell.Value=s
my_cell.NumberFormat=36   


Tallet .NumberFormat=36 viser datoen som 09-10-2016, hvilket er præcist som ønsket. Tallet 36 fandt jeg frem til ved at formatere en celle manuelt, og så aflæse tallet
Print my_cell.NumberFormat

Her er en makro som demonstrerer det meste:

REM  *****  BASIC  *****

Sub Main
Dim my_date As Date

my_doc = ThisComponent
my_sheets = my_doc.Sheets
antal=my_sheets.count

If NOT my_sheets.hasbyName("Dato") Then

my_sheets.insertNewByName("Dato", antal)

End If

the_sheet = my_sheets.getByName("Dato")
the_sheet.TabColor= RGB(0, 0, 100)

rem --------------------
my_date = DateValue("20 Jan 2016")
my_cell = the_sheet.getCellbyPosition(1,1)
my_cell.Value = my_date
my_cell.NumberFormat=36   

Print my_cell.Value
Print CDate(my_cell.Value)
Print IsDate(my_cell.Value)

End Sub



Lidt om datoer (i makro)

I makroen hvor vi importerede CSV-filen ved at indlæse linje for linje, anvendte vi the_cell.Value = [datoværdi]. Lad os kigge lidt mere på, hvordan datoer håndteres i LibreOffice i makroer.

Vi skal se dato-format problemet i to perspektiver:
1) Datoformatet i variable i makroen
2) Datoen, når den indsættes i eller læses fra en celle.

Det er vigtigt at skelne tydeligt mellem de to perspektiver, da resultatet ellers kan vise sig overraskende (forkert).

Denne artikel omhandler første punkt, nemlig håndtering og bearbejdning af datoer med makroer. Datofunktionerne er ikke afhængige af Calc, men fungerer også med datoer i f.eks. Writer.

I makroen, når vi arbejder med datoer i variable, kan vi tænk på datoer som to forskellige typer tekst: En lokaliseret (dansk, engelsk, ...) version og en international version (ISO 8601 date format). I bund og grund fungerer begge typer fuldstændig uden problemer, men lokaliseret dato er standard, og ISO 8601 (YYYY-MM-DD) er kun relevant, når vi vælger det eksplicit.

Ikke desto mindre regner LibreOffice med en intern dato-værdi, hvor 0 svarer til 29/12/1899: -1
30/12/1899: 0
31/12/1899: 1
01/01/1900: 2
01/02/1900: 3
01/01/2001: 36892

Datokonvertering:
  • CDate: Konverter et tal (datoværdi) eller en tekst til en dato.
  • DateValue: Konverterer en formateret tekst (i intervallet 1. december 1, 1582 til 31 December 9999) til en dato-værdi, som ikke har tidsinformationer. Datoen 1. december 1582 svarer ca. til det tidspunkt, hvor den gregorianske kalender blev indført. Se https://da.wikipedia.org/wiki/Gregorianske_kalender
  • CDateFromIso: Konverterer til en dato fra en ISO 8601-dato.
  • CDateToIso: Konverterer en dato til en ISO 8601-dato.
  • IsDate: Er teksten korrekt formateret som dato? Brug IsDate til at afprøve om en tekst indeholder en korrekt formateret dato.
Datoer lige omkring 30/12 1899 kan udløse fejl i tidligere versioner af LibreOffice, men det burde ikke være et problem i nyere versioner.

Når vi så har en dato(værdi), kan vi udlede nogle interessante informationer:
  • Year(date): Returnerer årstallet af en dato som et heltal.
  • Month(date): Returnerer månedsnummeret af en dato som et heltal.
  • Day(date): Returnerer dagstallet af en dato som et heltal.
  • WeekDay(date): Returnerer et heltal fra 1 til 7, som svarer til dage i ugen (søndag til lørdag).
Eksempel
Sub test
my_date=DateValue("19 Januar 1965")
Print isDate(my_date) 'True
Print my_date         '19/01/1965
Print year(my_date)   '1965
End sub


Funktionen DatePart gør noget lignende, nemlig udleder en konkret information
Print DatePart("yyyy",my_date) 'Årstal
Print DatePart("q",my_date)    'Kvartal
Print DatePart("m",my_date)    'Måned
Print DatePart("y",my_date)    'dag i året
Print DatePart("w",my_date)    'Ugedag
Print DatePart("d",my_date)    'Dag


DatePart splitter altså en dato ad i enkeltdele, og vi har en tilsvarende funktion til at samle delene i en dato igen:
Print DateSerial(1965, 1, 19)  '19/01/1965

Vær opmærksom på at DateSerial har en fejl, som betyder at datoen før år 100 konverteres forkert, idet der lægges 1900 til årstallet:
Print DateSerial(90, 1, 19)    '19/01/1990

Vi kan manipulere datoen med funktionen DateAdd sådan:
DateAdd("d", 3, my_date)       'Læg tre dage til datoen
DateAdd("m", 3, my_date)       'Læg 3 måneder til datoen
DateAdd("yyyy", -3, my_date)   'Træk 3 år fra datoen


DateDiff anvender tilsvarende tekstparameter til at formatere svaret fra en subtraktion:
my_date=DateValue("19 Januar 1965")
Print DateDiff("yyyy", my_date, Date)  'Jeg er 51 år
Print DateDiff("d",    my_date, Date)  'Mere præcist 18903 dage

tirsdag den 18. oktober 2016

Sjældent brugt: Lad tekst følge en streg

Lidt ovre i DTP-afdelingen har jeg eksperimenteret lidt med at lave sjove og dekorative skrifteffekter. Fontwork-galleriet giver en række gode standardløsninger, men du har faktisk mulighed for at skrive tekst, som flyder langs en frihåndstegnet streg eller en bue.

Første trin er at tegne en streg ved hjælp af en af funktionerne Streg, Fihåndslinje, Kurve eller Polygon. Jeg vælger i dette tilfælde at tegne en simpel kurve med Kurveværktøjet.

Nu dobbeltklikker jeg på kurven og tilføjer en tekst: ”LibreOffice nyhedsbrev”. Resultatet er at teksten vises skråt (lidt tilfældigt) inde i kurven.

Marker nu kurven og vælg fra menuen Formater – Tekstboks og figur – Fontwork, hvorefter du ser en flydende dialogboks, med navnet Fontwork. Dialogen er kun synlig, når din figur er markeret.


I dialogens øverste linje kan du vælge hvordan teksten skal følge stregen.
  • Vælg f.eks. ikon nr. 2 fra venstre, som hedder ”Rotèr”.
  • Vælg nu på anden ikonlinje det sidste (længst til højre) ikon ”Automatisk tekststørrelse”.
  • På den nederste ikonlinje, klik på det første ikon ”Kontur” for at fravælge dette. Linje du tegnede er nu skjult.
  • Fortsæt selv med eksperimenter, f.eks. ændre skriftfarve og tilføje tekstkontur eller slagskygge.


Det er også muligt at benytte figurer som udgangspunkt for Fontwork, men det kræver lidt ekstra arbejde. Vi skal f.eks. bruge Draw i stedet for Writer.
Første udfordring for os er at værktøjet Fontwork (Fontwork-dialogen) ikke er aktiv i Draw, hvilket vi kan reparere selv. Vi skal ind i Funktioner - Tilpas og tilføje Fontwork enten til en menu eller en værktøjslinje. Du finder Fontwork i kategorien Formater. Jeg har tilføjet Fontwork til menuen Formater.
  1. Opret nu en figur, f.eks. en cirkel eller lignende.
  2. Højreklik på cirklen og vælg Konverter - Til kurve.
  3. Dobbeltklik og skriv den tekst du ønsker at få vist
  4. Marker objektet og aktiver Fontwork-dialogen (som du placerede i en menu eller på en værktøjslinje tidligere.
  5. Vær kreativ!

Når du er færdig kan du klippe-klistre resultatet til et Writer-dokument, eller du kan tilføje resultatet til Galleriet.