Vorfreude, schönste Freude
Adventskalender mit C++ und Qt
Das große Ganze
24 dieser Tuerchen-Objekte wollen nun erschaffen und angeordnet werden – am besten in einem Objekt einer neuen Klasse Adventskalender, die das Hauptfenster stellt. Auch diese einfache Klasse, die lediglich einen Konstruktor besitzt, leiten wir von QWidget ab und schreiben ihr API in eine Datei namens adventskalender.h (Listing 1).
Listing 1
Adventskalender-API
adventskalender.h#include <qwidget.h>
class Adventskalender : public QWidget
{
public:
Adventskalender( QWidget *elternwidget = 0,
const char *name = "adventskalender" );
};
adventskalender.cpp besteht aus gar nicht besonders viel Programmierung, sondern vor allem aus Festlegungen: Welche Bildchen sollen sich hinter den einzelnen Türchen verstecken, …
#include "1.xpm" // enthaelt bild_1[…] #include "24.xpm" // bild_24
für welchen Tag ist welcher Türcheninhalt gedacht, ….
static const char bilder[24] = {
bild_1, […], bild_24 };… und an welchen x-y-Koordinaten soll das für einen bestimmten Tag gedachte Türchen stehen? (Die erste Spalte der ersten Zeile trägt die Koordinate 0,0.)
struct pos{
int x;
int y;
} position[24] = {
{1, 0}, // Position fuer Tuerchen Nr. 1[…]
{2, 2} // Nr. 24
};Im Adventskalender-Konstruktor legen wir zunächst ein unsichtbares sechsspaltiges und vierzeiliges Gitter fest, in dem die einzelnen Türchen "einrasten" sollen:
Adventskalender::Adventskalender( QWidget *elternwidget, const char *name )
: QWidget( elternwidget, name )
{
QGridLayout * gitterraster = new QGridLayout( this, 6, 4,
6, 4, "gitterraster" );Bei den letzten drei Argumenten des QGridLayout-Konstruktors handelt es sich um die Randbreite (6), den Platz zwischen den Zellen (4 Pixel) und die Widget-ID des gitterraster-Zeigers. 24 Türchen erzeugen wir nun in einer for-Schleife, deren Körper innerhalb der geschweiften Klammern erst mit der Zählervariablen i gleich 1 bis hin zur 24 durchlaufen wird (i++ bedeutet, dass der Inhalt von i am Ende jeden Schleifendurchlaufs um eins erhöht wird):
for ( int i = 1; i <= 24; i++ ){
QString name( "tuerchen" );
name += QString::number(i);Damit jedes Tuerchen-Objekt eine eigene ID bekommt, setzen wir sie in der Variablen name zusammen: Beim Türchen, das im ersten Schleifendurchlauf entsteht, lautet sie tuerchen1, beim letzten Durchlauf lautet name auf tuerchen24. name += Wert ist dabei eine Kurzform für name = name + Wert.
Nachdem wir die Tuerchen-API am Anfang der Datei mit #include "tuerchen.h" innerhalb der Adventskalender-Implementierung bekannt gemacht haben, können wir den public deklarierten Tuerchen-Konstruktor nun mit den Argumenten Eltern-Widget, Hintergrundbildchen, Aufschrift und ID aufrufen:
Tuerchen * tuer = new Tuerchen( this, new QPixmap( bilder[i-1] ), i, (const char *) name );
Mit bilder[i-1] holen wir uns dabei das an i-ter Stelle abgelegte XPM aus dem bilder-Array – da die Zählung bei Array-Elementen jedoch bei 0 beginnt, müssen wir als Index i-1 angeben. Indem wir es dem QPixmap-Konstruktor als Argument übergeben, erhalten wir ein passendes Pixmap-Objekt.
Zuletzt bauen wir das neue Tuerchen an passender Stelle ins Layout-Raster ein:
gitterraster->addWidget( tuer, position[i-1].x,
position[i-1].y );
}Als Positionsangabe dient jeweils der x- und der y-Wert aus dem position-Element mit dem Index i-1. Tuerchen Nr. 24 wird damit an der Stelle (2,2) im Raster eingebaut.
Das gesamte Adventskalender-Widget färben wir jetzt (wie den Tuerchen-Pushbutton) grün ein und teilen dem Qt-Layout-Management außerdem mit, dass es eine feste, unveränderliche Größe haben soll. Diese berechnen wir jedoch nicht, sondern sagen mit sizeHint() einfach: "Nimm die Größe, bei der alle Teil-Widgets ideal platziert sind."
setBackgroundColor( QColor( 0, 155, 0 ) ); setFixedSize( sizeHint() ); }
Finale
Alle Widgets sind da, fehlt nur noch das Applikationsobjekt selbst. In der (allen C/C++-Programmen eigenen) Hauptfunktion main() (Listing 2) übergeben wir eventuelle Kommandozeilen-Optionen und Argumente (in unserem Fall prophylaktisch) an die eigentliche Applikation (argv enthält diese, argc ihre Anzahl). Das neue Adventskalender-Objekt wird in Zeile 8 zum Hauptfenster der Applikation, auf die app zeigt. Zeile 9 sorgt dafür, dass es auch auf dem Bildschirm erscheint, während Zeile 10 die sogenannte Anwendungsschleife startet: Das Adventskalender-Objekt beginnt zu leben. Sobald es geschlossen wird, bekommen wir von exec() einen Rückgabewert, und das Programm beendet sich.
Listing 2
Hauptroutine
advcal.cpp1 #include <qapplication.h>
2 #include "adventskalender.h"
3
4 int main( int argc, char argv )
5 {
6 QApplication * app = new QApplication( argc, argv );
7 Adventskalender * advcal = new Adventskalender();
8 app->setMainWidget( advcal );
9 advcal->show();
10 return app->exec();
11 }
Der Code ist geschrieben (und auf CD komplett nachzulesen) – wie wird daraus jetzt ein ausführbares Programm? Hiermit schlägt tmakes (oder qmakes) große Stunde: Aus einer einfachen Projektdatei, die alle selbstgeschriebenen Header-Dateien unter HEADERS, alle Implementierungsfiles unter SOURCES und den Namen der zu generierenden ausführbaren Datei unter TARGET angibt (Listing 3), wird mit
tmake -o Makefile advcal.pro
ein Makefile. Die CONFIG-Angaben release, qt und warn_on bedeuten, dass wir eine optimalisierte Qt-Applikation erstellen wollen, bei deren Übersetzung der Compiler mehr Warnungen ausgeben soll als üblich. Dass es sich beim Ergebnis um eine Anwendung (und nicht um eine Bibliothek) handelt, legt die TEMPLATE-Variable fest.
Listing 3
Projektdatei
advcal.proTEMPLATE = app
CONFIG = qt warn_on release
HEADERS = adventskalender.h \
tuerchen.h
SOURCES = advcal.cpp \
adventskalender.cpp \
tuerchen.cpp
TARGET = advcal
Ein make sorgt dann hoffentlich für fehlerfreies Durchkompilieren – und ein ausführbares Binary namens advcal.
Glossar
GUI
"Graphical User Interface" – grafische Benutzerschnittstelle, der für Anwender/innen sichtbare Teil einer grafischen Applikation.
Compiler
Programm, das aus menschenlesbarem Quellcode ein maschinenlesbares ausführbares Binary macht.
Interpreter
Programm, das in einer Skript-Sprache geschriebenen Quellcode interpretiert und sofort ausführt.
Xlib
Die grundlegende Bibliothek, die alle zur Entwicklung einer X-Applikation nötigen (Low-Level-)Funktionen enthält.
basename
Dieses Kommando schneidet von einem Dateinamen, den es als erstes Argument bekommt, die Pfadangabe und ggf. die als zweites Argument angegebene Dateinamensendung ab. Aus tuer.gif in der Variablen bild wird so tuer., und da das basename-Kommando in `Backticks` eingeschlossen wurde, fügt die Shell dessen Ergebnis an deren Stelle ein. So wird letztlich folgendes Kommando ausgeführt:
static
Globale (also außerhalb einer Klasse oder Funktion deklarierte) statische Variablen sind außerhalb ihrer Datei (bzw. der, in die sie mit #include eingelesen wurden) unbekannt.
//
C++-Kommentarzeichen. Alles, was hinter ihm auf der Zeile steht, ignoriert der Compiler.
aufgedrückt
Operator
->, der die darauffolgende Funktion auf das Objekt anwendet, auf das ein Zeiger zeigt. Arbeitet man hingegen mit Objekten, die direkt in Variablen gespeichert sind, so heißt der entsprechende Operator . (Punkt).
Infos
[1] Xlib-Crashkurs (englisch): http://tronche.com/gui/x/xlib-tutorial/
[2] GTK+: http://www.gtk.org/
[3] Qt: http://www.trolltech.com/products/qt/qt.html
[4] Tcl/Tk: http://www.scriptics.com/
[5] Glade: http://glade.pn.org/
[6] tmake: ftp://ftp.troll.no/freebies/tmake/
[7] qmake-Dokumentation: http://doc.trolltech.com/3.0/qmake-manual.html
[8] xv: http://www.trilon.com/xv/xv.html
[9] ImageMagick: http://www.imagemagick.org/



