Grafische Oberflächen mit QT

Aus LinuxUser 05/2006

Grafische Oberflächen mit QT

Schöne neue Welt

Grafische Oberflächen (GUIs) für KDE programmieren: Das klingt anstrengender, als es tatsächlich ist. Mit Qt entwickeln Sie auf einfache Weise einen Bildbetrachter – wir zeigen, wie es geht.

Windows-Umsteigern fällt der Schritt auf die Konsole häufig schwer, sie vermissen die gewohnten grafischen Oberflächen. Unter Linux verzichten indes viele interessante Anwendungen auf eine GUI. Dabei fällt es nicht schwer, ein grafisches Bedien-Interface zu entwickeln. Das hier besprochene Beispiel eines Bildbetrachters ist in Qt programmiert: Die Firma Trolltech entwickelte die Sprache in den 90er Jahren und stellte sie 2000 unter die GPL. Qt arbeitet plattformübergreifend und basiert im wesentlichen auf C++. Der Artikel setzt daher Grundkenntnisse von C++ und Objektorientierung voraus. Es gibt zahlreiche Einführungen im Netz [1] sowie empfehlenswerte Bücher, die auch blutigen Anfängern [2] die Zusammenhänge anschaulich erklären. Den Quellcode für das Beispiel finden Sie auf unserer Web-Seite [3], die GPL-Version von Qt in der Version 4.1.1 gibt es unter [4].

Qt installieren und einrichten

Vermutlich müssen Sie die aktuelle Version von Qt auf Ihrem Rechner installieren – wenn überhaupt, finden Sie dort die Version 3. Übersetzen Sie Qt-4.1.1 am besten aus dem Quellcode. Debianer haben Glück: Sie holen das aktuelle Qt über Synaptic oder Apt einfach aus dem Repository Unstable und passen nur die Pfade auf die unten beschriebene Weise an.

Sie kopieren den Quellcode in ein beliebiges Verzeichnis und entpacken ihn über tar xvfz qt-x11-opensource-src-4.1.1.tar.gz. Dann wechseln Sie in das neu entstandene Unterverzeichnis und geben ./configure ein. Das prüft nun, ob alle von Qt optional genutzten Programme vorhanden sind. Abhängig von Ihrer Distribution sollten Sie zusätzliche Pakete installieren (siehe Kasten “Abhängigkeiten”), um möglichst viele Funktionen von Qt-4.1.1. zu nutzen.

Abhängigkeiten

Konfigurieren Sie Qt, so verrät am Ende eine Liste, auf welche installierten Programme Qt zurückgreift – neben diesen steht dann ein yes (Abbildung 1). Lesen Sie hingegen neben Gif support ein no, installieren Sie die entsprechenden Header-Dateien nach. Unter Debian wäre das zum Beispiel die libungif4-dev. Über den Befehl make confclean respektive gmake confclean (Suse 10.0) machen Sie die Konfiguration rückgängig und prüfen die Abhängigkeiten erneut.

Abbildung 1: Wollen Sie Qt optimal nutzen, sollte die Entwicklungsumgebung möglichst viele Features unterstützen. Der Aufruf <code srcset=

./configure verrät, auf welche Bibliotheken Qt zugreift.” width=”292″ height=”300″ /> Abbildung 1: Wollen Sie Qt optimal nutzen, sollte die Entwicklungsumgebung möglichst viele Features unterstützen. Der Aufruf ./configure verrät, auf welche Bibliotheken Qt zugreift.

Anschließend geben Sie ein beherztes make bzw. gmake (Suse 10.0) ein, um die Quellen zu übersetzen. Dieser Prozess zieht sich hin – langsame Rechner brauchen durchaus Stunden. Anschließend installieren Sie Qt, indem Sie im Root-Modus gmake install (Suse 10.0) oder make install eingeben.

Gewöhnlich landet Qt unter /usr/local/Trolltech/QT-4.1.1. Diesen Pfad bringen Sie dem System nun bei. Auch das Programm qmake muss im Pfad liegen, um damit Qt-Projekte zu übersetzen. Dazu ergänzen Sie die Datei .bashrc im Home-Verzeichnis:

QTDIR=/usr/local/Trolltech/Qt-4.1.1:$QTDIR
export QTDIR
PATH=/usr/local/Trolltech/Qt-4.1.1/bin:$PATH
export PATH

Starten Sie anschließend die Bash neu, tippen Sie env ein und prüfen Sie, ob die Werte für die Variablen $PATH und $QTDIR stimmen.

Programme kompilieren

Das hier vorgestellte Beispielprogramm liegt in einer ungekürzten Fassung auf der Qt-Dokumentationsseite [5]. Im Original skaliert der Bildbetrachter die Bilder noch zusätzlich. In der kurzen Version öffnet er ein Hauptfenster mit einem einzigen Menü, das die Punkte Open und Quit enthält.

Über Open öffnen Sie eine Grafik. Erkennt das Programm das Dateiformat nicht, kommt eine entsprechende Meldung. Um das Programm auszuprobieren, kompilieren Sie es zunächst. Es besteht aus drei einzelnen Dateien, wobei die Header-Dateiimageviewer.h die Klassen, Methoden und Attribute enthält, während die Datei imageviewer.cpp die vom Hauptprogramm main.cpp benötigten Funktionen beinhaltet.

Geben Sie qmake -project ein, um aus dem Quellcode eine so genannte Projektdatei zu erzeugen. Der Schritt übersetzt die Teile des Quellcodes, die von Trolltech stammen, wie das Signal-Slot-Konzept. Anschließend geben Sie qmake ein, um ein Makefile zu generieren. Wie üblich kompiliert make das Programm. Die ausführbare Datei heißt so, wie der Ordner, in dem sie liegt, zum Beispiel bildbetrachter. Machen Sie das Programm über chmod u+x bildbetrachter ausführbar und rufen Sie es über ./bildbetrachter auf (Abbildung 2).

Abbildung 2: Der Bildbetrachter in Aktion: Die spartanische Oberfläche bietet Ihnen nur zwei Menüpunkte an. Mit einem öffnen Sie Bilder, mit dem anderen beenden Sie das Programm.

Abbildung 2: Der Bildbetrachter in Aktion: Die spartanische Oberfläche bietet Ihnen nur zwei Menüpunkte an. Mit einem öffnen Sie Bilder, mit dem anderen beenden Sie das Programm.

Alles Routine

Die Funktion main() in der Datei main.cpp (Listing 1) ist das eigentliche Hauptprogramm. Es holt alle anderen Funktionen, die es braucht, aus der Datei imageviewer.cpp und entnimmt der Datei imageviewer.h die Informationen über Klassen, Attribute und Methoden.

Listing 1

    #include <QApplication>
    #include "imageviewer.h"
    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv);
        ImageViewer imageViewer;
        imageViewer.show();
        return app.exec();
    }

Zunächst holen Sie über #include Daten aus der Header-Datei der Klasse QApplication und aus imageviewer.h. Jede auf Qt basierende GUI-Anwendung braucht eine Instanz der Klasse QApplication. Über die Parameter int argc, char *argv[] übergeben Sie zusätzlich angegebene Kommandozeilen-Parameter an die Instanz, die den Namen app trägt.

Nachfolgend erzeugen Sie mit Imageviewer imageViewer eine Instanz der gleichnamigen Klasse und machen den Bildbetrachter über die Methode show() (Zeile 8) für den Betrachter sichtbar. Beenden Sie das Programm, informiert der Rückgabewert app.exec() die Funktion main() darüber.

Der Kopf des Ganzen

Betrachten Sie nun die Header-Datei imageviewer.h (Listing 2). Sie folgt im wesentlichen den Gepflogenheiten von C++ und wird hier nur knapp behandelt.

Listing 2

    #ifndef IMAGEVIEWER_H
    #define IMAGEVIEWER_H
    #include <QMainWindow>
    class QAction;
    class QLabel;
    class QMenu;
    class QScrollArea;
    class QScrollBar;
    class ImageViewer : public QMainWindow
    {
        Q_OBJECT
    public:
        ImageViewer();
    private slots:
        void open();
    private:
        void createActions();
        void createMenus();
        void adjustScrollBar(QScrollBar *scrollBar);
        QLabel *imageLabel;
        QScrollArea *scrollArea;
        QAction *openAct;
        QAction *exitAct;
        QMenu *fileMenu;
    };
    #endif

Den Quelltext rahmen die Makros #ifndef, #define und #endif (Zeilen 1, 2, 36) ein, die dafür sorgen, dass der Kompiler Header-Dateien nicht doppelt einbindet. Die Präprozessor-Direktive in Zeile 4 holt die Header der Qt-Standardklasse QMainWindow, die das Hauptfenster für den Bildbetrachter bereitstellt. Die Klasse ImageViewer erbt die Eigenschaften dieser Elternklasse und benötigt daher deren Header.

Nun folgen in einer so genannten “forward declaration” (Zeilen 6 bis 10) gleich mehrere Klassen. Diese Deklarationen kürzen das Kompilieren ab, da der Kompiler nicht die kompletten Header-Daten verarbeiten muss. Das braucht er nicht, weil Sie die Instanzen der Klassen später als Zeiger und nicht als Werte deklarieren.

Nun widmen Sie sich der Klasse ImageViewer (Zeilen 12 bis 34). Sie leitet sich von QMainWindow ab. Das Makro Q_OBJECT muss überall dort stehen, wo auch das Signal-Slot-Konzept zum Einsatz kommt (dazu im Abschnitt “Signalfeuer” mehr). Welche öffentlichen und privaten Methoden die Klasse ImageViewer besitzt, bestimmen die Zeilen 16 bis 33.

Der Abschnitt private slots ist Qt-spezifisch, er verwandelt die Methode open(), die im Signal-Slot-Konzept vorkommt (Zeile 19, 20), in eine Privatveranstaltung. Die ersten drei unter private aufgeführten Methoden besitzen keinen Rückgabewert und sorgen später für die Menüs, deren Funktionen und die Scroll-Leisten. In den Zeilen 27 bis 33 tauchen dann die schon erwähnten Zeiger auf.

Signalfeuer

Ein wichtiger Unterschied zwischen Qt und C++ besteht im Signal-Slot-Konzept. Das ist eine Trolltech-Erfindung und kommt ins Spiel, soll sich – wie im Beispiel – bei einem Klick auf Open der Dateidialog öffnen. Eine Funktion sendet ein Signal aus, eine andere reagiert darauf.

Um das Konzept zu realisieren, nutzt Qt den so genannten “Meta Object Compiler” (moc). Er erzeugt zusätzlichen Quellcode, den der Kompiler anschließend mit der ausführbaren Datei verlinkt. Der schon erwähnte Befehl qmake erzeugt also ein Makefile, das auch den Meta Object Compiler berücksichtigt. Das Makro Q_OBJECT verweist auf den speziellen Code – QObject dient allen anderen Qt-Objekten als Basisklasse. Die Methode connect() verbindet Sender und Empfänger des Signals:

QObject::connect(&quit, SIGNAL(clicked()), &app, SLOT(quit()));

Das Signal-Slot-Konzept ist recht einfach: Die Variable &quit steht in diesem Beispiel für einen Button. Klicken Sie ihn an (clicked()), beendet sich (quit()) die Applikation &app. Ein Objekt kann Signale an mehrere Slots zugleich schicken. Umgekehrt empfangen Slots auch Signale von mehreren Objekten. Sie lauschen dabei permanent auf Signale, die Anzahl gesendeter Argumente ist beliebig.

Volle Funktion

Die Deklarationen in der Header-Datei verraten noch nicht allzu viel über die Arbeitsweise des Programms, Sie initialisieren lediglich Klassen und Methoden. Erst die Datei imageviewer.cpp (Listing 3) haucht den Methoden Leben ein.

Listing 3

#include <QtGui>
    #include "imageviewer.h"
    ImageViewer::ImageViewer()
    {
        imageLabel = new QLabel;
        imageLabel->setBackgroundRole(QPalette::Base);
        scrollArea = new QScrollArea;
        scrollArea->setBackgroundRole(QPalette::Dark);
        scrollArea->setWidget(imageLabel);
        setCentralWidget(scrollArea);
        createActions();
        createMenus();
        setWindowTitle(tr("Image Viewer"));
        resize(500, 400);
    }
    void ImageViewer::open()
    {
        QString fileName = QFileDialog::getOpenFileName(this,tr("Open File"), QDir::currentPath());
        if (!fileName.isEmpty()) {
            QImage image(fileName);
            if (image.isNull()) {
                QMessageBox::information(this, tr("Image Viewer"),tr("Cannot load %1.").arg(fileName));
                return;
            }
            imageLabel->setPixmap(QPixmap::fromImage(image));
            imageLabel->adjustSize();
            }
    }
    void ImageViewer::createActions()
    {
        openAct = new QAction(tr("&Open…"), this);
        openAct->setShortcut(tr("Ctrl+O"));
        connect(openAct, SIGNAL(triggered()), this, SLOT(open()));
        exitAct = new QAction(tr("E&xit"), this);
        exitAct->setShortcut(tr("Ctrl+Q"));
        connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
    }
    void ImageViewer::createMenus()
    {
        fileMenu = new QMenu(tr("&File"), this);
        fileMenu->addAction(openAct);
        fileMenu->addSeparator();
        fileMenu->addAction(exitAct);
        menuBar()->addMenu(fileMenu);
    }

Die #include-Makros binden einerseits sämtliche QtGui-Klassen ein, andererseits die Header-Datei imageviewer.h. Zunächst legen Sie fest, was ImageViewer() tut. In Zeile 7 deklarieren Sie eine neue Instanz der Klasse QLabel mit dem Namen imageLabel, setBackgroundRole() holt sich in Zeile 8 von der Klasse QPalette eine Hintergrundfarbe.

Die anschließenden vier Zeilen 10 bis 13 definieren einen Scroll-Bereich, der seine Farbe ebenfalls über setBackgroundRole() bezieht. Der Aufruf von setWidget() macht die Instanz imageLabel zum Kindobjekt von scrollArea, dadurch übernehmen die Scroll-Leisten das Bild. Die letzte Zeile dieses Blocks erklärt den Scroll-Bereich zum zentralen Bereich.

Über createActions() und createMenus() binden Sie die Menüs und Aktionen ein, die Sie weiter unten spezifizieren (Zeilen 15, 16). Das Hauptfenster schmückt der Titel Image Viewer (Zeile 18), es öffnet sich anfangs in einer Größe von 500 mal 400 Pixel (Zeile 19).

Nun folgt in den Zeilen 22 bis 34 die Funktion open(). Zunächst verschafft Ihnen die Klasse QFileDialog die Möglichkeit, eine Grafikdatei über eine grafische Oberfläche zu suchen und auszuwählen. In Zeile 24 öffnet getOpenFileName() ein Dialogfenster mit dem momentanen Verzeichnis (Abbildung 3). Sie wählen ein Bild aus, dessen Name zur Instanz fileName der Klasse QString wird.

Die If-Abfrage (Zeilen 25/26) prüft, ob zum Dateinamen auch ein Bild gehört und erzeugt im positiven Fall eine Instanz der Klasse QImage mit dem Bild image. Die Instanz erhält also die Datei, die zu fileName gehört. Die nächste If-Schleife untersucht, ob sich die Datei lesen lässt; andernfalls poppt eine Nachricht auf, dass der Bildbetrachter das Bild nicht laden kann.

Abbildung 3: Neue Bilder öffnen Sie bequem über ein grafisches Menü. Die Klasse "QFileDialog" stellt die Methoden zur Verfügung, die Sie brauchen, um das Menü zu bauen.

Abbildung 3: Neue Bilder öffnen Sie bequem über ein grafisches Menü. Die Klasse “QFileDialog” stellt die Methoden zur Verfügung, die Sie brauchen, um das Menü zu bauen.

Passt alles, malen die Zeile 31 und 32 das Bild image auf die Fläche imageLabel und ändert anschließend die Größe von imageLabel.

Um ein neues Bild zu betrachten, klicken Sie auf den Menüpunkt Open, das löst eine Aktion aus. Diese Aktionen deklarieren Sie in der Methode createActions() (ab Zeile 36), die dem Attribut openAct Speicherplatz reserviert (Zeile 38) und der Aktion ein Shortcut zuweist (Zeile 39). Dann folgt in Zeile 40 die Signal-Slot-Verbindung: Aktivieren Sie das Attribut openAct, so sendet es ein Signal an open(), das ein Menü wie in Abbildung 3 öffnet. Drücken Sie hingegen auf den Exit-Knopf (Zeile 42 bis 44), beenden Sie die komplette Anwendung.

Wie aber aktivieren Sie openAct und exitAct? Per Mausklick, indem Sie die Aktionen dem Menü zuordnen (ab Zeile 47). Die Methode createMenus() erzeugt das Menü. Sie öffnet zunächst eine Instanz der Klasse QMenu mit dem Attribut fileMenu. Über addAction() ordnen Sie dem Menü in den Zeilen 50 bis 52 die Attribute openAct und exitAct zu, dazwischen fügen Sie mit addSeparator() einen Trennstrich ein. Sie belegen also das leere Menü mit den in createActions() deklarierten Aktionen.

Abschließend platzieren Sie das Menü fileMenu an passender Stelle in der Menüleiste (Zeile 54), kompilieren das Programm und starten es mit ./bildbetrachter. Das war’s: Nun begutachten Sie Ihre Bilder in einem selbst programmierten Bildbetrachter. Den ergänzen Sie nun wahlweise um weitere Features: Zahlreiche Anregungen dazu erhalten Sie auf der Qt-Web-Seite [5].

Glossar

Header-Datei

Diese Dateien enden meist mit *.h, in ihnen deklarieren bzw. definieren Sie die Klassen, Variablen und Funktionen für die Anwendung.

Klassen

Begriff aus der objektorientierten Programmierung. Eine Klasse versammelt verschiedene Funktionen und Variablen unter einem Dach.

Methoden

So nennt man die Funktionen innerhalb einer Klasse.

Attribute

Der Begriff dient als ein Synonym für Eigenschaften, die Teil einer Klasse sind.

Instanz

Eine Klasse manifestiert sich konkret erst in einer Instanz beziehungsweise in einem Objekt. Die Klasse dient also umgekehrt als abstrakte Schablone für Objekte.

Infos

[1] C++-Einführung im Netz: http://www.highscore.de/cpp/einfuehrung

[2] Guter C++-Einstieg für Ahnungslose: Alexander Niemann, Stefan Heitsiek: Das Einsteigerseminar C++, Bonn 2005.

[3] Das Beispiel auf unserer Web-Seite: http://www.linux-user.de/Downloads/2006/05/qt

[4] Quellcode von Qt-4.1.1: http://wftp.tu-chemnitz.de/pub/Qt/qt/source/qt-x11-opensource-src-4.1.1.tar.gz

[5] Beispielanwendung im Original: http://doc.trolltech.com/4.1/widgets-imageviewer.html

LinuxUser 05/2006 KAUFEN
EINZELNE AUSGABE
ABONNEMENTS
TABLET & SMARTPHONE APPS
E-Mail Benachrichtigung
Benachrichtige mich zu:

Hinweis: Dieser Artikel ist älter als ein Jahr, enthaltene Informationen sind möglicherweise veraltet.

0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben