Vorfreude, schönste Freude
Adventskalender mit C++ und Qt
Bekanntmachung
Damit andere Objekte, etwa der Adventskalender selbst, wissen, wie sie Tuerchen erzeugen (und was sie sonst noch damit anstellen können), müssen sie über die Schnittstellen (API, "Application Programmers' Interface") der Tuerchen-Klasse aufgeklärt werden. Diese schreibt man in eine Header-Datei, die sich mit der #include-Direktive leicht dort einlesen lässt, wo sie gebraucht wird. Darin steht, …
#include <qwidget.h>
class Tuerchen : public QWidget{…, dass es sich bei Tuerchen um eine von QWidget abgeleitete Klasse handelt, die
public: Tuerchen( QWidget*, QPixmap*, const int, const char * );
zur allgemeinen (also öffentlichen, public) Verwendung einen Konstruktor anbietet, der vier Argumente der angegebenen Datentypen verlangt. Nicht für andere Objekte zugänglich sind hingegen die "Ablagen", in denen Tuerchen-Objekte ihre eigenen Daten aufbewahren:
private: int aufschrift; // Tag QLabel* bild; // Inhalt des Tuerchens QPushButton* tuer; // die "Deckplatte" };
eine Integer-Variable, die den Tag enthält, für den das Türchen gedacht ist, sowie Zeiger auf das Label mit dem "geheimen" Bildchen und die am entsprechenden Tag "abzunehmende" Deckplatte. Um diese darzustellen, bietet sich ein entsprechend formatierter Knopf an, auf den die neugierigen User klicken sollen – also ein Pushbutton. Die gesamte, mit dem Stichwort class beginnende Klassen-Deklaration ist eine einzige C++-Anweisung, weshalb am Ende das Semikolon nicht fehlen darf.
Doch bislang kennt noch niemand die Klassen QLabel und QPushButton. #include-Anweisungen müssten her. Da Zeiger jedoch nur auf Speicherstellen weisen, die ein entsprechendes Objekt enthalten, brauchen wir an dieser Stelle noch nicht den Ballast der gesamten API für diese Klassen mitschleppen. Mit
class QLabel; class QPushButton;
behaupten wir einfach nur vor der Tuerchen-Deklaration, dass diese Klassen bekannt sind. Spätestens, wenn die Klassenvariablen mit Anfangswerten belegt, initialisiert, werden (eine Aufgabe, die dem Konstruktor zufällt) und damit tatsächlich ein Objekt dieser Klasse gespeichert wird, brauchen wir die jeweilige Klassen-API jedoch. Deshalb gehört ein
#include <qlabel.h> #include <qpushbutton.h>
an den Anfang der Implementationsdatei tuerchen.cpp. Die API der meisten Qt-Klassen ist in einem Headerfile gleichen Namens (jedoch kleingeschrieben) verstaut, bei den Ausnahmen findet man den passenden Dateinamen am Anfang der jeweiligen Klassendokumentation.
Den Namen der Header-Datei der Tuerchen-Klasse haben wir mit tuerchen.h selbst festgelegt – doch da es sich um eine eigene API handelt, die im selben Verzeichnis wie die cpp-Datei liegt, wird sie beim Einfügen nicht in spitze Klammern, sondern in doppelte Anführungszeichen gesetzt:
#include "tuerchen.h"
Geburtshelfer
Innerhalb der geschweiften Klammern des Tuerchen-Konstruktors belegen wir jetzt die Klassenvariable aufschrift mit dem ganzzahligen Wert aus dem Parameter tag:
aufschrift = tag;
Damit bild, der Zeiger auf das Label mit dem Hintergrundbildchen, tatsächlich auf etwas zeigt, rufen wir mit dem Schlüsselwort new ein neues QLabel-Objekt ins Leben. Dessen Konstruktor ist zufrieden, wenn man ihm das Eltern-Widget als Argument übergibt. Da es ein Kind des jeweiligen Tuerchen-Objekts sein soll, reicht hier das Keyword this:
bild = new QLabel( this );
Das neue, noch leere Label-Objekt bekommt mit der QLabel-Funktion setPixmap() die im Konstruktor-Argument bildchen enthaltene xpm-Grafik aufgedrückt, …
bild->setPixmap( *bildchen ); bild->setAlignment( Qt::AlignCenter );
…, die wir eine Zeile später zentrieren. Da AlignCenter nichts ist, was QWidget und damit Tuerchen geerbt haben, müssen wir davor den doppelten Doppelpunkt setzen, vor dem wiederum steht, in welcher Klasse (genauer in welchem Namensraum) es definiert ist – in diesem Fall ganz global im Namensraum Qt.
Nicht alle XPMs sind genau quadratisch, weshalb um manche ein Rand in der Standardfarbe des Labels, grau, verbliebe. Das sieht nicht schön aus, und so färben wir das komplette Label mit der Hintergrundfarbe der Pixmaps ein. Bei unseren Beispielbildchen ist das weiß, das netterweise als Qt::white vordefiniert ist.
bild->setPalette( Qt::white );
Genaugenommen sorgen wir mit setPalette() dafür, dass das Label in einer Farbenpalette auf der Basis von Weiß eingefärbt wird. Richtig bemerkbar macht sich dieses Detail bei 3D-Effekten weniger "flacher" Widgets wie dem QPushButton.
Wer hat nicht schon bei Adventskalendern aus Papier versucht, vorzeitig hinter das Geheimnis zu kommen? Selbst wenn sich die Türchen nicht vorzeitig öffnen lassen, kann ein verräterisches Flimmern beim Aufruf (und grafischen Aufbau) des Programms mehr als gewünscht verraten. Daher verstecken wir das bild-Objekt vorsichtshalber mit bild->hide();.
Das Pushbutton-Türchen verlangt ein wenig Vorarbeit. Zwar lesen wir mit #include "tuer.xpm" die in der Variablen bild_tuer abgelegte XPM-Beschreibung des Frontbildchens ein, doch darin ist noch keine Tageszahl integriert. Mit
QPixmap tuerfuellung( bild_tuer );
machen wir aus der Beschreibung erst einmal ein richtiges Pixmap-Objekt (keinen Zeiger!) namens tuerfuellung, das wir mit
QPainter leinwand( &tuerfuellung );
auf eine virtuelle Leinwand kleben. Jetzt holen wir einen weißen Stift für diese leinwand
leinwand.setPen( Qt::white );
und schreiben in der linken, oberen Ecke (Qt::AlignTop) eines auf dem QPainter links oben (bei den Koordinaten 0,0) beginnenden Rechtecks von 64 Pixeln Breite und eben dieser Höhe den Text, der in der Variablen aufschrift gespeichert ist:
leinwand.drawText( 0, 0, 64, 64, Qt::AlignTop, QString::number( aufschrift ) );
aufschrift ist aber eine ganzzahlige Zahl und kein Text. Den machen wir aus ihr mit Hilfe der QString-Funktion number(). Wir räumen die Staffelei weg (leinwand.end();) und erzeugen den Button mit der veränderten Pixmap, auf den unser Zeiger tuer weisen soll:
tuer = new QPushButton( this ); tuer->setPixmap( tuerfuellung );
Auch das Türfüllungsbild misst nicht ganz 64x64 Pixel, weshalb es sich anbietet, die Farbpalette des hinter tuer versteckten Objekts entsprechend anzupassen. Mit Tools wie xv oder display erfährt man den RGB-Wert der entsprechenden Hintergrundfarbe, bei tuer.xpm auf der CD ist es ein Rotanteil von 0, ein Grünanteil von 155 und ein Blauanteil von 0. Da setPalette() auch QColor-Objekte als Argument entgegennimmt, machen wir aus dem RGB-Wert zunächst ein solches:
tuer->setPalette( QColor( 0, 155, 0 ) );
Damit das komplette Tuerchen-Objekt nicht über die Größe der darin enthaltenen Bildchen hinauswächst, legen wir noch eine feste Größe fest:
setFixedSize( 64, 64 );



