Control Keys

move to next slide (also Enter or Spacebar).
move to previous slide.
 d  enable/disable drawing on slides
 p  toggles between print and presentation view
CTRL  +  zoom in
CTRL  -  zoom out
CTRL  0  reset zoom

Slides can also be advanced by clicking on the left or right border of the slide.

The Qt class library

  • Qt is a widely used class library for cross-platform development of GUIs
  • Qt supports all major platforms, including Windows, Mac OS, and Linux
  • Qt is easy to learn, very well documented (see Online Reference Documentation) and there are many examples
  • Qt supports several C++ compilers, such as gcc or MS Visual Studio
  • Qt comes with the integrated development environment QtCreator 

Install, compile, and run Qt 6

  • Installing the Qt 6 open source framework (Download)
  • Creating a QMake-project with filename "fake.pro"
    TARGET = ApplicationName
    SOURCES += main.cpp Widget1.cpp Widget2.cpp
    HEADERS += Widget1.h Widget2.h
    QT += widgets
  • Creating a Makefile with qmake  (in a shell) :
    qmake -o Makefile fake.pro
  • Compiling: (in a shell)
    make
  • Compiling creates an executable file

The first GUI

The first GUI

  • Creating a first widget with the classes QApplication and QWidget
  • Display text with QLabel
gui1

Source code of the example: HelloGUI.cpp

The first 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
}

The first 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[]) {
  ...
}

Adding a Menu

  • Creating a menu with QMenuBar, QMenu, and QAction
gui1


Source code of the example: MyQMenuBar.cpp

Adding a Menu

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));
    }
};        

Adding a Button

  • Creating a button with QPushButton
gui1


Source code of the example: MyQButton.cpp

Adding a Button

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() {...}
};

Composition hierarchy for the example ("is part of")

containment_hierarchy


gui1

Inheritance hierarchy ("is a")

class_hierarchy

Event Processing

Event Processing

  • Event: Something has been activated, changed, moved, etc.
  • Event Source: The component that is directly affected by the event, for example, a pressed QPushButton
  • Event Listener: The class that would like to be informed about the event

Event Processing in Qt

  • Qt does not use callback functions as used by many frameworks (and also no EventListener as in Java)
  • Instead, Qt uses the principle of "signals and slots"
  • An event source sends a signal
  • An event observer is informed on a slot (a member function) about an event
  • A signal and a slot are connected via the connect() function
  • The advantage of the "signal and slot"-principle is that neither the sender nor the recipient have to know each other because a connect() can also be performed by a third party

Signals and Slots

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

The Qt classes have predefined signals and 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)

  • Attention: The employed keywords "signals" and "slots" are not part of the C++ standard
  • These keywords can be used only in classes that are derived from QObject
  • Qt comprises the so-called "Meta-Object Compiler" (MOC). This pre-compiler automatically translates all the classes that are identified by the keyword "Q_OBJECT" in standard C++ notation
  • The MOC also generates the additional code that is required internally by Qt to generate the meta-class QObject
  • The meta-class contains function pointers and the names of all signals and slots of the derived class
  • For defining own slots and signals, the own class must be derived from QObject, must be declared in a header file, and must be identified by the keyword "Q_OBJECT", so that the MOC can perform its task
  • The tool qmake identifies the relevant header files using this keyword and will automatically insert calls to the MOC in the makefile

Adding Event Processing for the Button

  • A click on the button increments a counter and returns the count in the text of the label
gui1

Source code of the example:
ActionButton.cpp, MyWidget.h

Adding Event Processing for the 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

Adding Event Processing for the 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));
  }

Adding Event Processing for the Menu

  • Selection of the menu item "Reset" resets the counter.
gui_action_menu


Source code of the example:
ActionMenu.cpp, MyWidget.h

Adding Event Processing for the Menu

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()));
  }
  ...

Adding Event Processing for the Menu

 ...
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);
  }
  ...

Mouse and Keyboard Events

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 *);
  ...
}

Mouse and Keyboard Events

  • In order to respond to mouse and keyboard events, the corresponding virtual member function can be overridden in a derived class from QWidget

QMouseEvent Example

  • A mouse-click on the QWidget displays the position of the mouse pointer as a text
gui_mouselistener


Source code of the example: MyMouseEvent.cpp

QMouseEvent Example

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 the previous examples no layout manager was used
  • This is not a problem as long as the window size is not changing

Layout-Manager

  • A layout manager automates the placement of components
  • The layout is automatically adjusted when the window size is changed
  • To this end, the individual components communicate their minimum, maximum, and desired layout size

Inheritance Hierarchy QLayout

class_hierarchy

Layout-Manager: QHBoxLayout

  • The QHBoxLayout-Manager allows components to be arranged horizontally in a line
  • The member function addStretch() inserts a QSpacerItem. This component fills the unused space and thereby allows, for example, to push the other components to the left or right border
gui_borderlayout

Source code of the example: 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

  • The QGridLayout manager arranges the components on a regular grid
  • Optionally, a component can span over multiple columns or rows
gui_gridlayout


Source code of the example: 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);
  }
};

Nested Layouts

  • Layouts can also be nested
  • This is done either by the member function addLayout() or by adding a new window that has its own internal layout
gtgui_nestedlayouts


Source code of the example: NestedLayouts.cpp

Nested 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 is particularly suitable for drawing 2-dimensional objects
  • A QGraphicsScene instance is generated that is then display by the class QGraphicsView

Example: Line, Rectangle, and Circle

  • The example draws a line, a rectangle, and a circle

gui_linerectcircle

Source code of the example: LineRectCircle.cpp

Example: Line, Rectangle, and Circle

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);
  }
};

Example: Drawing a general path

  • The example draws a "T" using the QPolygonF class

gui_generalpath

Source code of the example: MyGeneralPath.cpp

Example: Drawing a general path

    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);

Are there any questions?

questions

Please notify me by e-mail if you have questions, suggestions for improvement, or found typos: Contact

More lecture slides

Slides in German (Folien auf Deutsch)