Über dieses Handbuch

Dieses Projekt ergänzt die API-Dokumentation zur Qwt-Bibliothek und bietet sowas wie ein Programmiererhandbuch mit vielen Details zu den Internas der Bibliothek. Der Fokus liegt aber ganz klar auf der QwtPlot Diagrammkomponente. Das ist übrigends schon die 2. Auflage (komplett neu überarbeitet), weil es zum Zeitpunkt der ersten Ausgabe noch kein tolles AsciiDoc gab und ich irgendwie beim Textschreiben hängengeblieben bin.

Die Texte und Bilder stehen unter der Creative-Commons BY-NC Lizenz (siehe Lizenztext im Qwt Handbuch Repository https://github.com/ghorwin/QwtBook), können also frei verwendet, modifiziert und angepasst werden, aber bitte nicht publiziert oder zum Training von kommerziellen KI-Systemen benutzt werden. Alle Quelltextbeispiele, sowohl im Text, als auch in den herunterladbaren Tutorial/Beispiel-Quelltextarchiven, stehen unter der MIT-Lizenz und können damit in open-source wie auch kommerziellen Projekten genutzt werden.

Häufig wird das QwtPlot ja benutzt, um ein eigenes Postprozessing oder Visualisierungstool zu schreiben. Vielleicht lohnt sich hier der Blick auf das kostenfreie PostProc 2, welches wir an der TU Dresden entwickelt haben und das intern ein angepasstes und erweitertes QwtPlot verwendet. Das Programm ist spezialisiert auf die Visualisierung von dynamischer Simulationsergebnissen und Messdaten/Zeitreihen und man kann da einen guten Eindruck davon bekommen, was mit QwtPlot alles machbar ist. Postproc 2 wird weiter aktiv gepflegt und kann hier heruntergeladen werden: https://bauklimatik-dresden.de/software/postproc2/.

Viel Spaß bei der Lektüre - und falls noch Inhalte fehlen, einfach Geduld haben und später wiederkommen (oder im Github-Repo ein Issue anlegen). Und schaut Euch vielleicht auch meine anderen Tutorials unter https://schneggenport.de an!

 — Andreas Nicolai

1. Überblick über die Qwt Bibliothek

Qwt - Qt Widgets for Technical Applications ist eine Open-Source Bibliothek für technische Anwendungen und stellt bestimmte Widgets für Anzeigen und Kontrollkomponenten bereit. Die wohl wichtigste Komponente der Qwt Bibliothek ist das QwtPlot, eine sehr flexible und mächtige Diagrammkomponente.

exBode
Abbildung 1. Beispiel für die QwtPlot-Komponente

Die Qwt Bibliothek steht unter einer Open-Source-Lizenz, wurde und wird aktiv vom Entwickler Uwe Rathmann gepflegt und wird auf SourceForge.net gehostet:

1.1. Entwicklungsgeschichte

  • die erste Version der Qwt-Bibliothek stammt noch aus dem Jahr 1997 von Josef Wilgen

  • seit 2002 wird die Bibliothek von Uwe Rathmann entwickelt und gepflegt

  • Version 5 ist wohl am weitesten verbreitet (erstes Release vom 26.02.2007)

  • Version 6 (erstes Release vom 15.04.2011, kein Qt3 Support mehr) enthält wesentliche API-Änderungen

  • aktuelle stabile Version 6.3.0 (Stand Mai 2025)

  • im trunk gibt es zum Teil bereits wesentlich mehr und fortgeschrittene Funktionen

1.1.1. Download der Bibliothek

Die Qwt Bibliothek kann von der Qwt SourceForge Projektseite als Quelltextarchiv geladen werden. Unter Linux wird Qwt bei vielen Distributionen als Paket gehalten. Genau genommen gibt es mehrere Pakete für die unterschiedlichen Qwt-Bibliotheksversionen bzw. Qt Versionen. Details zur Installation und Verwendung der Bibliothek gibt es im Kapitel 13.

1.2. Widget-Konzept und Erscheinungsbild

Die Qwt Bibliothek liefert Komponenten, welche analog zu den normalen Qt-Widgets in Desktopanwendungen verwendet werden können. Die Komponenten verwenden die Qt Palette, sodass die Qwt-Widgets in die jeweilige Oberfläche passen. Dadurch integrieren sich die Widgets nahtlos in Programmoberflächen. Einzelne Komponenten des QwtPlot unterstützen auch Styles. So ermöglichen z.B. Abrundungseffekte beim Plot-Widget das Immitieren klassischer Anzeigen.

styledDialsAndPlot
Abbildung 2. QwtPlot mit abgerundeten Ecken

Details zum Styling und zur Anpassung des Erscheinungsbildes sind im Kapitel 10 zu finden.

1.3. Besitzer/Eigentümer-Konzept des QwtPlot-Widgets

Eine grundlegende Eigenschaft der QwtPlot-Klasse ist die Besitzübername hinzugefügter Elemente. Dies gilt allgemein für alle Elemente des Plots (Linien, Marker, Legende, …​). D.h. nach Übertragung der Eigentümerschaft kümmert sich das QwtPlot um das Aufräumen des Speichers.

Einmal hinzugefügte Elemente werden nicht wieder losgelöst werden (bzw. nur über einen Trick, wie im Kapitel 12.1 beschrieben wird). Daher ist es sinnvoll, bei veränderlichen Diagrammelementen einen Mechanismus zur jeweiligen Neuerstellung eines Zeichenobjekts vorzusehen (Factory-Konzept).

1.4. Zeichenobjekte und deren Achsenabhängigkeit

Ein wesentliches Designmerkmal beim QwtPlot ist die Möglichkeit, beliebige Zeichenobjekte (Kurven, Marker, Legende, …​) dem Plot zu übergeben. Damit sich diese Zeichenobjekte (engl. PlotItem) am Koordinatengitter ausrichten können, wird ihnen eine Achsenabhängigkeit gegeben. Dadurch erhalten diese Zeichenobjekte eine Information, wann immer sich die Achsenskalierung ändert (durch Zoomen, oder Änderung der Wertebereiche etc.).

Diese Funktionalität definiert die zentrale Bedeutung der (bis zu) 4 Achsen im Diagramm. Deswegen sind diese auch fest im QwtPlot verankert und werden nicht wie andere Zeichenobjekte beliebig hinzugefügt.

1.5. Vererbungskonzept

Grundsätzlich ist das QwtPlot und die beteiligten Klassen auf maximale Anpassungsfähigkeit ausgelegt, d.h. es wird (fast) überall Polymorphie unterstützt. Wenn die eingebaute Funktionalität nicht zureichend ist, kann man einfach immer die entsprechende Klasse ableiten und die jeweils anzupassende Funktion re-implementieren und verändern. Dies wird anhand von Beispielen in den individuellen Kapiteln des Handbuchs beschrieben.

1.6. Verwendung der Designer Plugins

Die Qwt Bibliothek bringt Plugins für QtDesigner mit, welche das Einfügen von Qwt-Komponenten in ui-Dateien erleichtert. Es lassen sich jedoch keine QwtPlot-Eigenschaften festlegen oder Kurven hinzufügen. Die eigentliche Anpassung und Ausgestaltung des Plots erfolgt im Quelltext. Deswegen wird die Konfiguration und Anpassung des QwtPlot in diesem Handbuch ausschließlich durch normale API-Aufrufe demonstriert.

Soll das QwtPlot auch ohne Designer-Plugins im grafischen QtDesigner-Editor eingefügt werden, kann man einfach ein QWidget einfügen und dieses als Platzhalter für die QwtPlot-Klasse definieren, siehe Kapitel 13.5.

Eine Beschreibung, wie die Designer-plugins erstellt und in Qt Creator/Designer integriert werden ist im Kapitel 13.3 beschrieben.

2. Erste Schritte und ein interaktives Diagramm

Um mit der Qwt-Bibliothek warm zu werden, erstellen wir in einem einfachen Beispiel ein interaktives Diagramm mit der QwtPlot-Komponente. Der komplette Beispielquelltext ist als 7z-Archiv herunterladbar: tutorial1.7z

2.1. Programmrohbau

2.1.1. QMake Projektdatei

Wir beginnen mit der qmake-Projektdatei, in der wir den Pfad für die Header-Dateien der Bibliothek und die zu linkende Bibliothek festlegen. Hier gehe ich davon aus, dass Qwt aus dem 6.3.0er Quelltextarchiv gebaut und lokal in die Standardverzeichnisse (C:\qwt-6.3.0 unter Windows und /usr/local/qwt-6.3.0 unter Linux/Mac) installiert wurde. Infos über das Compilieren der Bibliothek aus dem Quelltext und Installation gibt es in Kapitel 13.

TARGET   = Tutorial1
QT       += core gui widgets
CONFIG   += c++11

win32 {
	# Pfad zu den Qwt Headerdateien hinzufügen
	INCLUDEPATH += C:/qwt-6.3.0/include
	CONFIG(debug, debug|release) {
		QWTLIB = qwtd
	}
	else {
		QWTLIB = qwt
	}
	# Linkerpfad
	LIBS += -LC://qwt-6.3.0/lib -l$$QWTLIB
}
else {
	# Pfad zu den Qwt Headerdateien hinzufügen
	INCLUDEPATH += /usr/local/qwt-6.3.0/include/
	# Linkerpfad, unter Linux wird standardmäßig nur die release-Version der Lib gebaut und installiert
	LIBS += -L/usr/local/qwt-6.3.0/lib -lqwt
}

SOURCES += main.cpp

Dies ist eine .pro-Datei für eine Qwt-6.3.0-Installation aus dem Quelltext mit Standardeinstellungen (siehe Kapitel 13.2).

Beachte, dass die im Debug-Modus kompilierte Qwt-Bibliothek ein angehängtes d hat. Unter Linux wird standardmäßig nur die release-Version gebaut und installiert, daher braucht man hier die Fallunterscheidung nicht.

2.1.2. Minimalistisches Hauptprogramm

Für die Verwendung des QwtPlot braucht man nur eine sehr minimalistische main.cpp.

Hauptprogramm, in dem nur das nackige Plot selbst erstellt und angezeigt wird
#include <QApplication>

#include <QwtPlot>

int main(int argc, char *argv[]) {
	QApplication a(argc, argv);
	QwtPlot plot;
	plot.resize(800,500);
	plot.show();
	return a.exec();
}

Wenn man das Programm compiliert hat und ausführen will, beklagt sich Windows über eine fehlende DLL. Dazu in den Projekteinstellungen, unter "Ausführen", im Abschnitt "Umgebung" die PATH-Variable bearbeiten und dort den Pfad C:\qwt-6.3.0\lib hinzufügen.

Das Programm zeigt ein ziemlich langweiliges (und hässliches) Diagrammfenster (später wird das noch ansehnlicher gestaltet).

Tutorial1 a
Abbildung 3. Das nackte Plotwidget

Ein Hinweis zu den Header-Dateien der Qwt-Bibliothek.

Analog zu Qt Klassen werden die Qwt-Klassen über den gleichnamigen Header eingebunden, also:

#include <QwtPlot>       // für Klasse QwtPlot
#include <QwtPlotCurve>  // für Klasse QwtPlotCurve
#include <QwtLegend>     // für Klasse QwtLegend
// ...

Diese Header-Dateien sind aber nur Wrapper um die eigentlichen Include-Dateien, mit dem Benennungsschema:

#include <qwt_plot.h>        // für Klasse QwtPlot
#include <qwt_plot_curve.h>  // für Klasse QwtPlotCurve
#include <qwt_legend.h>      // für Klasse QwtLegend
// ...

In früheren Versionen der Qwt-lib (auch der Debian-Paket-Version libqwt-qt5-dev) wurden die Wrapper-Headerdateien nach dem neuen Namensschema nicht installiert, sodass man die originalen qwt_xxx.h Includes verwenden muss. Wenn man also auch ältere Qwt-Versionen unterstützen möchte, bzw. unter Linux die Paketversion verwenden will, sollte die originalen Headerdateinamen verwenden.

2.2. Diagrammelemente hinzufügen

2.2.1. Linie hinzufügen

Als erstes fügen wir eine Linie bzw. Diagrammkurve hinzu (Header QwtPlotCurve bzw. qwt_plot_curve.h):

Hauptprogramm mit einer Linie
#include <QApplication>

#include <QwtPlot>
#include <QwtPlotCurve>

int main(int argc, char *argv[]) {
	QApplication a(argc, argv);
	QwtPlot plot;
	plot.resize(500,300);

	// etwas Abstand zwischen Rand und Achsentiteln
	plot.setContentsMargins(8,8,8,8);
	// Hintergrund der Zeichenfläche soll weiß sein
	plot.setCanvasBackground( Qt::white );

	// Daten zum Darstellen einlesen
	QVector<double> x, y;
	QFile f("spektrum.tsv");  // Datei enthält 2 Spalten
	f.open(QFile::ReadOnly);
	QTextStream strm(&f);
	strm.readLine(); // Kopfzeile überspringen
	while (!strm.atEnd()) {
		double xval, yval;
		strm >> xval >> yval;
		x.append(xval);
		y.append(yval);
	}

	QwtPlotCurve *curve = new QwtPlotCurve();
	curve->setPen(QColor(180,40,20), 0);
	curve->setTitle("Gamma-Spektrum");
	curve->setRenderHint( QwtPlotItem::RenderAntialiased, true ); // Antialiasing verwenden
	curve->setSamples(x, y);
	curve->attach(&plot); // Plot takes ownership

	plot.show();
	return a.exec();
}

Im erweiterten Hauptprogramm wird zunächst der Header für die QwtPlotCurve eingebunden. Das Kurvenobjekt selbst wird mit new auf dem Heap erstellt. Die Daten der Kurve lesen wir aus einer Textdatei (2 Spalten, mit Kopfzeile) aus. Die Datei spektrum.tsv ist im Archiv des Tutorialquelltextes enthalten.

Grundsätzlich gilt beim QwtPlot: Alle Plotelemente müssen via new auf dem Heap erstellt werden und dem Plot dann übergeben werden. Dieses wird dann Besitzer und gibt den Speicher frei. Deshalb dürfen Linien, Legende, Marker etc. niemals als Stack-Variablen erstellt werden, sonst gibt es (je nach Destruktoraufrufreihenfolge) einen Speicherzugriffsfehler.

Attribute wie Linienfarbe, Titel (wird später in der Legende angezeigt), und Antialising werden gesetzt (im Kapitel 4 werden alle Eigenschaften von Linien im Detail erläutert).

Die Funktion setSamples() setzt die Daten der Linie. Wichtig ist hier, dass die übergebenen Vectoren die gleiche Länge haben. Es handelt sich um eine parametrische Kurve, d.h. weder x noch y Werte müssen monoton sein oder sonstwelchen Regeln folgen. Jedes x,y Wertepaar definiert einen Punkt und diese Punkte werden mit der Linie verbunden.

Die Funktion attach() fügt das QwtPlotCurve-Objekt zum Diagramm hinzu.

Beim Hinzufügen der Linie mittels attach() zum Diagramm wird das Plot neuer Eigentümer und kümmert sich um das Aufräumen des Speichers. Man muss also nicht mehr manuell delete für das QwtPlotCurve-Objekt aufrufen.

Zusätzlich zu dem Code, welcher die Linie hinzufügt, wurden noch 2 kleine Anpassungen am Erscheinungsbild vorgenommen:

  • Ränder wurden mittels setContentsMargins() hinzugefügt (siehe auch QWidgdet::setContentsMargins() )

  • der Hintergrund der Zeichenfläche (canvas) wurde weiß gefärbt.

Das Ergebnis sieht schon eher nach Diagramm aus.

Tutorial1 b
Abbildung 4. Diagramm mit Linie

2.2.2. Legende hinzufügen

Als nächstes wird eine Legende eingefügt (Header QwtLegend bzw. qwt_legend.h):

// Legende anzeigen
QwtLegend * legend = new QwtLegend();
QFont legendFont;
legendFont.setPointSize(8);
legend->setFont(legendFont);
plot.insertLegend( legend , QwtPlot::BottomLegend); // plot takes ownership

Auch hier wird oben wieder der Header für die Klasse QwtLegend eingebunden.

Die Legende bekommt hier noch einen veränderten Font. Das weitere Anpassen der Legende wird in Kapitel 5 beschrieben.

Die Legende kann links, rechts, oberhalb oder unterhalb der Zeichenfläche liegen, oder in der Zeichenfläche selbst. Die Platzierung wird beim Aufruf von insertLegend() festlegegt.

Das Plot nimmt beim Aufruf von insertLegend() wiederum Besitz vom Legendenobjekt und kümmert sich um das Aufräumen des Speichers.

2.2.3. Diagrammtitel hinzufügen

// Titel hinzufügen
QwtText text("Gamma-Spektrum");
QFont titleFont;
titleFont.setBold(true);
titleFont.setPointSize(10);
text.setFont(titleFont);
plot.setTitle(text);

Die Klasse QwtText (Header QwtText bzw. qwt_text.h) kapselt einen QString und ergänzt Funktionalität zum Rendern von mathematischen Symbolen mittels MathML (siehe Kapitel 8.1).

2.2.4. Diagrammraster hinzufügen

Gitterlinien werden durch das Zeichenobjekt QwtPlotGrid gezeichnet (Header QwtPlotGrid bzw. qwt_plot_grid.h):

// Haupt- und Nebengitter anzeigen
QwtPlotGrid *grid = new QwtPlotGrid();
QPen gridPen(Qt::gray);
gridPen.setStyle(Qt::DashLine);
grid->setMajorPen(gridPen);
// Minor grid
grid->enableYMin( true );
gridPen.setColor(Qt::lightGray);
gridPen.setStyle(Qt::DotLine);
grid->setMinorPen(gridPen);
grid->attach( &plot ); // plot takes ownership

Das Raster selbst kann hinsichtlich der Stifts (QPen) für das Haupt- und Nebengitter angepasst werden. Die Funktion enableYMin() schaltet das Nebengitter für die Y-Achse ein. Wie auch bei den Plotkurven übergibt attach() das QwtPlotGrid Objekt an das QwtPlot, welches sich dann um die Speicherverwaltung kümmert.

Ein Raster wird standardmäßig an eine x- und y-Achse gebunden, wobei man aber auch die Gitterlinien für eine der Achsen ausblenden kann. Wenn man z.B. ein Diagramm mit 2 y-Achsen hat und für jede ein Gitterraster anzeigen möchte (auch wenn das meistens verwirrend aussieht), dann braucht man zwei QwtPlotGrid-Objekte.

Inzwischen sieht das Diagramm schon ganz ansehnlich aus.

Tutorial1 c
Abbildung 5. Diagramm mit Linie, Legende, Titel und Gitterlinien

2.2.5. Achsenkonfiguration

Das QwtPlot hat 4 Achsen eingebaut, genannt:

  • QwtPlot::yLeft und QwtPlot::yRight

  • QwtPlot::xBottom und QwtPlot::xTop

Standardmäßig sind die Achsen xBottom und yLeft sichtbar, wie im bisher verwendeten Plot.

Jedes Zeichenelement im Plot (Kurven, Marker, …​) wird einer oder mehrerer Achsen zugeordnet. In unserem Einführungsbeispiel verwendet die QwtPlotCurve standardmäßig die Achsen xBottom und yLeft.

Die Achsen können wie folgt konfiguriert werden.

// Achsen formatieren
QFont axisFont;
axisFont.setPointSize(8);
axisFont.setBold(true);
QFont axisLabelFont;
axisLabelFont.setPointSize(8);
// X-Achse
QwtText axisTitle("Kanal");
axisTitle.setFont(axisFont);
// Titel Text und Font setzen
plot.setAxisTitle(QwtPlot::xBottom, axisTitle);
// Font für Achsenzahlen setzen
plot.setAxisFont(QwtPlot::xBottom, axisLabelFont);
// Y-Achse
axisTitle.setText("Ereignisse");
plot.setAxisTitle(QwtPlot::yLeft, axisTitle);
plot.setAxisFont(QwtPlot::yLeft, axisLabelFont);

Der Titel jeder Achse wird wiederum über ein QwtText-Objekt (enthält Text und Font) gesetzt. Der Font für die Zahlen an den Achsen selbst wird über setAxisFont() geändert.

Die Achsen selbst lassen sich vielfältig anpassen, siehe Kapitel 7.

Tutorial1 d
Abbildung 6. Vollständig formatiertes Diagramm

Die Achsen passen sich standardmäßig automatisch an den Wertebereich der angezeigten Kurven an. Das kann man natürlich auch ändern, siehe Kapitel 7.

2.2.6. Logarithmische Achsen

Das QwtPlot kann auch logarithmische Achsen verwenden. Dazu muss man eine anderen Skalenberechnungsklasse einbinden, die QwtLogScaleEngine (Header QwtLogScaleEngine bzw. qwt_scale_engine.h):

// Logarithmische Y-Achse
QwtLogScaleEngine * logScale = new QwtLogScaleEngine();
plot.setAxisScaleEngine(QwtPlot::yLeft, logScale); // plot takes ownership
// manuelle Achsenlimits festlegen, da autoscale bei log-Achsen nicht sinnvoll funktioniert
plot.setAxisScale(QwtPlot::yLeft, 1e-3,1000);

Beim Aufruf von setAxisScaleEngine() nimmt das Plot wiederum das Objekt in Besitz und kümmert sich dann um das Speicheraufräumen.

Kapitel 7 beschreibt die Details der ScaleEngine und gibt weitere Beispiele.

Tutorial1 e
Abbildung 7. Diagramm mit logarithmischer Y-Achse

2.2.7. Markierungslinien

Ein weiteres Zeichenelement, das man hin und wieder braucht, sind horizontale oder vertikale Markierungslinien. Beispielhaft fügen wir eine solche Linie mal dem Plot hinzu (Header QwtPlotMarker bzw. qwt_plot_marker.h):

// Vertikale, gestrichelte Plot-Markierung einfügen
QwtPlotMarker * marker = new QwtPlotMarker("207,50 keV");
marker->setLabelOrientation(Qt::Vertical); // Vertikale Linie
marker->setLabelAlignment(Qt::AlignRight | Qt::AlignBottom); // Label unten und rechts von der Linie
marker->setValue(36, 0); // bei vertikalen Linien muss die x-Koordinate festgelegt werden
QPen markerPen(QColor(40,60,255));
markerPen.setStyle(Qt::SolidLine);
marker->setLinePen(markerPen);
marker->setLineStyle(QwtPlotMarker::VLine);
marker->setLabel(QwtText("207,50 keV"));
marker->attach(&plot); // plot takes ownership

Auch bei den Markern gibt es vielfältige Einstellungsmöglichkeiten, siehe Kapitel 6.

Tutorial1 f
Abbildung 8. Diagramm mit logarithmischer Y-Achse und vertikaler Peak-Markierung

Nun ist das Diagramm selbst fertig und wir widmen uns der Nutzerinteraktion.

2.3. Interaktion mit dem Diagramm

Das QwtPlot bietet die üblichen Interaktionsmöglichkeiten für den Anwender, wie z.B. Herein- und Herauszoonmen, oder Verschieben des Plotausschnitts.

2.3.1. Zoomfunktionalität mit QwtPlotZoomer

Die Zoom-Funktionalität wird über die Klasse QwtPlotZoomer hinzugefügt (Header QwtPlotZoomer bzw. qwt_plot_zoomer.h):

// Zoomer hinzufügen
// Achtung: NICHT QwtPlot selbst als 3 Argument übergeben, sonder das canvas()
QwtPlotZoomer * zoomer = new QwtPlotZoomer(QwtPlot::xBottom, QwtPlot::yLeft, plot.canvas());  // plot takes ownership
zoomer->setTrackerMode( QwtPlotPicker::AlwaysOn ); // Kurvenvwerte unterm Cursor anzeigen

Wenn man mit der Maus über das Diagramm fährt, sieht man bereits einen veränderten Cursor und dank des Aufrufs setTrackerMode(QwtPlotPicker::AlwaysOn) sieht man nun auch die x- und y-Werte (des Achsen xBottom und yLeft) unter dem Cursor.

Hineinzoomen kann man, indem man die Linke Maustaste gedrückt hält, und ein Zoom-Rechteck aufzieht. Das kann man auch mehrmals hintereinander machen. Das QwtPlot merkt sich intern diese Zoomstufen. Herauszoomen kann durch Klick auf die rechte Maustaste, wobei immer eine Zoomstufe hinausgezoomt wird.

Die äußerste Zoomstufe wird im Konstruktor der QwtPlotZoomer-Klasse basierend auf den aktuellen Wertebereichen der bereits hinzugefügten Kurven bestimmt. Sollte man die Werte der Kurven nachträglich ändern, oder den Zoomer hinzufügen, bevor man dem Plot Kurven gegeben hat, so kann man die Funktion QwtPlotZoomer::setZoomBase() aufrufen. Details dazu gibt es im Kapitel 9.

Im Quelltext gibt es noch eine Besonderheit. Während die bisherigen Plotelemente immer mit Memberfunktionen der QwtPlot-Klasse hinzugefügt wurde, bzw. mittels attach(), wird das Zoomerobjekt analog zu Qt Klassen als Kindobjekt der Zeichenfläche gegeben und registriert sich darüber als interaktives Element bei Plot.

Es ist wichtig darauf zu achten, dass man beim Konstruktor der Klasse QwtPlotZoomer als 3. Argument das Canvas-Objekt des Plots übergibt. Dieses erhält man mit der Funktion QwtPlot::canvas(). Wenn man hier stattdessen das Plot selbst übergibt, führt dies zu einem Speicherzugriffsfehler.

Im Konstruktor der QwtPlotZoomer Klasse registriert sich das Objekt als Kind des Canvas-Widgets, wodurch das QObject-System sich um die Speicherverwaltung kümmert. Man muss also das QwtPlotZoomer Objekt nicht freigeben.

Damit der Zoomer weiß, welche Achsen beim Zoom manipuliert werden sollen, muss man die x- und y-Achse im Konstruktor angeben. Möchte man z.B. beide y-Achsen gleichzeitig zoomen, braucht man zwei QwtPlotZoomer-Objekte.

Tutorial1 g
Abbildung 9. Diagramm mit aufgezogenem Zoom-Rechteck

2.3.2. Plotausschnitt verschieben mit QwtPlotPanner

Wenn man Ausschnitt eines hineingezoomten Plots interaktiv verschieben möchte, kann man den QwtPlotPanner hinzufügen (Header QwtPlotZoomer bzw. qwt_plot_zoomer.h):

// Panner hinzufügen, wie auch beim PlotZoomer muss das Canvas-Objekt als Argument übergeben werden
QwtPlotPanner * panner = new QwtPlotPanner(plot.canvas());  // plot takes ownership
panner->setMouseButton(Qt::MidButton); // Mittlere Maustaste verschiebt

Wie beim QwtPlotZoomer wird das Objekt als Kindobjekt des Canvas-Widgets hinzugefügt. Üblich ist das Verschieben von Bildschirminhalten mit gedrückter mittlerer Maustaste, also legt man das mit setMouseButton() fest.

Damit ist das Einstiegstutorial beendet. Mit dem QwtPlot kann man bereits mit wenigen Handgriffen ein voll funktionsfähiges und interaktives Diagramm erstellen. In diesem Tutorial war das QwtPlot gleichzeitig das Anwendungs-Widget. Wenn man das QwtPlot aber in bestehende Designer-Formularklassen einfügen will, gibt es verschiedene Techniken:

  • die Verwendung von Platzhalter-Widgets

  • die Einbindung von Qt Designer Plugins für die Qwt Bibliothek

Diese Methoden sind in Kapitel 13.5 beschrieben.

3. QWT Widgets und Eingabekomponenten

Neben dem QwtPlot gibt es in der Qwt-Bibliothek noch eine Reihe anderer Eingabekomponenten, die in diesem Kapitel kurz vorgestellt werden. Viele dieser Komponenten sind klassischen Anzeigen und Einstellrädern in wissenschafltlich/technischen Geräten nachempfunden.

Für die Anzeige der Skalen verwenden die nachfolgend vorgestellten Komponenten intern zur Darstellung der Skalen die in [sec::axisScales] näher beschriebenen Skalenberechnungs- und -zeichenklassen.

3.1. Schieberegler (Slider)

Die Klasse QwtSlider erlaubt die Darstellung verschiedener Schieberegler, welche mit der Maus oder Tastatur (Cursortasten) bedient werden können. Im Gegensatz zur QSlider Klasse können die Skalen viel flexibler und auch nichtlinear definiert werden.

controlsSlider
Abbildung 10. Beispiele für QwtSlider

Das Beispiel im Screenshot oben ist in der Qwt-Bibliothek als Beispiel controls enthalten.

3.2. Drehräder/Einstellräder und

Die Klasse QwtWheel zeigt ein horizontales oder vertikales Einstellrad. Die Klasse QwtThermo zeigt eine Balkenanzeige, allerdings mit einer flexibel hinterlegbaren Farbtabelle. Dies erlaubt z.B. Farbverläufe oder Farbsprünge bei Übersteigen bestimmter Schwellwerte.

Qt selbst bietet für eine Balkenanzeige die Klasse QProgressBar an, welches sich aber im Erscheinungsbild an den jeweiligen Plattformstil für Fortschrittsbalken orientiert und auch keine Skalen bietet.

controlsWheelThermo
Abbildung 11. Beispiele für QwtWheel und QwtThermo

Das Beispiel im Screenshot oben ist in der Qwt-Bibliothek als Beispiel controls enthalten.

3.3. Drehknöpfe

Die Klasse QwtKnob zeigt einen Drehknopf, mit ebenso flexibel konfigurierbaren Skaleneinheiten. Die Qt-Klasse QDial bietet ebenso ein Einstellrad, jedoch wiederum viel simpler und mit weniger Einstellungsmöglichkeiten hinsichtlich der Skalendarstellung und -skalierung.

controlsKnob
Abbildung 12. Beispiele für QwtKnob

Das Beispiel im Screenshot oben ist in der Qwt-Bibliothek als Beispiel controls enthalten.

3.4. Analoge Zeiger-Anzeigen

Die Klasse QwtDial zeichnet analoge Zeigeranzeigen, die aber auch mit der Maus/Tastatur verändert werden können (wenn man das aktiviert). Die Anzeigen lassen sich farblich sehr individuell konfigurieren.

controlsDials
Abbildung 13. Beispiele für QwtDial

Das Beispiel im Screenshot oben ist in der Qwt-Bibliothek als Beispiel controls enthalten.

Bemerkenswert ist vielleicht noch, dass die Anzeigenadel selbst unabhängig von der Klasse QwtDial durch eine separate Klasse implementiert wird. Als Standard wird hier QwtDialSimpleNeedle verwendet, wie im Screenshot oben. Man kann sich hier aber auch austoben, und selber beliebige Anzeigenadeln entwerfen und integrieren.

4. Liniendiagramme

5. Legende

6. Markierungslinien

7. Plotachsen

Die Achsen/Skalen eines Plots (insgesamt 4, open, unten, links und rechts) können bereits in den mitgeliferten Klassenimplementierungen vielfältig angepasst und verändert werden. Und natürlich können die beteiligten Klassen auch abgeleitet und so beliebig modifiziert/geändert werden.

Die wichtigsten Klassen in Bezug auf die Achsen sind:

  • QwtAxis

  • QwtAbstractScaleDraw und die Spezialisierungen QwtScaleDraw und QwtDateScaleDraw

  • QwtScaleEngine`und die Spezialisierungen `QwtLinearScaleEngine`und `QwtLogScaleEngine

7.1. Allgemeine Achsenformatierung

7.2. Skalen

8. QwtText und Sonderformatierungen

8.1. MathML

9. Interaktiver Zoom und Verschieben von Diagrammausschnitten

10. Anpassung/Styling der Qwt Komponenten

10.1. Allgemeines zu Farbpaletten

Die Qwt-Komponenten verwenden die Qt Palette und deren Farbrollen für die Einfärbung.

10.2. Rahmen und Zeichenfläche des Diagramms

Beim QwtPlot können verschiedene Elemente angepasst werden. Nachfolgend ist ein QwtPlot zu sehen, welches in einem äußeren Widget (dunkelgrau) eingebettet ist. Die hellgraue Fläche ist das eigentliche QwtPlot:

![Rahmen und Zeichenfläche](imgs/plotStylingOverview.png)

Im Screenshot sind die wichtigsten Attribute markiert:

  1. Innenabstand (siehe QWidget::setContentsMargins())

  2. Rahmen (hauptsächlich für den Druck wichtig)

  3. Hintergrund des Plot-Widgets

  4. Zeichenfläche (engl. Canvas) (betrifft Hintergrundfarbe und Rahmen)

10.2.1. Farbe und Rahmen des Plots

Die Farbe des äußeren Bereichs des Plots wird über die Paletteneigenschaft des QwtPlot kontrolliert. Standardmäßig wird der äußere Rand des Plot-Widgets transparant gezeichnet, d.h. die Farbe des darunterliegenden Widgets ist sichtbar. Um eine eigene Farbe zu setzen, muss daher ```setAutoFillBackground(true)``` aufgerufen werden:

QPalette pal = plot.palette();
// Die QPalette::Window Farbrolle definiert die Einfärbung
// des äußeren Plotbereichs
pal.setColor(QPalette::Window, QColor(196,196,220));
plot->setPalette(pal);
// die Eigenschaft "autoFillBackground" muss dafür eingeschaltet sein
plot->setAutoFillBackground(true);

![](imgs/plotStyling1.png)

Hinweis: In Abschnitt [Gradient als Plot-Hintergrund](customization/#gradient-als-plot-hintergrund) wird beschrieben, wie man einen Farbverlauf im Plothintergrund umsetzt, und diesen bei Größenänderung entsprechend anpasst.

Der Rahmen wird wie bei einem normalen Widget angepasst:

plot->setFrameStyle(QFrame::Box | QFrame::Sunken);

Normalerweise ist ein solcher Rahmen nicht notwendig für die Bildschirmdarstellung oder für das Einbetten des QwtPlot in eine Programmoberfläche. Der Rahmen ist jedoch häufig beim [Export/Druck](export) des Widgets sinnvoll.

10.2.2. Zeichenfläche

Die Zeichenfläche kann eingefärbt werden:

plot->setCanvasBackground(Qt::darkGray);

![](imgs/plotStyling3.png)

Der Randabstand zwischen Achsenbeschriftung und Titel zum Rand kann definiert werden:

plot->setContentsMargins(15,10,35,5);

![](imgs/plotStyling4.png)

Die Rahmen um die Zeichenfläche kann durch Anpassen des Zeichenflächenobjekts (QwtPlotCanvas) verändert werden. QwtPlotCanvas ist von QFrame abgeleitet, wodurch es entsprechend angepasst werden kann. Es wird einfach neues Objekt erstellt, konfiguriert und dem Plot übergeben (das QwtPlot wird neuer Besitzer des Zeichenflächenobjekts):

QwtPlotCanvas * canvas = new QwtPlotCanvas(&plot);
canvas->setPalette(Qt::white);
canvas->setFrameStyle(QFrame::Box | QFrame::Plain );
canvas->setLineWidth(1);
plot->setCanvas(canvas);

![](imgs/plotStyling5.png)

Einfacher geht es durch Setzen des Stylesheets für das Canvas-Widget (siehe Qt-Widgets Dokumentation, welche Attribute unterstützt werden):

plot->canvas()->setStyleSheet(
    "border: 1px solid Black;"
    "border-radius: 15px;"
    "background-color: qlineargradient( x1: 0, y1: 0, x2: 0, y2: 1,"
        "stop: 0 LemonChiffon, stop: 1 PaleGoldenrod );"
);

![](imgs/plotStyling6.png)

11. Exportieren und Drucken

Neben der Anzeige auf dem Bildschirm ist das Speichern schicker Diagramme und Verwendung dieser in Berichten eine nicht unwichtige Aufgabe. Allerdings ist es nicht trivial, gute Diagramme mit sinnvollen Schriftgrößen zu exportieren. Grundsätzlich muss hier zwischen Pixelgrafik-Export und Vektorgrafik unterschieden werden.

11.1. Exportieren des Plots als Pixelgrafik

Der naheliegenste Export des Plots ist eine 1-zu-1 Kopie in die Zwischenablage oder in eine Bitmapdatei (jpg, gif, png,…​).

11.1.1. Erstellen einer 1-zu-1 Kopie des Plotwidgets

Jedes QWidget kann direkt in eine QPixmap gezeichnet werden. Und dieses kann dann in eine Datei gespeichert werden.

// Plot in Pixmap rendern
QPixmap p = plot.grab();
// Pixmap in Datei speichern
p.save("diagramm_screenshot.png");

11.1.2. Kopie in die Zwischenablage

Statt das Pixmap in eine Datei zu speichern, kann man das auch einfach in die Zwischenablage kopieren. Dazu QClibBoard und QApplication einbinden und:

// Plot in Pixmap rendern
QPixmap p = plot.grab();
// Pixmap in Zwischenablage kopieren
qApp->clipboard()->setImage(p.toImage());

11.1.3. QwtPlot mit anderer Auflösung abspeichern

Wenn das QwtPlot mit einer anderen Auflösung/Pixelgröße als angezeigt auf dem Bildschirm abgespeichert werden soll, so verwendet man die QwtPlotRenderer:

// Render-Objekt erstellen
QwtPlotRenderer renderer;
// Statt der versenkten Box wird ein Rahmen mit Skalenstrichen gezeichnen
renderer.setLayoutFlag(QwtPlotRenderer::FrameWithScales);
// Zielgröße festlegen
QRect imageRect( 0.0, 0.0, 1200, 600 );
// Bildobjekt in der entsprechenden Größe erstellen...
QImage image( imageRect.size(), QImage::Format_ARGB32 );
// und mit weißem Hintergrund füllen
image.fill(Qt::white);

// Das Diagramm in das QImage zeichnen
QPainter painter( &image );
renderer.render( &plot, &painter, imageRect );
painter.end();

// QImage zurück in Pixmap konvertieren
QPixmap plotPixmap( QPixmap::fromImage(image) );
plotPixmap.save("diagram.png");

Das Diagramm aus dem Tutorial 1 (Kapitel 2) sieht dann z.B. so aus:

Tutorial1 h
Abbildung 14. Plot in höherer Auflösung abgespeichert

11.1.4. Diagrammelemente skalieren (DPI ändern)

TODO

11.2. Exportieren des Plots als Vektorgrafik

TODO

11.3. Drucken

TODO

12. Fortgeschrittene Themen

Die nachfolgend vorgestellten Themen greifen in die internen Datenstrukturen der Qwt-Bibliotheksklassen ein und diese könnten sich in zukünftigen Bibliotheksversionen sicher nocheinmal deutlich ändern. Daher sind diese Techniken mit Vorsicht zu genießen!

12.1. Objekte aus dem QwtPlot loslösen

Die API des QwtPlot geht davon aus, dass Objekt beim Hinzufügen/Ersetzen existierender Plotelemente das Plot als neuen Eigentümer erhalten. Sobald ein Plotelement ein vorheriges Plotelement ersetzt, löscht das QwtPlot das alte Objekt automatisch. Es gibt keine release-Funktionen, wie man die von shared pointer-Implementierungen kennt. Daher kann man einmal hinzugefügte Objekte nicht entfernen, anpassen und wieder neu hinzufügen.

Die empfohlene Methode ist:

  1. Plotelement neu erstellen

  2. anpassen

  3. das bisherige PlotElement ersetzen

Mitunter ist dies aber nicht praktikabel und man hätte gerne eine Methode, um ein existierendes Objekt loszulösen. Man kann das aber mit einem Trick dennoch machen.

TODO…​ erklären

13. Download/Installation/Erstellung der Qwt Bibliothek

13.1. Download fertiger Pakete

13.1.1. Linux

Unter Linux kann man auf die Pakete des Paketmanagers zurückgreifen.

Debian/Ubuntu
# Paket mit Headern für die Entwicklung
sudo apt install libqwt-qt5-dev

Headerdatei-Pfad: /usr/include/qwt

13.2. Erstellung aus dem Quelltext

13.2.1. Windows

  • Release qwt-6.3.0.zip herunterladen und entpacken.

  • Datei qwtconfig.pri bearbeiten und Optionen ein-/ausschalten

  • Kommandozeile mit Qt Umgebungsvariablen öffnen: Startmenu → Qt 5.15.2 (MinGW 8.1.0 64-bit)

  • Ins Verzeichnis mit der qwt.pro wechseln

13.2.2. Windows - MinGW32

Es wird eine MinGW32 Installation mit mingw32-make im PATH erwartet.

:: Makefile erstellen
qmake qwt.pro
:: Bibliothek und Plugin/Beispiele bauen
mingw32-make -j8
:: Biblithek installieren
mingw32-make install

Die -j8 sind für das parallele Bauen auf 8 CPUs.

Sofern nicht in der Datei qwtconfig.pri ein anderer Installationspräfix in der Variable QWT_INSTALL_PREFIX eingestellt wurde, ist die Bibliothek nun unter c:\Qwt-6.3.0 installiert:

c:\Qwt-6.3.0\include  - Header-Dateien
c:\Qwt-6.3.0\lib      - Bibliothek/DLLs
c:\Qwt-6.3.0\doc\html - API Dokumentation (`index.html` in diesem Verzeichnis öffnen)

13.2.3. Linux/Mac

13.3. Qt Designer Plugins

  1. wie erstellt man die Designerplugins und bekommt die in die Komponentenpalette…​

13.4. Verwendung des Plots in eigenen Programmen

13.4.1. Windows

13.4.2. Linux/Mac

13.5. Das QwtPlot in eine Designer-Oberfläche/ui-Datei integrieren

Wenn man mittels Qt Designer eine Programmoberfläche baut, möchte man da vielleicht auch ein QwtPlot einbetten. Das kann man auf zwei verschiedene Arten machen:

  1. ein QWidget als Platzhalter einfügen und zu einem Platzhalterwidget für das QwtPlot machen, oder

  2. die Qwt-Designer-Plugins verwenden.

13.5.1. Definition eines Platzhalterwidgets

Zur Erklärung wird im Qt Designer ein einfaches Widget entworfen:

Tutorial1 ui1
Abbildung 15. Widget mit Platzhalter-Widget für das Diagramm

Unter der Spinbox wurde ein QWidget eingefügt. Dieses soll nun als Platzhalter für das QwtPlot dienen. Dazu im Kontextmenü des Widgets die Option "Als Platzhalter für benutzerdefinierte Klasse festlegen…​" auswählen:

Tutorial1 ui2

Und im Dialog eine neue Platzhalterklasse wie folgt definieren:

Tutorial1 ui3

Die Eingabe mit "Hinzufügen" bestätigen und dann auf "Anwenden" klicken, um das Platzhalter-Widget in das QwtPlot zu wandeln. Wir benennen das noch in plot um, und füge das horizontale Layout und das Plotwidget in ein vertikales Layout ein:

Tutorial1 ui4

Damit sich das Plotwidget den ganzen vertikalen Platz schnappt, wählt man das Top-Level Widget aus und scrollt in der Eigenschaftsleiste bis nach unten zu den Einstellungen für das vertikale Layout. Dort gibt man bei den Stretch-Faktoren "0,1" ein, wodurch sich das 2. Widget im Layout (das Plot) komplett ausdehnt.

13.5.2. Verwendung der Designer-Plugins

Dazu muss man die QtDesigner-Plugins zunächst erstellen und integrieren.

TODO :

Wenn man die erstmal installiert hat, kann man ein QwtPlot direkt aus der Komponentenpalette in den Entwurf zeihen und ist fertig.

14. Über den Autor

  1. später, siehe https://schneggenport.de