Makros in LibreOffice Base

Aus LinuxUser 06/2021

Makros in LibreOffice Base

Im Eigenbau

Mit etwas Know-how passen Sie Datenbankanwendungen in LibreOffice Base maßgeschneidert an Ihre Bedürfnisse an.

Oft versuchen Benutzer, in einer Datenbanktabelle einen bereits vorhandenen Eintrag erneut abzuspeichern. Erlaubt die Datenbank für das jeweilige Attribut keine doppelten Einträge, wirft sie eine für den Anwender relativ unverständliche Fehlermeldung aus. Hier wären benutzerdefinierte Fehlermeldungen wesentlich benutzerfreundlicher.

Das – und noch viele andere Dinge – lässt sich in LibreOffice Base mit Makros realisieren. Um die nützlichen Codeschnipsel verwenden zu können, brauchen Sie lediglich die LibreOffice-Suite [1] zu installieren (Listing 1). Danach erstellen Sie die Datenbank und verknüpfen die Makros mit den Formularen.

Listing 1

LibreOffice installieren

### beliebiger Desktop
$ sudo apt install libreoffice
### KDE
$ sudo apt install libreoffice-plasma libreoffice
### Gnome
$ sudo apt install libreoffice-gnome libreoffice

Features

Makros können unter anderem Dialogboxen realisieren, Benutzereingaben verarbeiten, Datenbanken manipulieren, Operationen im Dateiverzeichnis vornehmen sowie LibreOffice-Dokumente erstellen und bearbeiten. Sie lassen sich in Bibliotheken und Dokumenten abspeichern, wobei sich Ersteres empfiehlt: In einem Dokument abgespeicherte Makros kann man ausschließlich innerhalb dieses Dokuments einsetzen.

LibreOffice verwaltet Makros hierarchisch. So beinhaltet ein Dokumenten-Container keine oder beliebig viele Bibliotheken. Eine Bibliothek beherbergt hingegen Module und Dialoge. Alle Funktionen und Subroutinen lagern in einem Modul. Ein Makro kann mehrere Subroutinen umfassen, die gegebenenfalls Funktionen anderer Module und Bibliotheken verwenden. Da wir in diesem Artikel nicht auf alle Features von Makros eingehen können, empfehlen wir Ihnen die Lektüre des kostenlosen Tutorials “Macros explained” von Andrew Pitonyak [2].

Schaltbrett

Schaltbretter (englisch: Switchboards) ermöglichen den direkten Zugriff auf ein Formular durch einen Klick auf den entsprechenden Button. Das erspart dem Anwender unter Umständen mehrere Klicks, und er findet sich auf dem Switchboard leichter zurecht (Abbildung 1).

Abbildung 1: Ein Switchboard erspart dem Anwender unter Umständen mehrere Klicks.

Abbildung 1: Ein Switchboard erspart dem Anwender unter Umständen mehrere Klicks.

Beim Switchboard gehen Sie zunächst wie bei einem Formular vor, indem Sie links auf Formulare klicken und dann in der Entwurfsansicht ein Formular erstellen. Anschließend fügen Sie für jedes Formular, das über das Switchboard aufgerufen wird, einen Button hinzu. Das Makro selbst erstellen Sie unter Extras | Makros | Makros verwalten | Basic.

Im Fenster Basic Makros sehen Sie alle Makros, auf die Sie innerhalb des LibreOffice-Dokuments zugreifen können. Benötigen Sie ein neues Modul für das Makro, klicken Sie auf den Button Verwalten und anschließend auf Neu. Im daraufhin erscheinenden Fenster legen Sie den Speicherort für das Modul fest und geben ihm einen aussagekräftigen Namen (Abbildung 2).

Abbildung 2: Neue Module erstellen Sie mithilfe des Makro-Organizers.

Abbildung 2: Neue Module erstellen Sie mithilfe des Makro-Organizers.

Für das Erstellen von Makros integriert LibreOffice eine eigene Entwicklungsumgebung (Integrated Development Environment, IDE), die Sie in der Entwurfsansicht eines Formulars unter Extras | Makros | Makros bearbeiten aufrufen.

Eine Funktion namens OpenForm dient dazu, redundanten Code beim Switchboard zu vermeiden. Sie erhält als Argument einen String, der den Namen des Formulars übergibt. Der Button Edit Jobs aus dem Switchboard zeigt auf die Subroutine OpenJobsForm, die die Funktion OpenForm aufruft (Abbildung 3).

Abbildung 3: Die integrierte IDE von LibreOffice Base verfügt über ein Debug-Tool, das beim Aufspüren von Fehlern hilft.

Abbildung 3: Die integrierte IDE von LibreOffice Base verfügt über ein Debug-Tool, das beim Aufspüren von Fehlern hilft.

Um den Button mit dem Makro zu verknüpfen, öffnen Sie durch einen Rechtsklick auf den Button das Kontextmenü. Dort klicken Sie auf Steuerelement-Eigenschaften und wechseln in den Reiter Ereignisse. Beim Eintrag Aktion ausführen wählen Sie das gewünschte Makro beziehungsweise die passende Subroutine aus (Abbildung 4).

Abbildung 4: Das Ereignis <span class="ui-element">Aktion ausf&uuml;hren</span> entspricht dem Klick auf dem Button.

Abbildung 4: Das Ereignis Aktion ausführen entspricht dem Klick auf dem Button.

Formulare erstellen

Das Makro sollte Benutzereingaben validieren, bevor es sie in der Datenbank ablegt. Auf diese Weise lassen sich fehlerhafte Datensätze in der Datenbank verhindern. Als Beispiel dient im Folgenden ein Formular, das nach einem Klick auf den Button Save die Daten von Mitarbeitern in der Datenbank speichert. Dafür gibt es die beiden Tabellen Jobs mit den Jobtiteln (Abbildung 5) und Staff mit den Daten der einzelnen Mitarbeiter (Abbildung 6).

Abbildung 5: In der Tabelle <span class="ui-element">Jobs</span> stehen die verf&uuml;gbaren Jobtitel.

Abbildung 5: In der Tabelle Jobs stehen die verfügbaren Jobtitel.


Abbildung 6: Die Tabelle <span class="ui-element">Staff</span> beherbergt die Mitarbeiterdaten.

Abbildung 6: Die Tabelle Staff beherbergt die Mitarbeiterdaten.

Über den Formularassistenten erstellen Sie nun ein Formular, das die entsprechenden Daten in der Datenbank hinterlegt. Dazu klicken Sie links auf das Formular-Icon und dann auf den Link Formulare unter Verwendung des Assistenten erstellen. Im Assistenten wählen Sie die entsprechende Tabelle aus und geben an, aus welchen Attributen sich das Formular zusammensetzt.

Da in unserem Beispiel die Tabelle Staff auf die Daten der Tabelle Jobs zugreift, empfiehlt es sich, ein Ausklappmenü zu verwenden: Die Mitarbeitertabelle Staff hält zu jedem Mitarbeiter seine Funktionsbezeichnung im Unternehmen fest. Um redundante Einträge in der Datenbank zu vermeiden, genügt es, lediglich die Job-ID des zugehörigen Job-Titels in der Mitarbeitertabelle abzuspeichern (Abbildung 7).

Abbildung 7: Dieses Formular speichert die Daten in der Mitarbeitertabelle <span class="ui-element">Staff</span> ab.

Abbildung 7: Dieses Formular speichert die Daten in der Mitarbeitertabelle Staff ab.

Im Entwurfsmodus befinden sich links die Steuerelemente, die sich per Drag & Drop an der entsprechenden Stelle im Formular ablegen lassen. Das Ausklappmenü realisieren Sie beispielsweise über ein Listenfeld. Um dem Benutzer mitzuteilen, was sich mit dem Listenfeld auswählen lässt, ziehen Sie per Drag & Drop ein Label in das Formular. Sie beschriften es im Kontextmenü des Labels durch einen Klick auf Steuerelement-Eigenschaften und geben im Feld Titel den Namen dafür ein.

Das Listenfeld lässt sich anhand eines SQL-Befehls, einer Tabelle oder Abfrage befüllen. Dazu klicken Sie links auf den Steuerelementeassistenten und ziehen das Listenfeld ins Formular. Im Assistenten wählen Sie zunächst aus, aus welcher Tabelle die Daten stammen, in unserem Fall aus der Tabelle Jobs.

Als Nächstes geben Sie an, welches Attribut der Tabelle im Listenfeld erscheint. Üblicherweise handelt es sich dabei um lesbare Attribute wie Titel oder Namen. Außerdem müssen Sie die Tabellen noch miteinander verknüpfen, damit das Makro beim Betrachten der Datensätze die richtigen Daten lädt. In unserem Beispiel verbinden Sie dabei das Attribut job_id der Tabelle Staff mit dem Attribut ID der Tabelle Jobs.

Formulare überprüfen

Die Validierung der eingegebenen Daten übernimmt in unserem Beispiel das Makro. Dazu erstellen Sie zunächst wie bereits beschrieben ein Modul, das die Subroutine samt Funktionen aufnimmt.

Listing 2 deklariert zunächst in der Subroutine InsertStaff die Variablen lokal, indem es das Schlüsselwort DIM vor den Variablennamen setzt (Zeile 2 bis Zeile 24). Variablen, die auf das Formular oder die Steuerelemente zugreifen, sind üblicherweise vom Typ OBJECT. Die Werte der einzelnen Steuerelemente können beispielsweise Ganzzahlen (INTEGER), Fließkommazahlen (DOUBLE), boolesche Variablen (BOOLEAN) oder Zeichenketten (STRING) sein. LibreOffice unterstützt noch weitere Datentypen, die das Tutorial “Macros explained” auf Seite 37 auflistet.

Listing 2

Prüfmodul

Sub InsertStaff (oEvent AS OBJECT)
  # Variablen deklarieren
  DIM oDoc AS OBJECT
  DIM oDrawpage AS OBJECT
  DIM oForm AS OBJECT
  DIM oFName AS OBJECT
  DIM oLName AS OBJECT
  DIM oNotes AS OBJECT
  DIM oUsername AS OBJECT
  DIM oJob AS OBJECT
  DIM fName AS STRING
  DIM lName AS STRING
  DIM sNotes AS STRING
  DIM sUsername AS STRING
  DIM sJob AS STRING
  DIM oSQL_Statement AS OBJECT
  DIM oSQL_Statement2 AS OBJECT
  DIM stSql AS STRING
  DIM stSql2 AS STRING
  DIM oResult AS OBJECT
  DIM oResultStaff AS OBJECT
  DIM iResult AS INTEGER
  DIM iJobId AS INTEGER
  DIM oConnection AS OBJECT
  # Formular und Subformular in Variablen speichern
  oDoc = thisComponent
  oDrawpage = oDoc.Drawpage
  oForm = oDrawpage.forms.getByName("MainForm")
  oSubForm = oForm.getByName("SubForm")
  # Datenbankverbindung herstellen
  oConnection = oForm.activeConnection()
  # Werte aus Steuerelementen in Variablen speichern
  oFName = oForm.getByName("txtfirst_name")
  fName = oFName.Text
  oLName = oForm.getByName("txtlast_name")
  lName = oLName.Text
  oNotes = oForm.getByName("txtnotes")
  sNotes = oNotes.Text
  oUsername = oForm.getByName("txtusername")
  sUsername = oUsername.Text
  oJob = oSubForm.getByName("jobtitles")
  sJob = oJob.getCurrentValue()
  # Daten aus der Tabelle Staff abrufen
  oResultStaff = GetStaffRecord(sUsername)
  # Wenn Daten aus der Tabelle Staff abgerufen werden konnten
  If oResultStaff.next Then
    MsgBox ("Benutzername bereits vergeben!", 0, "Error")
  # Mitarbeiterdaten in Tabelle Staff abspeichern
  Else
    stSql2 = "SELECT *  FROM ""Jobs"" " & "WHERE LOWER(""job"") LIKE ? "
    oSQL_Statement2 = oConnection.prepareStatement(stSql2)
    oSQL_Statement2.setString(1,LCase(sJob))
    oResult = oSQL_Statement2.executeQuery(stSql2)
    oResult.next
    iJobId = oResult.getInt(1)
    stSql = "INSERT INTO ""Staff"" " & "(""first_name"", ""last_name"", ""notes"", ""username"", ""job_id"") VALUES(?, ?, ?, ?, ?)"
    oSQL_Statement = oConnection.prepareStatement(stSql)
    oSQL_Statement.setString(1,fName)
    oSQL_Statement.setString(2,lName)
    oSQL_Statement.setString(3,sNotes)
    oSQL_Statement.setString(4,sUsername)
    oSQL_Statement.setInt(5,iJobId)
    iResult = oSQL_Statement.executeUpdate
    MsgBox ("Daten wurden gespeichert", 0, "Success")
  End If
End Sub
Function GetStaffRecord(name AS STRING) As Object
  DIM oForm AS OBJECT
  DIM oResult AS OBJECT
  DIM stSql AS STRING
  DIM oSQL_Statement AS OBJECT
  DIM oConnection AS OBJECT
  # SQL-Statement als String speichern
  stSql = "SELECT *  FROM ""Staff"" " & "WHERE ""username"" = " & name
  oForm = ThisComponent.Drawpage.forms.getByName("MainForm")
  # Verbindung zur Datenbank herstellen
  oConnection = oForm.activeConnection()
  # String in SQL-Statement umwandeln
  oSQL_Statement = oConnection.createStatement()
  # Abfrage in Datenbank vornehmen
  oResult = oSQL_Statement.executeQuery(stSql)
  # gefundene Datensätze zurückgeben
  GetStaffRecord = oResult
End Function

Um das Formular als Objekt in einer Variablen festzuhalten, benötigen Sie dessen Namen, den Sie im Formularentwurf durch einen Klick auf den Formularnavigator ermitteln. Dort klicken Sie mit der rechten Maustaste auf den Eintrag MainForm und ein weiteres Mal mit der linken Maustaste auf Eigenschaften. Im Feld Name erscheint dann der Name des Formulars. Auf das Formular greifen Sie im Makro zu wie in den Zeilen 26 bis 28 von Listing 2.

Die Namen der Steuerelemente ermitteln Sie in deren Kontextmenüs durch einen Klick auf Steuerelemente-Eigenschaften und lesen den Wert im Feld Name ab. In Zeile 29 sehen Sie, wie Sie ein Steuerelement als Objekt speichern. Anschließend lesen Sie die eingegebenen Werte aus und speichern sie in Variablen, die dem Datentyp des Werts entsprechen (ab Zeile 32). Bei Eingabefeldern vom Typ Text entnehmen Sie den Wert dem Eingabefeld wie in Zeile 40 gezeigt. Ähnlich funktioniert das für den ausgewählten Eintrag eines Listenfelds (Zeile 42).

Da in unserem Fall der Benutzername des Mitarbeiters nur einmal in der Spalte username vorkommen darf, nehmen Sie eine SQL-Abfrage in der Tabelle Staff vor, um zu ermitteln, ob der Benutzername bereits vergeben ist. Diese Prüfung übernimmt in Listing 2 die Funktion GetStaffRecord ab Zeile 68. Dieser Funktion übergeben Sie als Argument den eingegebenen Benutzernamen. Als Ergebnis liefert die Funktion einen gefundenen Datensatz zurück (Zeile 84). Im Kopf der Funktion geben Sie dazu den Datentyp an, den die Funktion zurückgibt (Zeile 70).

Um zu überprüfen, ob die SQL-Abfrage (Zeile 44) mindestens einen Datensatz finden konnte, benutzen Sie eine If-Anweisung (ab Zeile 46). Kommt der Benutzername bereits in der Tabelle vor, geben Sie über die Anweisung MsgBox eine entsprechende Meldung aus (Zeile 47). Andernfalls speichert das Makro aus Listing 2 die Mitarbeiterdaten in die Tabelle Staff. Dazu ermittelt es zunächst die ID des Jobtitels (Zeile 50), fügt sie zusammen mit den anderen Daten in die Tabelle Staff ein (Zeilen 56 bis 63), und gibt zu guter Letzt die Benachrichtigung aus, dass die Daten gespeichert wurden (Zeile 64).

Eingaben löschen

Hin und wieder kommt es vor, dass Anwender sich vertippen oder im Listenfeld falsche Einträge auswählen. Das notwendige Zurücksetzen der Werte kann dann ein Makro übernehmen. Der Code aus Listing 3 speichert dazu zunächst das Formular MainForm sowie die Eingabefelder in Variablen ab. Beim Zurücksetzen übergibt er dem Eingabefeld einen leeren String und teilt anschließend dem Formular den neuen Wert durch einen Aufruf von commit mit.

Listing 3

Eingaben löschen

Sub ResetForm (oEvent AS OBJECT)
  DIM oForm AS OBJECT
  DIM oSubForm AS OBJECT
  DIM oDocument As Object
  DIM oDispatcher As Object
  DIM oFName AS OBJECT
  DIM oLName AS OBJECT
  DIM oNotes AS OBJECT
  DIM oUsername AS OBJECT
  DIM oJob AS OBJECT
  DIM oStaffId AS OBJECT
  oForm = ThisComponent.Drawpage.forms.getByName("MainForm")
  oSubForm = oForm.getByName("SubForm")
  # Eingabefelder zurücksetzen
  oFName = oForm.getByName("txtfirst_name")
  oFName.Text = ""
  oFName.commit()
  oLName = oForm.getByName("txtlast_name")
  oLName.Text = ""
  oLName.commit()
  oNotes = oForm.getByName("txtnotes")
  oNotes.Text = ""
  oNotes.commit()
  oUsername = oForm.getByName("txtusername")
  oUsername.Text = ""
  oUsername.commit()
  oStaffId = oForm.getByName("txt_idStaff")
  oStaffId.Text = ""
  oStaffId.commit()
End Sub

Daten durchlaufen

Statt der Navigationsleiste kann ebenso gut ein Makro die Datensätze anzeigen. Für die Iteration benötigen Sie zwei global deklarierte Variablen: ein Zähler sowie eine Variable, die den maximalen Wert des Zählers festhält (Listing 4).

Listing 4

Zählervariable

GLOBAL counter AS LONG
GLOBAL maxCounter AS LONG

Außerdem erstellen Sie einen Button namens Reset und verknüpfen ihn mit der Subroutine aus Listing 5. Sie setzt den globalen Zähler counter zurück und ruft die Funktion SetMaxCounter auf, die die Zahl der Datensätze ermittelt. Diese Zahl landet in der globalen Variablen maxCounter (Listing 6). Generell ist es wichtig, beim Datensatz die Methode next aufzurufen (Zeile 16): Die Datensätze liegen in einem Array, sodass Sie den ersten Datensatz durch den Aufruf von .next erhalten. Außerdem liefert die verwendete SQL-Abfrage lediglich die Zahl der Datensätze zurück. Die lässt sich daher durch den Befehl Datensatz.getInt(1) abrufen (Zeile 18).

Listing 5

Zähler zurücksetzen

Sub ResetCounter (oEvent AS OBJECT)
  counter = 0
  SetMaxCounter
End Sub

Listing 6

Zähler ermitteln

Function SetMaxCounter()
  DIM oForm AS OBJECT
  DIM oResult AS OBJECT
  DIM stSql AS STRING
  DIM oSQL_Statement AS OBJECT
  DIM oConnection AS OBJECT
  # Formular in Variable abspeichern
  oForm = ThisComponent.Drawpage.forms.getByName("MainForm")
  # Verbindung zur Datenbank aufbauen
  oConnection = oForm.activeConnection()
  # Zahl der Datensätze ermitteln
  stSql = "SELECT COUNT(""ID"") as count_staff FROM ""Staff"" "
  oSQL_Statement = oConnection.createStatement()
  # SQL-Abfrage vornehmen
  oResult = oSQL_Statement.executeQuery(stSql)
  oResult.next
  # Abfrageergebnis in globaler Variable speichern
  maxCounter = oResult.getInt(1)
End Function

Sie benötigen einen weiteren Button namens next, um durch die Datensätze zu iterieren. Dazu klicken Sie auf das Steuerelement Schaltfläche und ziehen es ins Formular. Den so erzeugten Button verknüpfen Sie in unserem Beispiel mit der Subroutine NextItem aus Listing 7. Genauso wie beim Zurücksetzen des Formulars definiert auch diese Subroutine zunächst die Variablen der einzelnen Eingabefelder, um sie manipulieren zu können.

Danach erfolgt der Aufruf der Funktion CheckStaffRecord, die Listing 8 zeigt. Sie stellt zunächst sicher, dass der aktuelle Stand des Zählers nicht den Höchstwert übersteigt, und ruft dann die Funktion GetStaffRecord auf. Das Entscheidende an dieser Methode ist die SQL-Anweisung in Zeile 24, die dafür sorgt, dass der gewünschte Datensatz der Zeilennummer (counter) entspricht. Danach speichert die Funktion die Daten aus dem gefundenen Datensatz in den einzelnen Eingabefeldern ab.

Listing 7

Über Datensätze iterieren

Sub NextItem (oEvent AS OBJECT)
  DIM oForm AS OBJECT
  DIM oStaffID AS OBJECT
  DIM oFName AS OBJECT
  DIM oLName AS OBJECT
  DIM oNotes AS OBJECT
  DIM oUsername AS OBJECT
  DIM oStaff AS OBJECT
  DIM indexStaff AS INTEGER
  oForm = ThisComponent.Drawpage.forms.getByName("MainForm")
  # Datensatz aus der Tabelle Staff abrufen
  oStaff = CheckStaffRecord()
  If oStaff.next Then
    # Eingabefelder mit Daten füllen
    oStaffID = oForm.getByName("txt_idStaff")
    oStaffID.Text = oStaff.getInt(1)
    oStaffID.commit()
    oFName = oForm.getByName("txtfirst_name")
    oFName.Text = oStaff.getString(2)
    oFName.commit()
    oLName = oForm.getByName("txtlast_name")
    oLName.Text = oStaff.getString(3)
    oLName.commit()
    oNotes = oForm.getByName("txtnotes")
    oNotes.Text = oStaff.getString(5)
    oNotes.commit()
    oUsername = oForm.getByName("txtusername")
    oUsername.Text = oStaff.getString(6)
    oUsername.commit()
  Else
    MsgBox ("Keine weiteren Daten verfügbar.", 0, "Error")
  End If
End Sub

Listing 8

Datensatz auslesen

# Setzt den Zähler und liefert einen
# Datensatz aus der Tabelle Staff zurück
Function CheckStaffRecord() As Object
  DIM oStaff AS OBJECT
  If counter < maxCounter Then
    oStaff = GetStaffRecord(counter)
    counter = counter + 1
  Else
    counter = 0
    oStaff = GetStaffRecord(counter)
    counter = counter + 1
  End If
  CheckStaffRecord = oStaff
End Function
# Ruft einen Datensatz aus der Tabelle
# Staff an einer bestimmten Stelle ab
Function GetStaffRecord(id AS INTEGER) As Object
  DIM oForm AS OBJECT
  DIM oResult AS OBJECT
  DIM stSql AS STRING
  DIM oSQL_Statement AS OBJECT
  DIM oConnection AS OBJECT
  stSql = "SELECT *  FROM ""Staff"" " & "LIMIT 1 OFFSET " & id
  oForm = ThisComponent.Drawpage.forms.getByName("MainForm")
  oConnection = oForm.activeConnection()
  oSQL_Statement = oConnection.createStatement()
  oResult = oSQL_Statement.executeQuery(stSql)
  GetStaffRecord = oResult
End Function

Dateien kopieren

In Formularen verlinkte Bilder befinden sich meist an unterschiedlichen Orten. Um alle Bilder im selben Pfad abzulegen, bedarf es Funktionen, um auf Dateien und Ordner zuzugreifen (Abbildung 8).

Abbildung 8: Dieses Formular legt den Pfad zum Bild in die Artikeltabelle <span class="ui-element">Items</span> ab.

Abbildung 8: Dieses Formular legt den Pfad zum Bild in die Artikeltabelle Items ab.

Zunächst erstellen Sie einen Button namens save, der zusätzlich zu den Datenbankoperationen die Bilder in ein Arbeitsverzeichnis kopiert. Dazu definieren Sie die Subroutine InsertItems(oEvent AS OBJECT) (Listing 9) und verknüpfen sie mit dem Button.

Listing 9

Datensatz erstellen

# Erstellt einen neuen Datensatz in der
# Tabelle Staff und aktualisiert diese
Sub InsertItems (oEvent AS OBJECT)
  DIM iResult AS INTEGER
  DIM newPicturePath AS STRING
  DIM oPic AS OBJECT
  DIM sPic AS STRING
  [...]
  oPic = oForm.getByName("Image Control 1")
  sPic = oPic.ImageUrl
  [...]
  iResult = InsertItem (sArticleName, bInStock, dPrice, sPic, dSPU, sSKU, sEAN, 0, 0)
  newPicturePath = SavePicture (sPic,iResult)
  UpdateItem (iResult, newPicturePath)
End Sub

Beim Speichern der Daten aus den Eingabefeldern gehen Sie so wie in Listing 10 vor. Die Funktion InsertItem gibt nach dem Speichern des Datensatzes eine ID vom Typ INTEGER zurück, die der ID des soeben abgespeicherten Datensatzes entspricht. Diese ID übergibt sie zusammen mit der URL, die sie dem Steuerelement für Bilder entnimmt, der Funktion SavePicture.

Listing 10

Speichern des Artikels

# Speichert einen Artikel in der Tabelle Items ab
Function InsertItem (n AS STRING, stock AS BOOLEAN, p AS DOUBLE, pp AS STRING, spu AS DOUBLE, sku AS STRING, ean AS STRING, s AS INTEGER, a AS INTEGER) AS INTEGER
  DIM oConnection AS OBJECT
  DIM oForm AS OBJECT
  DIM iResult AS INTEGER
  DIM stSql AS STRING
  DIM oSQL_Statement AS OBJECT
  stSql = "INSERT INTO ""Items"" " & "(""name"", ""in_stock"", ""price"", ""picture_path"", ""smallest_purchasing_unit"", ""sku"", ""ean"", ""status"", ""assigned"") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)"
  oForm = ThisComponent.Drawpage.forms.getByName("MainForm")
  oConnection = oForm.activeConnection()
  oSQL_Statement = oConnection.prepareStatement(stSql)
  oSQL_Statement.setString(1,n)
  oSQL_Statement.setBoolean(2,stock)
  oSQL_Statement.setDouble(3,p)
  oSQL_Statement.setString(4,pp)
  oSQL_Statement.setDouble(5,spu)
  oSQL_Statement.setString(6,sku)
  oSQL_Statement.setString(7,ean)
  oSQL_Statement.setInt(8,s)
  oSQL_Statement.setInt(9,a)
  iResult = oSQL_Statement.executeUpdate
  InsertItem = iResult
  End Function
# Aktualisiert beim Artikel den Pfad des Bilds
Function UpdateItem (i AS INTEGER, pp AS STRING) AS INTEGER
  DIM oConnection AS OBJECT
  DIM oForm AS OBJECT
  DIM iResult AS INTEGER
  DIM stSql AS STRING
  DIM oSQL_Statement AS OBJECT
  stSql = "UPDATE ""Items"" " & " SET ""picture_path"" = ? " & "WHERE ID = ?"
  oForm = ThisComponent.Drawpage.forms.getByName("MainForm")
  oConnection = oForm.activeConnection()
  oSQL_Statement = oConnection.prepareStatement(stSql)
  oSQL_Statement.setString(1,pp)
  oSQL_Statement.setInt(2,i)
  iResult = oSQL_Statement.executeUpdate
  UpdateItem = iResult
End Function

SavePicture (Listing 11) ruft wiederum die Funktion SetPath auf. Sie legt das global definierte (GLOBAL workingPath AS STRING) Arbeitsverzeichnis fest, sodass mehrere Funktionen darauf zugreifen können.

Listing 11

Bild abspeichern

# Speichert das Bild eines Artikels
# ab und gibt den neuen Pfad zurück
Sub SavePicture (pic AS STRING, itemId AS INTEGER) AS STRING
  DIM picturepath AS STRING
  DIM fileName AS STRING
  DIM extension AS STRING
  DIM newFileName AS STRING
  SetPath
  MsgBox ("old-filename: " & pic & CHR(13), 0, "Pla")
  picturepath = workingPath & itemId
  fileName = FileNameoutofPath(GetFileNameWithoutExtension(pic))
  extension = GetFileNameExtension(pic)
  filename = fileName & "." & extension
  If NOT FileExists(picturepath) Then
    MkDir picturepath
  End If
  picturepath = picturepath & "/" & filename
  If NOT FileExists(picturepath) Then
    FileCopy(pic, picturepath)
  End If
  newFileName = FileNameoutofPath(GetFileNameWithoutExtension(picturepath)) & "." & extension
  SavePicture = newFileName
End Sub

LibreOffice stellt bereits vordefinierte Pfade zur Verfügung, die sowohl unter Linux als auch unter Windows gelten. So steht in Listing 12 die Variable $(home) (Zeile 6) sowohl für das Home-Verzeichnis unter Linux als auch für den Ordner Dokumente und Einstellungen unter Windows. Das Tutorial “Macros explained” nennt auf Seite 193 noch weitere Pfadvariablen.

Listing 12

Speicherort festlegen

# Speicherort für Bilder
# als String festlegen
Function SetPath()
  DIM oPathSub
  DIM name
  name = "$(home)"
  oPathSub = CreateUnoService("com.sun.star.util.PathSubstitution")
  workingPath = oPathSub.getSubstituteVariableValue(name)
  workingPath = workingPath & "/" & "warehouse" & "/" & "item-"
End Function

In unserem Beispiel landet das Bild im Unterverzeichnis warehouse/item-ID/ (Listing 11, Zeile 10). Mit anderen Worten: Jeder Artikel erhält seinen eigenen Ordner, um die Bilder des Artikels dort abzuspeichern.

Nach dem Definieren des Verzeichnisses für die einzelnen Artikel kommt zum Pfad noch der Dateiname samt Erweiterung des Bilds hinzu. Der Dateiname lässt sich mittels GetFileNameWithoutExtension(Datei) extrahieren (Listing 11, Zeile 11), GetFileNameExtension(Datei) ermittelt die Dateierweiterung (Zeile 12). Das Kopieren des Bilds von A nach B übernimmt die Funktion FileCopy (Zeile 19).

Fazit

Wie unsere Beispiele zeigen, erreichen Makros schnell eine beachtliche Länge und werden dann unübersichtlich. Von daher sollten Sie so wenige wie möglich einsetzen, damit die Datenbanksoftware fehlerfrei funktioniert. Zum Erlernen der Makrosprache empfiehlt sich neben dem bereits mehrfach erwähnten Tutorial “Macros explained” das Base-Handbuch [3] des LibreOffice-Teams. (jlu)

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDF
LinuxUser 06/2021 KAUFEN
EINZELNE AUSGABE
ABONNEMENTS
TABLET & SMARTPHONE APPS
E-Mail Benachrichtigung
Benachrichtige mich zu:

Hinweis: Dieser Artikel ist älter als ein Jahr, enthaltene Informationen sind möglicherweise veraltet.

0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben