Skripte programmieren für LibreOffice – Teil 2

LibreOffice Calc für Fortgeschrittene

LibreOffice Calc für Fortgeschrittene

Skripte programmieren für LibreOffice – Teil 2

LibreOffice Calc für Fortgeschrittene


Der Hauptnutzen von LibreOffice Calc besteht darin, mehrere Arbeitsblätter zu beherbergen. Die darin enthaltenen Daten wiederum sind in Tabellen bestehend aus Zellen organisiert, auf die durch Angabe von Zeilen- und Spaltennummern zugegriffen werden kann. Mit Hilfe von Makros lässt sich die Organisation von Daten innerhalb eines Tabellenkalkulationsblatts automatisieren. Verglichen mit LibreOffice Writer fällt die Formatierung der Daten zwar schlichter aus, jedoch lassen sich unter LibreOffice Calc komplexe Berechnungen vornehmen [1].

Hauptsächlich können Makros unter LibreOffice Calc eingesetzt werden, um den Inhalt zu ändern, zu durchlaufen sowie zu formatieren. So lassen sich mit Makros die folgenden Punkte realisieren:

  • Auf Arbeitsblätter zugreifen, neue Arbeitsblätter einfügen etc.

  • Zellen kopieren, verschieben und einfügen

  • Festgelegten Zellbereich iterieren

  • Zellen färben, Schriftgröße festlegen, Ränder setzen

  • Spaltenbreite und Zeilenhöhe einstellen

  • Daten zwischen den Dokumenten kopieren

  • Eigene Funktionen schreiben, die in der Formelzeile eingesetzt werden können

Während Teil 1 dieser Artikelserie das Iterieren von Zellen behandelte, beschäftigt sich Teil 2 mit fortgeschrittenen Themen unter LibreOffice Calc wie das Formatieren von Zellen und Spalten, dem Einsatz von Klassen des LibreOffice API sowie dem Export von PDF-Dateien. Der in Teil 2 vorgestellte Code ist zu Python 3.11.7 kompatibel und läuft unter LibreOffice mit der Version 7.4.2.3.

Fallbeispiel: Obst- und Gemüsebestellungen

Als Beispiel dient die Bestellung von Obst und Gemüse in Indien. So besteht das Dokument aus zwei Arbeitsblättern, wobei das dritte Arbeitsblatt Total Order mittels eines anderen Makros generiert worden ist:

  • Sheet1: Eingereichte Bestellungen der Kunden (Abb. 1)

  • Sheet2: Artikelliste („Description of the Items“) zusammen mit den Preisen („RT“) (Abb. 2)

  • Total Order: Auflistung der bestellten Mengen je Kunde (Abb. 3)

Sinn und Zweck des hier vorgestellten Makros ist es, für jeden Kunden einen Beleg in Form eines extra Arbeitsblatts zu erstellen, das als Vorlage für ein Belegexemplar im PDF-Format dient.

minosi_libreoffice_1

Abb. 1: Das erste Arbeitsblatt des Fallbeispiels enthält die eingereichten Bestellungen

minosi_libreoffice_2

Abb. 2: Im zweiten Arbeitsblatt erscheinen die verfügbaren Artikel sowie die entsprechenden Preise

minosi_libreoffice_3

Abb. 3: Total Order liefert eine Übersicht über die von den Kunden bestellten Mengen

Um das Makro schließlich in Python zu schreiben, ist es einfacher, bestehenden Code aus Basic in einen beliebigen Python-Editor zu kopieren und schrittweise in Python-Code umzuwandeln. So wird für das Makro eine Klasse namens Receipt in Python erstellt (Listing 1). Die Deklaration der Klasse beginnt mit den Konstanten, die direkt unterhalb des Schlüsselworts Class erscheinen. Hierzu zählen der Pfad order_sheet zum Calc-Dokument, Spaltenüberschriften, Indizes von Spalten und Reihen sowie die RGB-Werte diverser Farben, die als Tupeln festgehalten sind.

Listing 1

from unotools import Socket, connect
from unotools.component.calc import Calc
import uno
import sys
import random
from time import sleep, time
import os
import signal
from pathlib import Path

# Klasse zum Erstellen der Belege
class Receipt:
  ### Konstanten:
  order_sheet = "Receipt-Generator.ods"
  prefixFilename = "receipt"
  separator = "_"
  # Überschriften
  descrHeader = "Description"
  rateHeader = "Rate"
  subtotalHeader = "Subtotal"
  totalHeader = "Total"
  sTO = "Total Order"
  # Maximalwert für index "i"; Maximale Zahl der Kunden
  i_max = 1024
  ## Arbeitsblatt "Total Order"
  # Spalte "Category"
  colCategory = 1
  # Spalte "Delivery Date"
  colDeliveryDate = 2
  # Spalte "INO"
  s1Index = 3
  # Spalte "RT"
  s2Index = 4
  # Spalte "SP"
  s3Index = 5
  offset = 2
  ## Arbeitsblatt "Receipts..."
  # Spalte "Description of Items"
  colDescr = 1
  # Spalte "Rate"
  colRate = 2
  # Spalte "Kunde"
  colCustomer = 3
  # Spalte "Subtotal"
  colSubtotal = 4
  # Indizes der Reihen
  rowDate = 0
  rowHeader = 1
  # Farben
  orange_brown = (197,90,17)
  dark_green = (56,87,36)
  white=(255,255,255)
  black=(0,0,0)
  mint = (204,255,204)
  light_blue = (0,176,240)
  yellow = (198,208,12)

  def __init__(self):
    # Anzahl Kunden
    self.num_clients = 0
    # Liste mit den Arbeitsblättern der Kunden
    self.clients = []
    # Spaltennamen für Formeln
    self.columns = []
    # enthält das Lieferdatum der Bestellungen
    self.dates = []
    # Zähler
    self.i = self.s3Index
    self.j = self.offset
    self.k = self.offset
    self.m = -1
    # Anzahl Arbeitsblätter
    self.num_sheets = 0
    self.doc = None
    # "Sheet 1"
    self.sheet0 = None
    # "Sheet 2"
    self.sheet1 = None
    # "Total Order"
    self.sheet2 = None
    self.oLine = None
    # Überschriften der Belege
    self.items = [
    (self.descrHeader,self.colDescr,self.rowHeader,self.orange_brown,self.white),
    (self.rateHeader,self.colRate,self.rowHeader,self.dark_green,self.white),
    (self.nDeliveryDate,self.colDeliveryDate,self.rowDate,self.dark_green,self.white),
    (self.subtotalHeader,self.colSubtotal,self.rowHeader,self.light_blue,self.white)
    ]

  def setup_doc(self):
    # Calc-Dokument laden
     self.doc = open_calcdocument(self.order_sheet)
    # vorhandene Arbeitsblätter laden
    self.sheet0 = getSheetByIndex(0,self.doc)
    self.num_sheets += 1
    self.sheet1 = getSheetByIndex(1,self.doc)
    self.num_sheets += 1
    self.sheet2 = getSheet(self.sTO,self.doc)
    self.num_sheets += 1
    # Objekt zum Zeichnen von Rändern initialisieren
    self.oLine = line_style()

  # Belege in Form von Arbeitsblättern erstellen
  def create_receipts(self):
    current_cell = self.sheet2.getCellByPosition(self.i, 1)
    # durch Kundenliste in "Total Order" iterieren
    while current_cell.getType() != get_empty() and self.i < self.i_max:
      # current_cell: Kundenname
      current_cell = self.sheet2.getCellByPosition(self.i, 1)
      # Datum der Bestellung abrufen
      cell_date = self.sheet2.getCellByPosition(self.i,self.rowDate)
      next_cell = self.sheet2.getCellByPosition(self.i + 1, 1)
      if next_cell.getType() != get_empty():
        # Arbeitsblatt einfügen
        self.insert_sheet(current_cell)
        # Überschriften einfügen
        for x in self.items:
          self.insert_cell(x[1],x[2],x[0],x[-2],x[-1])
          # Lieferdatum einfügen
          self.insert_cell(self.colDeliveryDate+1,self.rowDate,cell_date.getString(),self.dark_green, self.white)
          # Kunde einfügen
          self.insert_cell(self.colCustomer,self.rowHeader,current_cell.getString(),self.dark_green,self.white)
          self.add_company(current_cell,cell_date)
          cell_j = self.sheet2.getCellByPosition(self.colCategory, self.j)
          # Artikelliste im Arbeitsblatt "Total Order" durchlaufen
          while cell_j.getType() != get_empty() and self.j < self.i_max:
            # cell_j: Artikel
            cell_j = self.sheet2.getCellByPosition(self.colCategory, self.j)
            # cell_s2: Preis
            cell_s2 = self.sheet2.getCellByPosition(self.s2Index, self.j)
            # cell: Bestellmenge
            cell = self.sheet2.getCellByPosition(self.i, self.j)
            if cell_j.getType() != get_empty() and cell.getType() != get_empty():
              # Artikel in Beleg einfügen
              self.insert_cell(self.colDescr,self.k,cell_j.getString(),self.mint,self.black)
              dVal = get_double(cell_s2)
              # Artikelpreis in Beleg einfügen
              self.insert_value(self.colRate,self.k,dVal)
              dVal = get_double(cell)
              # Bestellmenge in Beleg einfügen
              self.insert_value(self.colCustomer, self.k, dVal)
              # Zwischensumme ermitteln
              self.calculate_subtotal()
              self.k += 1
              self.j += 1
              # Gesamtpreis ermitteln und einfügen
              self.insert_cell(self.s1Index,self.k,self.totalHeader,self.yellow,self.black)
              self.calculate_total()
              # Ränder setzen
              self.insert_borders()
              self.num_clients += 1
              # k: zeigt auf die Zeilen innerhalb eines Belegs
              self.k = self.offset
              # j: zeigt auf die aktuelle Zeile in "Total Order"
              self.j = self.offset
            # Zähler für äußere while-Schleife
            self.i += 1

    # Belege ins PDF-Format exportieren
    def export_receipts(self):
      # Dateieigenschaften definieren
      fileProps0 = uno.createUnoStruct('com.sun.star.beans.PropertyValue')
      fileProps0.Name = "FilterName...