Tortendiagramme erstellen

From Scribus Wiki
Revision as of 22:10, 10 March 2007 by Uchiki (talk | contribs)
Jump to navigation Jump to search
Dieser Artikel gehört zur Kategorie Deutsche HOWTOs.
Installation Anwendungsfragen PDF-Export DTP Sonstiges

Die Schwierigkeit ein Tortendiagramm in Scribus zu erzeugen besteht darin – selbst wenn es einen Weg gäbe mit den verschiedenen Zeichenwerkzeugen von Scribus eines zu erstellen – daß die Programmierung dafür viel zu aufwendig wäre. Wir brauchen also eine elegantere Lösung. Diese Lösung heißt SVG (Scaleable Vector Graphics).

Es funktioniert nicht einen Kreis zu zeichnen und anschließend zu teilen, denn wir wollen Segmente in verschiedenen Größen und jedes davon mit einer andern Füllfarbe. Auf dieser Seite zeige ich wie die notwendigen Informationen für das Python-Skript weiter unten zusammengestellt werden.

SVG-Spezifikationen und Bögen zeichnen

Für ausführliche Informationen über die SVG-Spezifikationen siehe hier

Abcpie.png Beginnen wir mit diesem Beispiel-Tortenstück (Kreissegment). Wir werden die SVG-Spezifikationen benutzen um dem Programm mitzuteilen wie diese Figur gezeichnet werden soll. Wir starten an Punkt 1, zeichnen eine Linie zu Punkt 2, dann einen Bogen von Punkt 2 nach Punkt 3 und zuletzt wieder eine Linie von Punkt 3 zu Punkt 1.

Hier ist der Pfad-Befehl dafür:

<path d="M 200,200 l 150,0 a150,150 0 0,0 -37,-97 z" 
fill="red" stroke="black" stroke-width="2" stroke-linejoin="round" />

Die erste Zeile legt die Richtungen fest. M ist der Befehl für den Ausgangspunkt – der Kreis(Torten)mittelpunkt – und dieser liegt X=200 und Y=200 (links oben) in unserem SVG-Arbeitsraum. Alle folgenden Befehle in diesem Beispiel sind relativ zu diesem Ausgangspunkt. Mit dem Befehl I (X=150 und Y=0) wird eine Linie 150 Einheiten horizontal nach rechts gezogen, in der Vertikalen ändert sich nichts. (Für diese erste Linie hätte auch der h-Befehl – er zeichnet eine horizontale Linie – benutzt werden können, aber das Skript soll nicht nur für Segmente mit einer horizontalen Linie gelten)

Nun zeichnen wir mit dem a Befehl den Bogen. Die Radien X und Y sind beide auf 150 gesetzt – wären sie nicht gleich dann hätten wir elliptischen Bogen. Als nächstes folgen drei Nullen von denen nur die mittlere für unser Tortendiagramm verändert werden muß: da wir nur zwei Punkte auf einem hypothetischen Kreis brauchen, müßen wir festlegen ob wir die kurze oder die lange Strecke um den Kreis herum gehen wollen. Die Null in der Mitte wählt den kurzen Weg (unser Tortenstück ist kleiner als die halbe Torte)

Nun der komplizierte Teil: die letzen zwei Zahlen (-39, -97) sind relative Abstände vom Punkt 2 zu Punkt 3 – kartesische X,Y-Koordinaten auf dem Monitor. Das z zum Schluß gibt an daß die Figur geschlossen werden soll – zurück zu Punkt 1.

Und woher bekommen wir diese relativen Angaben zu unserem Bogen? -> Trigonometrie

Verschüttete Trigonometriekenntisse reaktivieren

Was repräsentieren Tortendiagramme? Tortendiagramme zeigen uns anschaulich die einzelnen Elemente einer Werteliste, wobei die Größe eines Tortenstückes vom jeweiligen Wert auf der Liste abhängt – die gesamte Torte entspricht der Summe dieser Werte. Um nun herauszufinden wie groß das Tortenstück angezeigt werden soll, mußt Du den einzelnen Wert durch die Summe aller Werte auf der Liste teilen. Da die Torte 360° umfaßt, sieht die Formel so aus:

  Winkel= [(Wert)/Summe aller Werte] × 360 

heraus kommt dabei der Winkel den das betreffende Tortenstück abdeckt.

Beispiel: Wert=25, Summe aller Werte=100 (der Wert entspricht also einem Viertel -> ein Viertelkreis für das Segment (90°)

[(25/100)] * 360 = 90

123pie.png Wir suchen nun die X,Y-Koordinaten von Punkt 3 in Bezug auf den Anfangspunkt (den Kreismittelpunkt) – siehe die relativen Parameter die weiter oben für die SVG-Figur benutzt wurden. Sobald wir diese Koordinaten wissen, subtrahieren wir sie von den Koordinaten des Punktes 2 – dann haben wir die relativen Werte für den SVG-Pfadbefehl. Stellen wir uns ein rechtwinkliges Dreieck mit der Linie 1-3 als Hypotenuse vor. Die Koordinaten von Punkt 3 sind X=Grundlinie (Ankathete) und Y=Höhe (Gegenkathete) des imaginären Dreieckes. Der Abstand 1-3 (Hypotenuse) ist unser Radius, also können wir über Winkelfunktionen den Winkel unseres Tortenstückes bestimmen:
  x = cos(Winkel) × Radius
und
  y = sin(Winkel) × Radius
Wie geht es mit dem nächsten Segment weiter? Wir bleiben bei unserer Startlinie als X-Achse für unsere Berechnungen. Für die folgenden Segmente gilt weiterhin unser Anfangspunkt. Wir brauchen nicht die Winkel für jedes Segment wissen, wir brauchen nur die "Eck"punkte der Bögen für unsere SVG-Berechnung.

Addiere den Wert des ersten Tortenstückes zum Wert des zweiten hinzu und teile diese Summe durch die gesamte Torte (360°) für die Unterteilung der Torte

  Winkel = [(Wert1 + Wert2)/Summe aller Werte] × 360 
1234pie.png

Wenn wir nun die Sinus- und Cosinusformel von weiter oben mit diesem stumpfen Winkel benutzen, erhalten wir als Ergebnis die X,Y-Koordinaten für den Punkt 4.

Wir können uns rund um die Torte arbeiten und mit den relativen Berechnungen für alle Punkte die Koordinaten ermitteln wie in piechart.py. Vom mathematischen standpunkt aus erhält man genauere Ergebnisse wenn immer vom gleichen Startpunkt aus gerechnet wird. Bei aufeinanderfolgenden Berechnungen bei denen der Ausgangspunkt jedesmal verschoben ist, addieren sich Fehler durch geringfügige Ungenauigkeiten – als Ergebnis könnte beim letzten Kreissegment eine Lücke oder eine Überlappung auftreten.

piechart.py benutzen

Wenn Du piechart.py startest (Script > Scripte für Scribus) öffnet sich ein Eingabefenster mit der Frage nach einem Namen für die zu speichernde SVG-Datei. An den Namen den Du eingibst wird automatisch .svg angehängt.

Das nächste Eingabefenster erwartet die Daten – die Werte aus denen die Torte berechnet und erstellt wird. Sobald Du einen Wert eingegeben hast und OK klickst (oder die Eingabe-Taste drückst) erscheint dasselbe Eingabefenster (leer) wieder, solange bis Du eine 0 oder Du die Eingabezeile leer läßt und OK Klickst/die Eingabe-Taste drückst.

Ein Meldungsfenster teilt Dir mit daß die Datei (im Skript-Ordner) erstellt wurde.

Einen undokumentierten importSVG() Befehl habe ich auskommentiert, da dies nicht in den Scribus-Versionen < 1.3.x funktioniert. Darüberhinaus auch wenn es in den 1.3.x -Versionen funktioniert – die neu eingefügte Seite hat eine andere Größe als das schon geöffnete Dokument – dieser Befehl ist also im Moment nicht sehr nützlich. Da es sich um einen undokumentierten Befehl handelt ist nicht klar ob andere Parameter dieses Verhalten korrigieren würden.

Einige Beispiele
Piechart1.png Hier sind zwei Ausgabebeispiele aus gleichen Daten. Die rechte Torte erhälst Du wenn im Skript die Rahmenfarbe (bordercolor) auf none gesetzt wird, die Linienstärke (stroke-width) auf 0, oder den Form-Rahmen in Scribus ganz normal bearbeitest.

Piechart2.png

Piechart3.png Dieses letzte Beispiel kann ganz einfach in Scribus nachgearbeitet werden: Zuerst löse die Gruppierung des SVGs auf - Kontextmenü (Rechtsklick auf das Objekt) > Gruppierung auflösen, Tastaturkürzel: Strg+U.
Nebenbei bemerkt, alle Abbildungen
auf dieser Wiki-Seite entstanden indem
Tortendiagramme mit piechart.py erstellt, in Scribus nachbearbeitet und als Screen-
shot gespeichert wurden.

Versuche das nicht zu Hause (in der Schule / bei der Arbeit)

Eine Bemerkung zum Zusammenhang von Ästhetik und genauer Darstellung:

Manchmal sieht man gekippte Tortensegmente, wahrscheinlich um es "besser aussehen" zu lassen. Dieses Beispiel zeigt was passiert wenn Du das tust. In beiden Abbildungen sind die Segmente jeweils aus den gleichen Daten erstellt. Obwohl die Segmente die gleiche Größe haben sollten sind sie klar erkennbar auf der rechten Seite ungleich – die Position des Segmentes auf dem Kreis beeinflußt die Darstellungsgröße.

Piechart5.png

piechartDE.py

Das englische Original liegt hier: piechart.py Im folgenden Skript sind die Fensterbezeichnungen, Meldungen und Kommentare übersetzt. Abspeichern als piechartDE.py (In Windows C:\Programme\Scribus 1.3.x\share\scripts - prüfe auch ob dieser Pfad in den Allgemeinen Einstellungen > Allgemein als Pfad für die Skripte gesetzt ist). In diesen Ordner werden auch die erzeugten SVGs gespeichert.

#!/usr/bin/env python
# File piechart.py
# Created 2006-05-29
# Gregory Pittman
# Übersetzung der Captions, Meldungen, Kommentare
# 04. Februar 2007 Uchiki
# Erstellt automatisch ein Tortendiagramm als SVG-Datei
# aus einer Werteliste
# Skript funktioniert unter Scribus Versionen 1.2.4.1 und 1.3.x
##########################################
from __future__ import division

import math
import scribus
# Wir erstellen eine Liste L, geben Werte ein, und kopieren die Liste zum Ende in eine Datei
L = ['<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n']
L.append('<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"\n') 
L.append('"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n')
L.append('<svg width="20cm" height="20cm" xmlns="http://www.w3.org/2000/svg" version="1.1">\n')
svgfile = scribus.valueDialog('SVG Datei','Name der SVG-Datei\n".svg" wird automatisch angehängt')
svgfile = svgfile + '.svg'
sectors = []
while 1:
    newvalue = scribus.valueDialog('Dateneingabe','Werte für die einzelnen Segmente des Tortendiagrammes eingeben\n- zum 

Beenden 0 eingeben oder leere Eingabezeile')
    if (newvalue == ''):
        newvalue = '0'
    newnum = float(newvalue)
    if newnum == 0:break
    sectors.append(newnum)
total = 0
i = 0
seg = 0
radius = 150
startx = 200   # X-Koordinate (Monitor) des Anfangspunktes : Mittelpunkt der Torte
starty = 200   # Y-Koordinate (Monitor) des Anfangspunktes : Mittelpunkt der Torte
lastx = radius # Start Koordinaten des 
lasty = 0      # ersten Bogens
# Falls Dir die Farben nicht gefallen -- hier kannst Du andere angeben 
# Wenn Du mehr als 9 Farben brauchst, kannst Du die Liste um so viele wie Du willst erweitern
colors = ['red','blue','yellow','magenta','orange','slateblue','slategrey','greenyellow','wheat']
bordercolor = 'black'
for n in sectors:
    total = total + n  # im voraus zu berechnen, da wir die Summe für die folgende Schleife
                       # brauchen
for n in sectors:
    arc = "0"                   # zeichnet kurzen Bogen (< 180 Grad)
    seg = n/total * 360 + seg   # aktueller Winkel plus alle vorausgegangenen
    if ((n/total * 360) > 180): # für den Fall das dieses Segment > 180 Grad ist
        arc = "1"
    radseg = math.radians(seg)  # Konvertierung zu radians für Cosinus-, Sinusfunktion
    nextx = int(math.cos(radseg) * radius)
    nexty = int(math.sin(radseg) * radius)
# Das Minuszeichen steht hier (-(lasty)), da in unserer Berechnung der Y-Wert des Graphen die
# positiven Zahlen 'nach oben' bedeuten, auf dem Monitor jedoch bedeuten sie 'nach unten'.
    L.append('<path d="M '+str(startx)+','+str(starty) + ' l '+str(lastx)+','+str(-(lasty))+' a' + str(radius) + ',' + 

str(radius) + ' 0 ' + arc + ',0 '+str(nextx - lastx)+','+str(-(nexty - lasty))+ ' z" \n') 
    L.append('fill="'+colors[i]+'" stroke="' + bordercolor + '" stroke-width="2" stroke-linejoin="round" />\n')
# Wir schreiben die XML-Befehle für jedes Segment einzeln und vergessen die alten Punkte die
# nicht mehr gebraucht werden; nextx wird zu lastx für das darauffolgende Segment
    lastx = nextx
    lasty = nexty
    i += 1
L.append('</svg>')         # End-Tag für die SVG-Datei
output = open(svgfile,'w') # Wir haben alles berechnet und die Liste L
output.writelines(L)       # mit unseren Befehlen erstellt, jetzt wird
output.close()             # alles in eine Datei geschrieben.
endmessage = svgfile + ' wurde erstellt\nund im Script-Ordner gespeichert'
scribus.messageBox("Fertig",endmessage,icon=0,button1=1)  # Meldungsfenster zur Information


# if scribus.haveDoc():            	# Die letzte Anweisung ist auskommentiert - sie erstellt 
# 	scribus.importSVG(svgfile)	# eine neue Seite. Dies funktioniert aber nicht
				   	# bei Scribus-Versionen vor <1.3.x 
					# Die SVG-Datei wird trotzdem gespeichert und kann
					# manuell importiert werden.

Noch mehr davon

Was ist mit einer Legende für unser Tortendiagramm? Ganz einfach:

  • nach lasty = 0 füge folgende Zeile ein:
ykey = 25
  • vor der Zeile lastx = nextx füge diese zwei Zeilen ein:
    L.append('<rect x="375" y="'+ str(ykey) + '" width="40" height="30" fill="'+colors[i] + '" stroke="black" stroke-width="1"/>\n')
    ykey = ykey + 35

Wenn Du nun das abgeänderte Skript laufen läßt erhälst Du das:

Piechart4.png In der Scribus-Dokumentation findest Du die Empfehlung Texte (z.B. die Werte, oder welche Art Daten dargestellt werden) zu diesem Kreisdiagramm in Scribus einzufügen – vor allem für den evtl. PDF-Export ist das besser (Textauflösung)

Wenn Du keine Legende (oder einzelne Legendeneinträge) willst, kannst Du wie weiter oben beschrieben die Gruppierung für das SVG auflösen und entsprechend bearbeiten. Möglich wäre auch ein weiteres Eingabefenster zu Beginn des Skriptes einzufügen und abzufragen ob eine Legende gewünscht wird. Dazu wandle die L.append('<rect... Anweisung in eine if -Bedingung (abhängig von der Eingabe) um. Wenn Du keine Legende erstellen willst, spielen die Zuweisung und die Veränderungen in der ykey-Variablen keine Rolle.

Noch mehr?

Sicher, aber das ist Deine "Hausaufgabe"

Schau Dir diesen Skriptausschnitt aus Creating a Graph, Part 2 an:

    caption = "Scale for this graph\n\n" "X: " + str(xmark) + "units/mark\n" + "Y: "
    caption = caption + str(ymark) + "units/mark\n" + str(xscale) + " pts/unit (X)\n"
    caption = caption + str(yscale) + " pts/unit (Y)"
# These caption statements can be combined to one line
    S = scribus.createText(200, 120, 150, 80)
    scribus.setTextColor("Blue", S)
    scribus.setText(caption, S)
    scribus.setTextAlignment(1, S)
    scribus.setFont("Nimbus Roman No9 L Bold", S)
    scribus.setFontSize(8, S)

Mit ein paar Veränderungen kannst Du einen Textrahmen zu piechart.py/piechartDE.py hinzufügen. In diesem stehen dann die Zahlen die Du eingibst, sowie der prozentuale Wert den diese Zahlen repräsentieren – für den Fall das Du das neben dem Tortendiagramm brauchst. Dieser Rahmen wird jedoch in Scribus erzeugt und nicht der SVG-Datei hinzugefügt.