Erstes KDE3-Programm

Graphische Programmierung unter unter Linux

Die Sprachen C/C++ verfügen von Haus aus über keine Grafikbefehle. Die Aufgabe übernehmen spezielle Grafikbibliotheken. Die bekannstesten Vertreter unter Linux sind Qt/KDE und GTK/GTK+. Wir beschäftigen uns mit QT/KDE-Bibliotheken, die bei KDE-Programmen eingesetzt werden.

Das erste Grafik-Programm

Das Ziel war es, ein möglichst kleines graphisches Programm zu schreiben, um nicht gleich abgeschreckt zu werden. Das Programm macht nicht viel. Es öffnet ein Fenster, füllt dieses mit der Farbe beige und malt eine dunkelgrüne Linie von links oben nach rechts unten im Fenster.

Installieren Sie dazu alle kde3-devel und qt3-devel-Bibliotheken bei OpenSuSE 10.3 (KDE 3.5.7/Qt 3.3.8)

Zusätzlich sollte man die HTML-Hilfe von qt3 lesen (Paket qt3-devel-doc). Hier gibt es 2 große Tutorials in Englisch zum Programmmiereinstieg.

Momentan beginnt der komplette Umstieg von KDE3 auf KDE4, dass im Dezember 2007 erscheinen soll. Hier kommt die neue Qt4-Bibliothek zum Einsatz. Sie erfordert eigene Anpassungen alter qt3-Programme. KDE4 verwendet auf nicht mehr das automake-Buildsystem, sondern setzt auf cmake.


Grober Programmaufbau

Ein simples KDE3-Programm besteht aus 3 Dateien.

1. Hauptprogramm/Unterprogramme (z.B. prg1.cpp)

2. Headerdatei mit Hauptfenster-Klasse (z.B. prg1.h)

3. Makefile, der die Übersetzungsregeln enthält (makefile)


Grundsätzliches zur Funktionsweise von Qt/KDE

QT ist eine sehr moderne objektorientierte Grafik- und Ein-/Ausgabebibliothek. Ihr Umfang ist in der Tat riesig. Es reicht von einfachen Grafikoperationen bis zu komplexen Datenbankschnittstellen. KDE selbst ist eine Erweiterung der Qt-Bibliotheken und gleichzeitig neben Gnome, die modernste Linux-Fensteroberfläche.

Aufgrund der objektorientierten Struktur von Qt, können relativ einfach komplexe graphische Objekte erschaffen werden. Vor Qt wurden Unix-Oberflächen mit Motif erstellt, was sehr komplex war. Sie werden sehen, das Qt eine sehr elegante und schöne Programmierung aufweist. Dieses Demoprogramm soll Ihnen helfen den Weg zur graphischen Programmierung leichter zu lernen.

Wenn ich die Zeit finde, werde ich dieses Programm um Menüs und Schalter erweitern. Dann kommt das moderne SLOT und SIGNAL-Konzept erst zum Tragen. Ein Qt-Programm geht nach seinem Start in eine Art Warteschleife. Dort horcht es auf Signale vom Benutzer oder Programmteilen. Solche Signale sind z.B Beispiel Tastendrücke, Mausbewegungen, Befehle zum Erneuern des Grafikbildes uvm. Solche Signale rufen dann Unterprogramme auf, die gewünschte Aktionen ausführen.


Programm prg1.cpp

// prg1.cpp

#include "prg1.moc"

KApplication* app;
BWindow* topW;

BWindow::BWindow()
{
  setGeometry(100, 40, 800, 500);
  setMinimumSize(800, 500);
  setMaximumSize(800, 500);
  setBackgroundMode(NoBackground);
  setCaption("prg1");
}

void BWindow::paintEvent(QPaintEvent*)
{
  QPainter p;
  p.begin(&buffer);
  p.setBrush(QColor(250, 250, 200));
  p.drawRect(0, 0, 800, 500);
  p.setPen(QColor(0, 79, 0));
  p.drawLine(0, 0, 800, 500);
  p.end();
  bitBlt(this, 0, 0, &buffer);
}

void BWindow::resizeEvent(QResizeEvent* event)
{
  buffer.resize(event->size());
}

void BWindow::keyPressEvent(QKeyEvent* e) { }

void BWindow::mousePressEvent(QMouseEvent* e) { }

int main(int argc, char** argv)
{
  app = new KApplication(argc, argv, "prg1");
  topW = new BWindow();
  app->setMainWidget(topW);
  topW->show();
  return(app->exec());
}


prg1.cpp - Besprechung

Das Programm ist in der Tat ziemlich klein und beginnt wie immer am Ende mit dem Hauptprogramm main.

app = new KApplication(argc, argv, "prg1");

Hier wird eine neue KDE-Applikation erzeugt und in der Zeigervariable app gespeichert.


topW = new BWindow();

Nun wird eines neues KDE-Hauptfenster erzeugt und in der Zeigervariable topW gespeichert. Die Klasse BWindow, die von KMainWindow abgeleitet ist, wird in prg1.h genauer besprochen. Jedenfalls wird hier der Konstruktor BWindow::BWindow() durch new aufgerufen, um die Fenstereigenschaften zu setzen.

Schauen wird mal kurz zum Konstruktor BWindow::BWindow(), ganz oben im Programm.

Ein Konstruktor erschafft ein Objekt, in unserem Falle einen Zeiger auf ein Hauptfenster von Typ BWindow, namens topW.

setGeometry(100, 40, 800, 500);

Fenster an X-Position=100, Y-Position=40 mit Breite 800 Pixel und Höhe 500 Pixel setzen.

setMinimumSize(800, 500);
setMaximumSize(800, 500);

Fenstergröße wird festgesetzt, es kann nicht verkleinert und vergrößert werden.

setBackgroundMode(NoBackground);

Keine Hintergrundfarbe setzen, wir füllen das Fenster später selber.

setCaption("prg1");

Der Fenstertitel soll prg1 lauten.

Nachdem der Konstruktor BWindow::BWindow() ausgeführt wurde, kehrt das zum nächsten Befehl im Hauptprogramm zurück.


app->setMainWidget(topW);

Nun wird topW zum Hauptfenster der KDE-Applikation app erklärt.

topW->show();

Bisher lief alles im Speicher ab, jetzt wird das neue Fenster auch auf dem Bildschirm dargestellt.

 return(app->exec());

Jetzt startet das KDE-Programm mit app->exec() und begibt sich in seine lauernde Warteschleife, um auf Signale zu reagieren. Schließt man das Fenster, dann beendet sich die Applikation und das Hauptprogramm wird mit return verlassen.


Qt-Signale beim Start eines Programm

Freundlicherweise sendet Qt am Anfang 2 Signale aus, um das graphische Zeichnen einmal durchzuführen, ansonsten würden wir nur ein leeres Fenster sehen. Später müssen wir dies selber per Befehl repaint oder update veranlassen.

Das erste Signal meldet, dass sich die Fenstergröße auf 800,500 Pixel geändert hat. Dieses Signal heißt resize. Es springt das Unterprogramm BWindow::resizeEvent(QResizeEvent* event) an. Dort habe ich den ersten Goodie eingebaut.

Meine Qt/KDE-Programme arbeiten mit Grafikpuffern. Sämtliche Zeichnungen werden zuerst im Dunkeln des Speichers komplett gezeichnet und dann auf den Bildschirm kopiert. Man vermeidet dadurch schreckliche Flickerorgien.

In diesem resize-Event wird der Hintergrundpuffer buffer auf die neue Größe des Fensters eingestellt.

buffer.resize(event->size());

Das zweite Signal fordert ein Neuzeichnen des Fensters an. Dieses Signal heißt paint. Es springt das Unterprogramm BWindow::paintEvent(QPaintEvent*). Dort liegen die eigentlichen Zeichenroutinen.

QPainter p;

Erstmal braucht man ein Objekt das Zeichnen kann, die Variable p in von der Zeichenklasse QPainter.

p.begin(&buffer); und p.end();

Mit begin schützt sich der Paint-Event vor anderen Zeichenanforderungen. Diese werden solche lange verzögert bis p.end() die Zeichenroutine als abgeschlossen kennzeichnet. So kann kein Grafikmüll durch Überzeichnen entstehen.

p.setBrush(QColor(250, 250, 200));

Pinselfarbe auf beige setzen, die 3 Zahlen sind der RGB-Code für beige (RGB = rot/grün/blau-Anteile 0 - 255).

p.drawRect(0, 0, 800, 500);

Zeichne Rechteck mit Pinselfarbe (X=0, Y=0, Breite=800, Höhe = 500)

p.setPen(QColor(0, 79, 0));

Setze Stiftfarbe auf dunkelgrün (RGB).

p.drawLine(0, 0, 800, 500);

Zeichen Linie von links oben nach rechts unten im Fenster.


Sonstige Qt-Signale im Programm

Ich habe noch zusätzlich 2 Events für die Tastatur und die Maus eingeführt. Momentan werden aber keine eigenen Aktionen dort ausgeführt.

BWindow::keyPressEvent(QKeyEvent* e)

BWindow::mousePressEvent(QMouseEvent* e)

Die Tasten auf der Tastatur haben unter Qt bestimmte Namen, die kann man notfalls in der Headerdatei qkeycode.h nachlesen. Ich werde demnächst einmal die Maus- und Tastatureinbindung demonstieren.

Zum Fensterschließen führt Qt intern das Signal quit() durch, was zum Abbruch des Programm führt (beendet app->exec()-Aufruf).


Headerdatei prg1.h

#include <kapp.h>
#include <kmainwindow.h>
#include <qwidget.h>
#include <qkeycode.h>
#include <qpixmap.h>
#include <qobject.h>
#include <qpainter.h>

class BWindow : public KMainWindow
{
  Q_OBJECT

  QPixmap buffer;

  protected:
  void paintEvent(QPaintEvent*);
  void keyPressEvent(QKeyEvent*);
  void mousePressEvent(QMouseEvent*);
  void resizeEvent(QResizeEvent*);

  public:
  BWindow();  
} ;


prg1.h - Besprechung

#include <kapp.h>
#include <kmainwindow.h>
#include <qwidget.h>
#include <qkeycode.h>
#include <qpixmap.h>
#include <qobject.h>
#include <qpainter.h>

Am Anfang müssen alle verwendeten KDE und Qt-Headerdateien eingebunden werden. Wir brauchen nur wenige zur Zeit, aber gibt mehr als 100 verschiedene davon.

Jedes KDE3-Programm braucht eine Hauptfensterklasse, die von KMainWindow abgeleitet ist. Mit dieser Klasse werden später alle Grafikoperationen durchgeführt.

class BWindow : public KMainWindow

Das Schlüsselwort Q_OBJECT ist für Qt sehr wichtig. Die Bezeicher SIGNAL und SLOT sind Qt spezifisch und müssen durch einen separaten Metacompiler namens MOC in echte C++-Syntax transformiert werden. Noch verwenden wir allerdings kein eigenes SIGNAL und keinen SLOT.

QPixmap buffer;

Die Variable buffer dient uns zum Puffern vom Fenster im Hintergrund.

Alle benötigten Ereignisse (Events) müssen vorher deklariert werden.

protected:
void paintEvent(QPaintEvent*);
void keyPressEvent(QKeyEvent*);
void mousePressEvent(QMouseEvent*);
void resizeEvent(QResizeEvent*);

Der Konstruktor BWindow() wird hier nur deklariert, befindet sich aber komplett in prg1.cpp.

public:
BWindow();

Beachten Sie das eine Klasse am Ende mit Semikolon abgeschlossen werden muß (ein beliebter Anfängerfehler).


Makefile

.PHONY: all clean

INCDIR = -I/opt/kde3/include -I/usr/lib/qt3/include
LIBDIR = -L/usr/lib/qt3/lib -L/opt/kde3/lib
LIBS = -lkdeui -lkdecore -lqt
MOC = /usr/lib/qt3/bin/moc
META = prg1.moc

#Compilerflags mit Optimierung (auskommentiert)
#FLAGS = -Wall -pipe -O2 -fno-strength-reduce $(INCDIR)

#Compilerflags mit Debugmode + ohne Optimierung
FLAGS = -g -Wall $(INCDIR)
OBJ = prg1.o

all: prg1

prg1.moc: prg1.h

prg1.o: prg1.moc

%o: %cpp
[tab]
g++ -c $(FLAGS) -o $@ $<

%.moc: %.h
[tab] $(MOC) -o $@ $<

prg1: $(META) $(OBJ)
[tab]
 g++ -o prg1 $(OBJ) $(LIBDIR) $(LIBS)


Arbeiten mit make

Die Übersetzung von KDE3-Programmen ist aufwendig, das kommt man mit einfachen Befehlen nicht mehr hin. Dieser makefile gilt nur für SuSE 9.0 mit KDE 3.x. Bei Redhat und Mandrake liegen die Dateipfade etwas anders.

Für Qt3/KDE3 sind hier Anpassungen im Pfadbereich vorzunehmen.

Make regelt die Übersetzung von Programmen. Es ist eine Art Skriptdatei.

INCDIR = -I/opt/kde3/include -I/usr/lib/qt3/include
LIBDIR = -L/usr/lib/qt3/lib -L/opt/kde3/lib
LIBS = -lkdeui -lkdecore -lqt
MOC = /usr/lib/qt3/bin/moc
META = prg1.moc

FLAGS = -g -Wall $(INCDIR)
OBJ = prg1.o

Hier werden einige Variablen mit Werten gesetzt.

INCDIR = Pfad für Headerdateien
LIBDIR = Pfad für Code-Bibliotheken
LIBS = Namen der Code-Bibliotheken
MOC = Pfad des QT-Metacompilers MOC
META = Name der erzeugten Metadatei prg1.moc
FLAGS = Compiler-Steuerflaggen
OBJ = Programm-Zwischendatei prg1.o

Name des fertigen Programms:

all: prg1

Abhängigkeiten der Metadatei prg1.moc

prg1.moc: prg1.h

Abhängigkeiten des Zwischendatei prg1.o:

prg1.o: prg1.moc

Erzeugung der prg.o-Datei:

%o: %cpp
[tab]
g++ -c $(FLAGS) -o $@ $<

Man beachte das Programmbefehle immer mit der Tab-Taste [tab] eingerückt werden, sonst läuft nichts !

Erzeugung der Meta-Datei prg1.moc:

%.moc: %.h
[tab] $(MOC) -o $@ $<

Erzeugung des gesamten Programm:

prg1: $(META) $(OBJ)
[tab]
 g++ -o prg1 $(OBJ) $(LIBDIR) $(LIBS)

Eigene Kommentare werden durch das #-Zeichen gekennzeichnet.

Übersetzungsprozeß einleiten und Programm starten

Mit dem Befehl make auf der Konsole wird das Programm übersetzt. Man muß sich vorher im Verzeichnis des Programms befinden. Wenn Sie alles richtig eingetippt haben, das sollte ein Programm namens prg1 entstehen. Mit . /prg1 wird dann dieses Programm gestartet (vergessen Sie den Punkt vor dem Befehl nicht !).

Ich hoffe, das obengenannte Fenster erscheint jetzt.


Weitere KDE-/Qt-Beispiele (Link)

Auf der Webseite von KRelais findet man jede Menge kleine KDE/Qt-Beispiele zum Ueben:

KRelais Webseite von Burkhard Brauer http://www.technische-simulation.de

Literatur

(ohne Literatur wird es schwer, die Qt-Onlinehilfe liegt nur in Englisch vor, also einen Monat nicht rauchen und trinken und schon kann man sich die ersten 2 Teile leisten)

Qt Solutions
Matthias Kalle Dalheimer(englisch)
dpunkt-Verlag Juli 2004
ISBN 3-89864-280-1
(aktuell mit qt3/4)

C++ GUI-Programming with Qt3
Jasmin Blanchette (englisch)
Prentice-Hall 2004
ISBN 0-13124072-2
(mit qt 3.2)

Das Qt-Buch
Helmut Herold
SuSE Press 2001
ISBN 3-934678-76-9
(excellent, aber sehr teuer)

KDE2 - Programmierung
David Sweet
Markt u. Technik-Verlag 2001
ISBN 3-8272-5980-0
(sehr gut, aber teuer)

KDE- und Qt-Programmierung 2. Auflage
Burckhard Lehner
Addison-Wesley-Verlag 2001
ISBN 3-8273-1477-1

Programming with Qt 2. Auflage (englisch)
Kalle Dalheimer
O'Reilly-Verlag 2001
ISBN 3-89721-130-0

C-Programmierung unter Linux
Helmut Herold / Jörg Arndt
SuSE Press 2002
ISBN 3-935922-08-6