Skripte programmieren für LibreOffice – Teil 2
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.
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.
Abb. 1: Das erste Arbeitsblatt des Fallbeispiels enthält die eingereichten Bestellungen
Abb. 2: Im zweiten Arbeitsblatt erscheinen die verfügbaren Artikel sowie die entsprechenden Preise
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...