Steuerungstasten

nächste Folie (auch Enter oder Spacebar).
vorherige Folie
 d  schaltet das Zeichnen auf Folien ein/aus
 p  wechselt zwischen Druck- und Präsentationsansicht
CTRL  +  vergrößert die Folien
CTRL  -  verkleinert die Folien
CTRL  0  setzt die Größenänderung zurück

Das Weiterschalten der Folien kann ebenfalls durch das Klicken auf den rechten bzw. linken Folienrand erfolgen.

Die Qt Klassenbibliothek

  • Qt ist eine weit verbreitete Klassenbibliothek zur plattformübergreifenden Entwicklung von GUIs
  • Qt unterstützt alle gängigen Plattformen, wie Windows, Mac OS und Linux
  • Qt ist leicht zu erlernen, sehr gut dokumentiert (siehe Online Reference Documentation) und es gibt viele Beispiele
  • Qt unterstützt zahlreiche C++ Compiler, wie gcc oder MS Visual Studio
  • Zur Entwicklung kann ebenfalls die Qt-eigene integrierte Entwicklungsumgebung QtCreator  verwendet werden

Qt 6 installieren, kompilieren und ausführen

  • Installation des Qt 6 Open Source Frameworks (Download)
  • Erstellen eines QMake-Projekts mit Dateinamen "fake.pro"
    TARGET = ApplicationName
    SOURCES += main.cpp Widget1.cpp Widget2.cpp
    HEADERS += Widget1.h Widget2.h
    QT += widgets
  • Erstellen eines Makefiles mit qmake  (in einer Shell) :
    qmake -o Makefile fake.pro
  • Kompilieren: (in einer Shell)
    make
  • Durch das Kompilieren entsteht eine ausführbare Datei

Das erste GUI

Das erste GUI

  • Erstellen eines ersten Widgets mit den Klassen QApplication und QWidget
  • Darstellung des Textes mittels QLabel
gui1

Quelltext des Beispiels: HelloGUI.cpp

Das erste GUI

#include <QApplication>
#include <QLabel>
#include <QWidget>

class MyWidget : public QWidget { // our own custom widget
  ...
};

int main (int argc, char* argv[]) {
  // create a QApplication object that handles initialization,
  // finalization, and the main event loop
  QApplication appl(argc, argv);
  MyWidget widget;  // create a widget
  widget.show(); //show the widget and its children
  return appl.exec(); // execute the application
}

Das erste GUI

#include <QApplication>
#include <QLabel>
#include <QWidget>

class MyWidget : public QWidget{
  public:
    MyWidget(QWidget *parent = NULL) : QWidget(parent) {
        this->resize(320, 240);
        this->setWindowTitle("HelloGUI with Qt");
        QLabel* label = new QLabel("Hello World", this);
    }
};

int main (int argc, char* argv[]) {
  ...
}

Hinzufügen eines Menüs

  • Erstellen eines Menüs mit QMenuBar, QMenu und QAction
gui1


Quelltext des Beispiels: MyQMenuBar.cpp

Hinzufügen eines Menüs

class MyWidget : public QMainWindow {
 public:
    MyWidget(QWidget *parent = NULL) : QMainWindow(parent) {
        this->resize(320, 240);
        this->setWindowTitle("HelloGUI with Qt");
        QLabel* label = new QLabel("Hello World", this);
        createMenus();
    }
  private:
    void createMenus() {
      QMenuBar *bar = menuBar();
      QMenu *fileMenu = bar->addMenu(tr("&File"));
      fileMenu->addAction(new QAction("Open", this));
      fileMenu->addAction(new QAction("Close", this));
    }
};        

Hinzufügen eines Buttons

  • Erstellen einer Schaltfläche mit QPushButton
gui1


Quelltext des Beispiels: MyQButton.cpp

Hinzufügen eines Buttons

class MyWidget : public QMainWindow {
 public:
    MyWidget(QWidget *parent = NULL) : QMainWindow(parent) {
        ...
        QLabel* label = new QLabel("Hello World", this);
        label->setGeometry(120, 20, 150, 30);
        QPushButton* button = new QPushButton( "Button text", this);
        button->setGeometry(120, 50, 150, 30);
        createMenus();
    }
  private:
    void createMenus() {...}
};

Kompositionshierarchie für das Beispiel ("ist Teil von")

containment_hierarchy


gui1

Vererbungshierarchie ("ist ein")

class_hierarchy

Ereignisverarbeitung

Ereignisverarbeitung

  • Ereignis (Event): Etwas wurde aktiviert, verändert, bewegt, etc.
  • Ereignisquelle: Die Komponente, von der das Ereignis ausgeht, z.B. ein gedrückter QPushButton
  • Ereignisbeobachter: Die Klasse, die von dem Ereignis informiert werden möchte

Ereignisverarbeitung in Qt

  • Qt verwendet keine Rückruffunktion (callback function) wie viele Frameworks (und auch keine EventListener wie Java)
  • Stattdessen verwendet Qt das "Signals und Slots"-Prinzip
  • Eine Ereignisquelle sendet ein Signal aus
  • Ein Ereignisbeobachter wird auf seinem Slot (einer Memberfunktion) über ein Ereignis informiert
  • Ein Signal und ein Slot werden mittels der connect() Funktion verbunden
  • Der Vorteil des "Signals und Slots"-Prinzips ist, dass weder Sender noch Empfänger voneinander wissen müssen, da ein connect() auch von dritter Seite ausgeführt werden kann

Signals und Slots

signalsandlslots
connect(QObject1, signal1, QObject2, slot1)
connect(QObject1, signal1, QObject2, slot2)
connect(QObject1, signal2, QObject4, slot1)
connect(QObject3, signal1, QObject4, slot3)
Quelle: equivalent zu Qt Documentation

Die Qt-Klassen haben vordefinierte Signals und Slots

class QAbstractButton : public QWidget {
  Q_OBJECT
public:
  QAbstractButton(QWidget* parent=0);
  void setText(const QString &text);
  ...
public slots:
  void click();
  void toggle();
  ...
signals:
  void pressed();
  void released();
  ...
};

Meta-Object Compiler (MOC)

  • Achtung: Die verwendeten Schlüsselworte "signals" und "slots" sind nicht Teil des C++-Standards
  • Diese Schlüsselworte können nur in Klassen verwendet werden, die von QObject abgeleitet sind
  • In Qt gibt es den sogenannten "Meta-Object Compiler" (MOC), der als Pre-Compiler automatisch alle Klassen, die mit dem Schlüsselwort "Q_OBJECT" gekennzeichnet sind, in Standard C++ Notation übersetzt
  • Der MOC erzeugt dabei ebenfalls den zusätzlichen Code, der Qt-intern zur Erzeugung der Meta-Klasse QObject benötigt wird
  • Die Meta-Klasse enthält u.a. Funktionszeiger und Namen aller Signals und Slots der abgeleiteten Klasse
  • Sollen eigene Slots und Signals definiert werden, muss die eigene von QObject abgeleitete Klasse in einem Header-File deklariert und mit dem Schlüsselwort "Q_OBJECT" gekennzeichnet werden, damit der MOC seine Arbeit verrichtet
  • Das Werkzeug qmake erkennt die relevanten Dateien anhand dieses Schlüsselworts und fügt entsprechende Aufrufe des MOCs ins Makefile ein

Hinzufügen einer Ereignisverarbeitung für den Button

  • Ein Klick auf den Button erhöht einen Zähler und gibt den Zählerstand im Label-Text aus
gui1

Quelltext des Beispiels:
ActionButton.cpp, MyWidget.h

Hinzufügen einer Ereignisverarbeitung für den Button

#ifndef MYWIDGET_H
#define MYWIDGET_H
#include <QMainWindow> 
...
class MyWidget : public QMainWindow {
    Q_OBJECT
public:
  MyWidget(QWidget *parent = NULL) : QMainWindow(parent) {...}
public slots:
  void buttonClicked() {...}
private:
  void createMenus() {...}
private:
  int counter;
  QLabel* label;
};
#endif // MYWIDGET_H

Hinzufügen einer Ereignisverarbeitung für den Button

public:
  MyWidget(QWidget *parent = NULL) : QMainWindow(parent) {
    ...
    counter = 0;
    label = new QLabel("Hello World", this);
    label->setGeometry(120, 20, 150, 30);
    QPushButton* button = new QPushButton( "Increment", this);
    button->setGeometry(120, 50, 150, 30);
    QObject::connect(button, SIGNAL(clicked()), 
                     this, SLOT(buttonClicked()));
    createMenus();
  }
public slots:
  void buttonClicked() {
      counter++;
      label->setText(QString("Click #%1").arg(counter));
  }

Hinzufügen einer Ereignisverarbeitung für das Menü

  • Die Auswahl des Menüeintrags "Reset" setzt den Zähler zurück.
gui_action_menu


Quelltext des Beispiels:
ActionMenu.cpp, MyWidget.h

Hinzufügen einer Ereignisverarbeitung für das Menü

class MyWidget : public QMainWindow {
    Q_OBJECT
public:
  MyWidget(QWidget *parent = NULL) : QMainWindow(parent) {
    ...
    createActions();
    createMenus();
    QObject::connect(button, SIGNAL(clicked()), 
                     this, SLOT(buttonClicked()));          
    QObject::connect(incrementAct, SIGNAL(triggered()), 
                     this, SLOT(buttonClicked()));
    QObject::connect(resetAct, SIGNAL(triggered()), 
                     this, SLOT(resetClicked()));
  }
  ...

Hinzufügen einer Ereignisverarbeitung für das Menü

 ...
private:
  void createActions() {
    incrementAct = new QAction("Increment", this);
    incrementAct->setShortcut(QKeySequence(Qt::ALT + Qt::Key_I));
    incrementAct->setToolTip(tr("Increment the counter"));
    resetAct = new QAction("Reset", this);
    resetAct->setShortcut(QKeySequence(Qt::ALT + Qt::Key_R));
    resetAct->setToolTip(tr("Reset the counter"));
  }
  void createMenus() {
    QMenuBar* bar = menuBar();
    QMenu* fileMenu = bar->addMenu(tr("Counter"));
    fileMenu->addAction(incrementAct);
    fileMenu->addAction(resetAct);
  }
  ...

Maus- und Tastatur-Ereignisse

class QWidget : public QObject, public QPaintDevice {
    ... 
protected: // Event handlers
  virtual void mousePressEvent(QMouseEvent *);
  virtual void mouseReleaseEvent(QMouseEvent *);
  virtual void mouseDoubleClickEvent(QMouseEvent *);
  virtual void mouseMoveEvent(QMouseEvent *);
  virtual void wheelEvent(QWheelEvent *);
  virtual void keyPressEvent(QKeyEvent *);
  virtual void keyReleaseEvent(QKeyEvent *);
  virtual void focusInEvent(QFocusEvent *);
  virtual void focusOutEvent(QFocusEvent *);
  ...
}

Maus- und Tastatur-Ereignisse

  • Soll auf Maus- und Tastatur-Events reagiert werden, kann in einer von QWidget abgeleiteten Klasse die entsprechende virtuelle Memberfunktion überschrieben werden

QMouseEvent Beispiel

  • Beim Klick mit der Maus auf das QWidget wird die Position des Mauszeigers ausgegeben
gui_mouselistener


Quelltext des Beispiels: MyMouseEvent.cpp

QMouseEvent Beispiel

class MyWidget : public QWidget {
public:
  MyWidget(QWidget *parent = NULL) : QWidget(parent) {
    this->resize(320, 240);
    this->setWindowTitle("HelloGUI with Qt");
    label = new QLabel("Position 0, 0", this);
    label->setGeometry(120, 20, 150, 30);
  }
protected:
  void mousePressEvent(QMouseEvent *e)
  {
    int x = e->x(); int y = e->y();
    label->setText(QString("Position %1, %2").arg(x).arg(y));
  }
private:
  QLabel* label;
};

Layout-Manager

Layout-Manager

  • In den bisherigen Beispielen wurde kein Layout-Manager verwendet
  • Das ist kein Problem, solange die Fenstergröße nicht verändert wird

Layout-Manager

  • Ein Layout-Manager automatisiert das Platzieren von Komponenten
  • Das Layout wird automatisch angepasst, wenn sich die Fenstergröße verändert
  • Dazu teilen die Komponenten ihre minimale, maximale, und gewünschte Größe mit

Vererbungshierarchie QLayout

class_hierarchy

Layout-Manager: QHBoxLayout

  • Der QHBoxLayout-Manager erlaubt, Komponenten horizontal in einer Linie anzuordnen
  • Die Memberfunktion addStretch() fügt ein QSpacerItem ein. Diese Komponente füllt den nicht benötigten Platz und ermöglicht so u.a. die anderen Komponenten an die linken oder rechten Rand anzuordnen
gui_borderlayout

Quelltext des Beispiels: HorizontalLayout.cpp

Layout-Manager: QHBoxLayout

class MyWidget : public QWidget {

public:
  MyWidget(QWidget *parent = NULL) : QWidget(parent) {
    this->setWindowTitle("HelloGUI with Qt");
     QPushButton* but1 = new QPushButton("Button 1");
     QPushButton* but2 = new QPushButton("Button 2");
     QPushButton* but3 = new QPushButton("Button 3");

     QHBoxLayout *layout = new QHBoxLayout;
     layout->addWidget(but1);
     layout->addWidget(but2);
     layout->addWidget(but3);
     //layout->addStretch(); // try this
     this->setLayout(layout);
  }
};

Layout-Manager: QGridLayout

  • Der QGridLayout-Manager ordnet die Komponenten auf einem regulären Gitter an
  • Optional kann eine Komponente mehrere Spalten oder Zeilen überspannen
gui_gridlayout


Quelltext des Beispiels: MyQGridLayout.cpp

Layout-Manager: QGridLayout

class MyWidget : public QWidget {
public:
  MyWidget(QWidget *parent = NULL) : QWidget(parent) {
     QPushButton* but1 = new QPushButton("Button 1");
     QPushButton* but2 = new QPushButton("Button 2");
     QPushButton* but3 = new QPushButton("Button 3");
     QPushButton* but4 = new QPushButton("Button 4");
     QLineEdit* edit = new QLineEdit();
     QGridLayout* layout = new QGridLayout();
     layout->addWidget(but1, 0, 0);
     layout->addWidget(but2, 0, 1);
     layout->addWidget(but3, 1, 0);
     layout->addWidget(but4, 1, 1);
     layout->addWidget(edit, 2, 0, 1, 2);
     this->setLayout(layout);
  }
};

Geschachtelte Layouts

  • Layouts können auch geschachtelt werden
  • Dies geschieht entweder durch die Memberfunktion addLayout() oder durch Hinzufügen ein neues Fenster, das sein eigenes internes Layout besitzt
gtgui_nestedlayouts


Quelltext des Beispiels: NestedLayouts.cpp

Geschachtelte Layouts

  QGroupBox *groupBox = new QGroupBox("Some more important buttons");
  QPushButton* impBut1 = new QPushButton("Important 1");
  QPushButton* impBut2 = new QPushButton("Important 2");
  QPushButton* impBut3 = new QPushButton("Important 3");
  QHBoxLayout *hlayout = new QHBoxLayout;
  hlayout->addWidget(impBut1);
  hlayout->addWidget(impBut2);
  hlayout->addWidget(impBut3);
  QGridLayout* layout = new QGridLayout();
  layout->addWidget(but1, 0, 0);
  layout->addWidget(but2, 0, 1);
  layout->addWidget(but3, 1, 0);
  layout->addWidget(but4, 1, 1);
  groupBox->setLayout(hlayout);
  layout->addWidget(groupBox, 2, 0, 1, 2);
  this->setLayout(layout);

QGraphicsScene

QGraphicsScene

  • QGraphicsScene eignet sich besonders zum Zeichnen von 2-dimensionalen Objekten
  • Dabei wird zunächst eine QGraphicsScene erzeugt und diese dann durch die Klasse QGraphicsView zur Anzeige gebracht

Beispiel: Linie, Rechteck und Kreis

  • Das Beispiel zeichnet eine Linie, ein Rechteck und einen Kreis

gui_linerectcircle

Quelltext des Beispiels: LineRectCircle.cpp

Beispiel: Linie, Rechteck und Kreis

class MyWidget : public QMainWindow {
public:
  MyWidget(QWidget *parent = NULL) : QMainWindow(parent) {
    this->setWindowTitle("HelloGUI with Qt");
    this->resize(320, 240);
    QGraphicsScene* scene = new QGraphicsScene();
    scene->addLine(20.0, 50.0, 50.0, 200.0);
    scene->addRect(100.0, 50.0, 60.0, 80.0);
    scene->addEllipse(200.0, 100.0, 80.0, 80.0);
    QGraphicsView* view = new QGraphicsView(scene);
    setCentralWidget(view);
  }
};

Beispiel: Zeichnen einer allgemeinen Form

  • Das Beispiel zeichnet ein "T" mit Hilfe der QPolygonF Klasse

gui_generalpath

Quelltext des Beispiels: MyGeneralPath.cpp

Beispiel: Linie, Rechteck und Kreis

    QGraphicsScene* scene = new QGraphicsScene();
    QPolygonF polygon;
    polygon <<
    QPointF( 50,  50)  << // start here
    QPointF( 50,  70)  << // going down
    QPointF(100,  70)  << // going right
    QPointF(100, 180)  << // going down
    QPointF(120, 180)  << // going right
    QPointF(120,  70)  << // going up
    QPointF(170,  70)  << // going right
    QPointF(170,  50)  << // going up
    QPointF( 50,  50);   // going left (back to start)
    scene->addPolygon(polygon);

Gibt es Fragen?

questions

Anregungen oder Verbesserungsvorschläge können auch gerne per E-mail an mich gesendet werden: Kontakt

Weitere Vorlesungsfolien

Folien auf Englisch (Slides in English)