Viser opslag med etiketten makro. Vis alle opslag
Viser opslag med etiketten makro. Vis alle opslag

søndag den 28. april 2019

Programmering med Python

Programmeringssproget Python vinder stor udbredelse for tiden, hvilket måske hænger sammen med at det er et programmeringssprog som er open source, og som ikke er afhængig af et bestemt operativsystem. Python er, lige som Java, helt platformsuafhængig. Til gengæld forudsætter afvikling af programmerne, at brugeren har et afviklingsmiljø installeret.

Python bruges i øvrigt som makrosprog i mange open source programmer, f.eks. Inkscape, Gimp og LibreOffice.

Når du downloader og installerer LibreOffice, får du helt automatisk et Python afviklingsmiljø med i pakken.

Python i LibreOffice

Hvis du programmerer makroer i LibreOffice Basic, benytter du den indbyggede IDE (integrated Development Environment), men til Python må du selv vælge hvilket udviklingsmiljø du gerne vil bruge. Der er mange at vælge imellem. Geany, PyCharm og Eclipse er bare eksempler.

Der er tre ting du skal være opmærksom på, inden du går i gang:
  1. Pythonfilen skal være placeret i dit brugerkatalog under user/Scripts/python/ eller en folder derunder. På min PC er det her: /home/leif/.config/libreoffice/4/user/Scripts/python
  2. Den makro (Python-funktion) du gerne vil stille til rådighed i LibreOffice brugergrænsefladen skal "udstilles" ved at tilføje navnet til Tuple-variablen g_exportedScripts.
  3. For at tilgå dokumentobjekter i LibreOffice skal du benytte XSCRIPTCONTEXT ved at kalde en af funktionerne getDocument (), getInvocationContext (), getDesktop (), getComponentContext (). Når først du har gjort det, kan du tilgå LibreOffice ad bagvejen, altså programmere makroer.
Her er et klassisk eksempel (Hello world):

# -*- coding: utf-8 -*-
# !/usr/bin/env python

import uno

def HelloWorld():
    """Write Helo World in Writer"""

    try:
        ctx = remote_ctx                   # use in development
    except:
        ctx = uno.getComponentContext()    # use in production

    # get desktop
    desktop = ctx.getByName("/singletons/com.sun.star.frame.theDesktop")
    # get document
    document = desktop.getCurrentComponent()
    # access the document's text property
    text = document.Text
    # create a cursor
    cursor = text.createTextCursor()
    # insert the text into the document
    text.insertString(cursor, "Hello World", 0)

HelloWorld()

g_exportedScripts = HelloWorld,


Du kan læse mere om Python i LibreOffice her: https://help.libreoffice.org/6.3/en-US/text/sbasic/python/main0000.html

Du kan læse mere om XSCRIPTCONTEXT her: https://wiki.openoffice.org/wiki/Python/Transfer_from_Basic_to_Python

Der er masser af muligheder på internettet for at lære programmering med Python. Du kan f.eks. tage et komplet kursus på Massachusetss Institute of Technology (MIT) helt gratis: https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-0001-introduction-to-computer-science-and-programming-in-python-fall-2016/

tirsdag den 11. september 2018

Konvertering af (mange) csv-filer

Det er ingen hemmelighed at LibreOffice er noget af en schweizerkniv når det kommer til at konvertere dokumenter af enhver art. Gamle præsentationer fra Mac eller regneark fra Super Calc. LibreOffice klarer det med et snuptag. Det kan endda gøres med en kommandolinje, f.eks.

"C:\Program Files (x86)\LibreOffice\program\scalc.exe" --convert-to pdf  C:\Users\leil\Documents\test\testregneark.xlsx --outdir C:\Users\leil\Documents\test

Her konverteres et regnearksdokument til PDF.

Der skal ikke meget til for at lave en løkke, som rekursivt konverterer alle xlsx-filer i en bestemt folder.

Men...

Import af CSV-filer er lidt mere kompliceret.

Når du importerer en CSV-fil med LibreOffice ledes du altid igennem et skærmbillede, som ser nogenlunde sådan ud:



Hvis vi skal konvertere en mængde af (ens) CSV-filer skal vi i kommandolinjen udfylde skærmbilledet ved at anvende en parameter "-infilter" som er temmelig kompliceret at regne ud. Det er reelt umuligt at regne sig frem til, at det skal se sådan ud: --infilter="csv:59/44,34,12,1,1/4,0,false,true,true,false,false".

Der er en meget let måde at finde denne parameters værdi på. Det kræver en lille makro:

Sub showFilterOptions
Dim args(),i%
args() = thisComponent.getArgs
    for i = 0 to uBound(Args())
        if args(i).Name = "FilterOptions" then inputbox "","",args(i).value
    next
End Sub


Hvis du manuelt importerer en CSV-fil (og udfylder skærmbilleder korrekt), kan du umiddelbart efter køre denne makro. LibreOffice "husker" nemlig hvad du senest har importeret, og med denne makro får du oplyst hvad der er gemt.



Og import-kommandoen er derfor:

"C:\Program Files (x86)\LibreOffice\program\scalc.exe" --convert-to pdf --infilter="csv:59/44,34,12,1,1/4,0,false,true,true,false,false" C:\Users\leil\Documents\CSV\testdokument.csv --outdir C:\Users\leil\Documents\CSV

onsdag den 4. april 2018

LibreOffice med kommandolinje

LibreOffice kan startes med forskellige parametre i en kommandolinje, hvor du f.eks. kan starte programmet uden opstartsskærmbilede eller i minimeret tilstand. Lera Goncharuk har skrevet en artikel på engelsk, som forklarer mulighederne: https://dnimruoynepo.blogspot.dk/2016/12/command-line-arguments-in-libreoffice.html. Lera er en af de meget aktive brugere i Rusland.

Det artiklen desværre ikke omtaler er muligheden for at åbne et dokument og samtidig eksekvere en makro.

I eksemplet har jeg et almindeligt tekstdokument (Dansk.odt) og en makro installeret (Generate.LoremIpsum.Main). Makroen er fra en udvidelse, som findes her: https://extensions.libreoffice.org/extensions/magenta-lorem-ipsum-generator.

Hvad makroen gør er egentlig ikke vigtig i denne sammenhæng.

Fra en kommandolinje kan jeg skrive: soffice --nologo /home/leif/Dokumenter/LibreOffice/Dansk.odt macro:///Generate.LoremIpsum.Main og dokumentet åbnes, og en dialogboks fra makroen vises. (Vær opmærksom på at stierne i eksemplet er fra min Linux-maskine. På Windows skal du selv finde stierne.)

Forklaring:
  • soffice starter LibreOffice
  • --nologo betyder at vi ikke ønsker splash-skærmen
  • /home/leif/Dokumenter/LibreOffice/Dansk.odt er dokumentet
  • macro:///Generate.LoremIpsum.Main er makroen
 Resultatet:





For at starte en kommandolinje i Windows, skal du klikke på "Kør" og så skrive CMD [enter].

Du kan oprette en genvej på skrivebordet, hvor du tilføjer hele kommandolinjen, og genvejen vil altså fungere som en genvej direkte til at udføre makroen.

Perspektiver
I kombination med andre parametre som f.eks. "--invisible" kan du på denne måde behandle dokumenter maskinelt, uden at programmet LibreOffice viser sig på skærmen.

onsdag den 21. februar 2018

Godt nyt til makro-programmøren

Jeg har flere gange skrevet om makroprogrammering, hvilket du kan se her: https://libreofficedk.blogspot.dk/search/label/makro

Jean François Nifenecker fra Frankrig har lavet tre referencekort (eller snydeark), som er blevet oversat til engelsk, og udgivet som en del af dokumentationen:
  • LibeOffice BASIC IDE
  • LibreOffice BASIC Overview
  • LibreOffice BASIC Calc
Hvert referencekort er på to sider, og kan eksempelvis udskrives og lægges under dit tastatur. Du kan finde reference-kortene her: https://documentation.libreoffice.org/en/english-documentation/macro/

lørdag den 10. december 2016

Mere om fejlhåndtering

Jeg har tidligere skrevet om fejlhåndtering i arktiklen "Håndtering af fejl" her: http://libreofficedk.blogspot.dk/2016/12/handtering-af-fejl.html

I den tidligere artikle beskrev jeg eksempler på håndtering af konkrete fejl, simpelthen ved at undersøge om en "ulovlig" situation er opstået, umiddelbart før en kritisk handling udføres. I denne artikel vil jeg komme ind på nogle af de indbyggede funktioner LibreOffice har til fejlhåndtering:
  • Erl: Linjenummer hvor seneste fejl opstod
  • Err: Fejlnummer for senestre opståede fejl
  • Error eller Error(error_number): Fejlbesked (tekst) for seneste fejl eller for et specifikt fejlnummer
  • On Error:
  • On Error Resume Next: Ignorer fejl og fortsæt makroen.
  • On Error GoTo 0: Nulstil den aktuelle error handler.
  • On Error GoTo LabelName: Ved fejl gå til ...[etikket]
  • CVErr: Konverterer et udtryk til et fejlobjekt

Lad os se på et par eksempler:

Sub test1
  On Error Goto BadError

Print UBound(Array1)

BadError:
  If Err <> 0 Then
    Print err
    Print erl
    Print Error(err)
End if
End sub


Der vil opstå en fejl, da variablen Array1 ikke eksistrer og derfor ikke har et UBound. Linjen Print UBound(Array1) fejler.

BadError svarer:
  • 91 (Fejlnummer)
  • 36 (Linjenummer)
  • Objektvariabel ikke initialiseret. (Fejlbesked som tekst) .
Her er et lidt mere omfattende eksempel:

Sub ExampleError
  On Error Goto BadError        'Sæt error handleren op
  Print 1/ CInt(0.2)            'Udfør noget ulovligt

BadError:                       'Error handler starter her
  Dim s As String               'Akkumuler beskeden
  Dim oldError As Integer       'Fejlnummeret
  Dim lineNum As Integer        'Linjenummeret
  If Err <> 0 Then              'Tjek Err
    oldError = Err              'Gem fejlnummeret
    lineNum = Erl               'Gem linjenummeret
    s = "Før nulstillet error handler " & CHR$(10) &_
        "Fejlnummer " & Err & " opstod ved linjenummer " & Erl & CHR$(10) &_
        "Fjlbesked: " & Error() & CHR$(10)
  End If
  On Error Goto 0               'Nulstil error handler
 
  REM Nu er der ingen ting at vise
  s = s & CHR$(10) & "Efter nulstillet error handler:" & CHR$(10) &_
      "Fejlnummer " & Err & " opstod ved linjenummer " & Erl & CHR$(10)
     
  REM Use the saved information
  s = s & CHR$(10) & "Fejlinformation blev gemt i variable " & CHR$(10) & "så vi kan finde dem frem senere:" & CHR$(10) &_
      "Fejlnummer " & oldError & " opstod ved linjenummer " &_
      lineNum & CHR$(10) & "Fjlbesked: " & Error(oldError)
  MsgBox s, 0, "Error Handling"
End Sub



Håndtering af fejl

I de tidligere artikler har jeg ikke gjort meget ud af at håndtere eventuelle fejlsituationer. I denne artikle ser vi på håndtering af meget konkrete fejlsituationer, som vi kan forudse.

En typisk situation som vi ALTID skal tage stilling til, er hver gang vi skal udføre matematisk division. Det kan nemlig meget let gå galt, hvis vi ikke først lige undersøger om vi har 0 (nul) i nævneren (det er forbudt at dele med nul). Et overordnet råd er derfor ALTID kontrolle nævneren før du udfører division.

Et andet eksempel er i artiklen Tekstdokumenters titel (læs artiklen her: https://libreofficedk.blogspot.dk/2016/12/tekstdokumenters-titel.html ), hvor jeg lavede følgende meget simple program:

Sub TitelFraFelt
oMaster = thisComponent.getTextFieldMasters().getByName("com.sun.star.text.fieldmaster.User.Titel")
thisComponent.DocumentProperties.Title = oMaster.Content
End Sub


Der er umiddelbart to situationer som vi bør tage stilling til:
  • Hvad nu hvis dokumentet ikke er et tekstdokument?
  • Hvad nu hvis feltet "Titel" ikke eksisterer?
Det første punkt bør vi altid kontrollere, når vi arbejder med makroer specifikke for tekstdokuemnter.

Denne lille rutine kontrollerer for om dokumentet er et tekstdokument:

If NOT ThisComponent.supportsService("com.sun.star.text.TextDocument") Then
  MsgBox "Det aktuelle dokument er ikke et Writer tekstdokument", 48, "Fejl"
  Exit Sub
End If


Det andet punkt kan vi løse vha. denne linje:

If thisComponent.getTextFieldMasters().hasByName("com.sun.star.text.fieldmaster.User.Titel") then

Makroen i artiklen kunne f.eks. se sådan ud:

Sub TitelFraFelt
rem Kontroller om det er et tekstdokument
If NOT ThisComponent.supportsService("com.sun.star.text.TextDocument") Then
  MsgBox "Det aktuelle dokument er ikke et Writer tekstdokument", 48, "Fejl"
  Exit Sub
End If
rem Kontroller om der findes et felt med navnet Titel
If thisComponent.getTextFieldMasters().hasByName("com.sun.star.text.fieldmaster.User.Titel") then
    oMaster = thisComponent.getTextFieldMasters().getByName("com.sun.star.text.fieldmaster.User.Titel")
    thisComponent.DocumentProperties.Title = oMaster.Content
    Else
      MsgBox "Feltet Titel findes ikke", 48, "Fejl"
    End if
End Sub




tirsdag den 6. december 2016

Tekstdokumenters titel


Det har i lang tid irriteret mig at mine PDF-filer vises med filnavn og dokumenttitel i titellinjen. Det er irriterende fordi jeg altid glemmer at rette dokumenttitlen, og mine PDF-filer har altid skabelonens navn (hvis jeg har brugt en skabelon) i titellinjen. 
Et tekstdokument kan have forskellige ”titler”. F.eks. kan du anvende en typografi med navnet ”Titel”. Det er bare ikke den titel der anvendes når vi laver PDF-filer. Det er den titel du kan registrere under Filer – Egenskaben på fanen Beskrivelse i feltet Titel.
Og hvem husker lige det?
I mit tilfælde er jeg så uheldig at mine skabeloner derimod har titler, og hvert eneste dokument ”arver” skabelonens titel, hvilket så i sidste ende vises i PDF-læserens titellinje. Det var faktisk bedre hvis der slet ikke stod noget som helst i det felt.
Jeg har forsøgt at automatisere opdatering af feltet, og jeg er kommet frem til to forslag. Det første går ud på at titlen inde i dokumentet (f.eks. med typografien ”Titel”) tildeles et bogmærke med navnet ”Titel”. Denne makro overfører Titel-teksten i dokumentet til egenskabstitlen:
Sub TitelFraBookmark
oBookmarkTitel = thisComponent.Bookmarks.getByName("Titel")
thisComponent.DocumentProperties.Title = oBookmarkTitel.Anchor.String
End Sub
Ulempen er at det ikke er tydeligt for brugeren hvad titlen er, for det er meget let at ødelægge bogmærket, så det kun er en del af teksten der er bogmærke.
I stedet bruger jeg en brugerdefineret variabel (IndsætFeltFlere felter… på fanen Variable opretter du et Brugerdefineret felt). I dokumentet markerer du feltet og vælger typografi ”Titel”.
Denne makro kopierer det brugerdefinerede felt til dokumentegenskaberne:
Sub TitelFraFelt
oMaster = thisComponent.getTextFieldMasters().getByName("com.sun.star.text.fieldmaster.User.Titel")
thisComponent.DocumentProperties.Title = oMaster.Content
End Sub


Makroen sørger for at feltet i dokumentet overføres til dokumentegenskaberne:


Så er det bare at knytte makroen (vælg selv hvilken af de to du synes er bedst) til en eller flere handlinger, f.eks. når dokumentet gemmes eller når dokumentet eksporteres.
Tilknytning af en makro til en handling kan du gøre under FunktionerTilpas på fanen Hændelser. Du kan sagtens tilknytte samme makro til flere forskellige hændelse.