Seite 1 von 1

Reportlab: Tabellenumbruch

Verfasst: Sonntag 10. Juli 2016, 13:21
von arren
Hallo Community,

nach Stundenlangem ausprobieren und googln bin ich an die Grenzen meiner Fähigkeiten gestoßen um folgendes scheinbar schlichtes Problem zu lösen:

Ich generiere mithilfe von Reportlab eine PDF die mir u.a. eine Tabelle mit Inhalt ausgibt. Hat die Tabelle eine gewisse Anzahl an Spalten erreicht, so findet kein Umbruch statt. Die Tabelle geht "unendlich" weiter, das pdf A4 Format schneidet die Tabelle daher bis zur sichtbaren Grenze ab, der restliche Teil fehlt mir also:
Bild


Gibt es hierfür ein Befehl, mit dem ich einen neuen Tebellenumbruch im neuen Absatz starten kann? Anbei der vollständige Code:

[codebox=pys60 file=Unbenannt.txt]import time
from reportlab.platypus import Spacer, Image
from reportlab.platypus import Paragraph, Table, TableStyle, SimpleDocTemplate
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import cm
from reportlab.lib import colors

doc = SimpleDocTemplate("Kenndatenauswertung.pdf", pagesize=A4,)

Daten = [['BEISPIEL', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10'],
['Strom in mA', 100, 300, 500, 700, 900, 1100, 1300, 1500, 1700, 1900, 100, 300, 500, 700, 900, 1100, 1300, 1500, 1700, 1900],
['Spannung in V', 2, 4, 6, 8, 10, 12, 14, 16, 18,20, 2, 4, 6, 8, 10, 12, 14, 16, 18,20]]

Story = []

styles = getSampleStyleSheet()

ptext = '<font size=20>Kenndatenauswertung </font>'
Story.append(Paragraph(ptext, styles["Normal"]))
Story.append(Spacer(1, 22))

ptext = '<font size=10>%s </font>' % time.strftime("%d.%m.%Y %H:%M:%S")
Story.append(Paragraph(ptext, styles["Normal"]))
Story.append(Spacer(1, 32))

ptext = '<font size=12>Die Diodenkennlinie zeigt das Widerstandsverhalten der Diode bei unterschied-\
lichen Stroemen und Die Diodenkennlinie zeigt das Widerstandsverhalten der Diode bei unterschiedlichen Stroemen und Spannungen an. Da die Diode je nach Polung ein unterschiedliches Verhalten aufweist, besteht das \
Kennlinienfeld aus einem Durchlassbereich (Diode in Durchlassrichtung) und einen Sperrbereich \
(Diode in Sperrrichtung). Da Dioden nicht alle gleich sind, hat jede Diode eine andere Kennlinie.</font>'
Story.append(Paragraph(ptext, styles["Normal"]))
Story.append(Spacer(1, 18))

t = Table(Daten)

t.hAlign = 'LEFT'
t.spaceBefore = 10
t.spaceAfter = 10
t.setStyle(TableStyle(
[('BOX', (0,0), (-1,-1), 0.5, colors.black),
('INNERGRID', (0,0), (-1,-1), 0.5, colors.black)]))
Story.append(t)
Story.append(Spacer(1, 32))

logo = "diagramm.gif"
im = Image(logo,width=14*cm,height=14*cm,kind='proportional')
Story.append(im)
Story.append(Spacer(1, 32))



doc.build(Story)[/code]

Re: Reportlab: Tabellenumbruch

Verfasst: Sonntag 10. Juli 2016, 19:39
von noisefloor
Hallo,
Gibt es hierfür ein Befehl, mit dem ich einen neuen Tebellenumbruch im neuen Absatz starten kann?
Nein, gibt's nicht. Jedenfalls keinen mir bekannten. Es war wohl mal angedacht, sowas wie "splitByColumn" einzuführen, ist aber wohl nie passiert.

Reportlab kennt, wenn man mit Frames arbeitet, zwar `reportlab.platypus.flowables.KeepInFrame`, aber das funktioniert mit Tabellen auch nicht.

Abhilfe: selber den Umbruch vornehmen. Einziger kleiner Nachteil: man _muss_ dafür eine Spaltenbreite festlegen, sonst geht das nicht.

Der folgende Code bricht die Tabelle an passender Stelle um (bzw. es werden zwei Tabellen geschrieben):

Code: Alles auswählen

from math import floor, ceil
from reportlab.platypus import Table, TableStyle, SimpleDocTemplate
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import cm
from reportlab.lib import colors
 
doc = SimpleDocTemplate('test.pdf', pagesize=A4,)
available_width = doc.width
col_width = 1*cm
max_number_of_cols = floor(available_width/col_width)

data = [['BEISPIEL', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20'],
        ['Strom in mA', 100, 300, 500, 700, 900, 1100, 1300, 1500, 1700, 1900, 100, 300, 500, 700, 900, 1100, 1300, 1500, 1700, 1900],
        ['Spannung in V', 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40]]
 
story = []
 
styles = getSampleStyleSheet()

for i in range(0, ceil(len(data[0])/max_number_of_cols)):
    data_section = []
    for d in data:
        data_section.append(d[i*max_number_of_cols:(i+1)*max_number_of_cols])
    t = Table(data_section)
    t.hAlign = 'LEFT'
    t.spaceBefore =  10
    t.spaceAfter = 10
    t.setStyle(TableStyle(
        [('BOX', (0,0), (-1,-1), 0.5, colors.black),
         ('INNERGRID', (0,0), (-1,-1), 0.5, colors.black)]))
    story.append(t)
doc.build(story)
Übrigens: AFAIK brechen auch Word. LibreOffice Writer & Co. lange Tabellen nicht automatisch um.

Gruß, noisefloor

Re: Reportlab: Tabellenumbruch

Verfasst: Montag 11. Juli 2016, 17:59
von arren
Hi noisefloor,

vielen Dank für deinen Lösungsansatz. Diese Weise es zu lösen gefällt mir sehr gut.

Ich steh gerade noch mit der folgenden Fehlermeldung, die kommt wenn ich deinen Code probiere, auf dem Schlauch:

Code: Alles auswählen

Traceback (most recent call last):
  File "test.py", line 23, in <module>
    for i in range(0, ceil(len(data[0]) / max_number_of_cols)):
TypeError: range() integer end argument expected, got float.

Re: Reportlab: Tabellenumbruch

Verfasst: Montag 11. Juli 2016, 18:11
von noisefloor
Hallo,

also bei mir funktioniert der Code genau so, mit Python 3.4. Benutzt du Python 2.7?

Gruß, noisefloor

Re: Reportlab: Tabellenumbruch

Verfasst: Montag 11. Juli 2016, 18:17
von arren
Benutze Python 2.7, das wäre auch schon der Grund hierfür.

Re: Reportlab: Tabellenumbruch

Verfasst: Montag 11. Juli 2016, 18:41
von arren
Gibt es denn an dieser Stelle eine Möglichkeit "i" float-fähig zu machen und das ganze somit für 2.7 zu ermöglichen...?!

Re: Reportlab: Tabellenumbruch

Verfasst: Montag 11. Juli 2016, 18:52
von noisefloor
Hallo,

nee, das macht auch keinen Sinn. Du brauchst ja i auch für's Slicing und dann musst du ja auch einen Integer-Wert haben.

Du musst für Python 2.7 eine Zeile wie folgt anpassen:

Code: Alles auswählen

data_section.append(d[int(i*max_number_of_cols):int((i+1)*max_number_of_cols)])
Gruß, noisefloor

Re: Reportlab: Tabellenumbruch

Verfasst: Montag 11. Juli 2016, 19:00
von arren
Danke für deine weitere Behilflichkeit, noisefloor!

Die angepasste Zeile ändert soweit an der Fehlermeldung nichts. Hmm

Re: Reportlab: Tabellenumbruch

Verfasst: Montag 11. Juli 2016, 19:16
von noisefloor
Hallo,

dann weiß ich nicht, was du machst. Weil wenn ich diese Zeile ändere, läuft das bei mir unter Python 2.7.

Gruß, noisefloor

Re: Reportlab: Tabellenumbruch

Verfasst: Montag 11. Juli 2016, 19:29
von arren
So sieht der Code letztlich bei mir aus, der die besagte Fehlermeldung rausgiebt.
Ich poste ihn mal hier, vielleicht fällt ja was auf, ansonsten vielen Dank für die geleistete Hilfestellung!!

Code: Alles auswählen

from math import floor, ceil
from reportlab.platypus import Table, TableStyle, SimpleDocTemplate
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import cm
from reportlab.lib import colors

doc = SimpleDocTemplate('test.pdf', pagesize=A4, )
available_width = doc.width
col_width = 1 * cm
max_number_of_cols = floor(available_width / col_width)

data = [['BEISPIEL', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18',
         '19', '20'],
        ['Strom in mA', 100, 300, 500, 700, 900, 1100, 1300, 1500, 1700, 1900, 100, 300, 500, 700, 900, 1100, 1300,
         1500, 1700, 1900],
        ['Spannung in V', 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40]]

story = []

styles = getSampleStyleSheet()

for i in range(0, ceil(len(data[0]) / max_number_of_cols)):
    data_section = []
    for d in data:
        data_section.append(d[int(i * max_number_of_cols):int((i + 1) * max_number_of_cols)])
    t = Table(data_section)
    t.hAlign = 'LEFT'
    t.spaceBefore = 10
    t.spaceAfter = 10
    t.setStyle(TableStyle(
        [('BOX', (0, 0), (-1, -1), 0.5, colors.black),
         ('INNERGRID', (0, 0), (-1, -1), 0.5, colors.black)]))
    story.append(t)
doc.build(story)

Re: Reportlab: Tabellenumbruch

Verfasst: Montag 11. Juli 2016, 19:45
von noisefloor
Hallo,

ok - habe noch eine Zeile geändert, aber hier nicht gepostet, sorry:

Code: Alles auswählen

for i in range(0, int(ceil(len(data[0])/max_number_of_cols))):
Gruß, noisefloor

Re: Reportlab: Tabellenumbruch

Verfasst: Dienstag 12. Juli 2016, 07:16
von Sirius3
Eigentlich sollte man gleich max_number_of_cols in ein int umwandeln:

Code: Alles auswählen

max_number_of_cols = int(available_width / col_width)
und mit groupby kommt man ohne dieses umständliche slicing aus:

Code: Alles auswählen

from itertools import groupby, izip, count
for _, data_section in groupby(izip(count(), *data), lambda entry: entry[0] // max_number_of_cols):
    t = Table(zip(*data_section)[1:])
    [...]