9.9.  The Event Loop: Revisited

[ fromfile: eventloop-revisited.xml id: eventloop-revisited ]

In Section 8.3 you met QApplication and the event loop. Now that you have had some experience with GUI programming, you are ready to look at events in more detail.

QWidgets send QEvents to other QObjects in response to user actions such as mouse events and keyboard events. A widget can also respond to events from the window manager such as repaints, resizes, or close events. Events are posted (i.e., added to the event queue) and can be filtered and prioritized. Furthermore, it is possible to post a custom event directly to any QObject or selectively respond to events that were posted to other objects. You can delegate the handling of an event to a specially defined handler object.

Each QWidget can be specialized to respond to keyboard and mouse events in its own way. Figure 9.23 is a screenshot of a QWidget named KeySequenceLabel that captures a QKeySequence and displays it for the user.

Figure 9.23.  KeySequenceLabel Widget

KeySequenceLabel Widget

In Example 9.13 you can see from the names of the private slots that QtCreator was used to compose the GUI. Also, we chose the Multiple Inheritance option for embedding the Ui class, so the KeySequenceLabel class is derived from QMainWindow and from the Designer generated Ui class.

Example 9.13. src/eventloop/keysequencelabel.h

[ . . . . ]
#include "ui_keysequencelabel.h"
#include <QList>
#include <QPair>
class QObjectBrowserAction;

class KeySequenceLabel : public QMainWindow, private Ui::KeySequenceLabel {
    Q_OBJECT
 public:
    explicit KeySequenceLabel(QWidget* parent = 0);
 protected:                         1
    void changeEvent(QEvent* e);
    void keyPressEvent(QKeyEvent*);
    void leaveEvent(QEvent*);
    void enterEvent(QEvent*);
    void paintEvent(QPaintEvent*);
    void timerEvent(QTimerEvent*);  2
    void updateUi();
 private slots:
    void on_actionShow_ObjectBrowser_triggered(bool checked);
    void on_m_clearButton_clicked();
    void on_actionQuit_triggered();
 private:
    QObjectBrowserAction* m_browserAction;
    QList<QPair<int, int> > m_keys;
    int m_paints;
};
[ . . . . ]

1

QWidget event handler overrides

2

QObject event handler override


The implementation of this class contains some interesting items.

There is a QLabel in the dialog used for display of a QKeySequence. The QKeySequence class encapsulates a sequence of up to four keystrokes, typically used as shortcuts to invoke QActions. The base class keyPressEvent() handler is overridden in the KeySequenceLabel widget. In Example 9.14, you can see that it catches the keystrokes and saves them to a list before the child widgets see them. In updateUi(), those events convert to a QKeySequence, and then to a QString, which is sent to the QLabel for display via setText().

Example 9.14. src/eventloop/keysequencelabel.cpp

[ . . . . ]

void KeySequenceLabel::keyPressEvent(QKeyEvent* evt) {
    bool doNothing = false;

    if (evt->key() == 0) doNothing = true;
    if (m_keys.size() > 3) doNothing = true;
    if (doNothing) {
        QMainWindow::keyPressEvent(evt); 1
        return;
    }
    QPair<int, int> pair = QPair<int, int>(evt->modifiers(), evt->key());
    m_keys << pair;
    evt->accept();
    updateUi();
}

void KeySequenceLabel::updateUi() {
    if (!m_keys.isEmpty()) {
        int keys[4] = {0,0,0,0};
        for (int i=0; i<m_keys.size(); ++i) {
            QPair<int, int> pair = m_keys[i];
            keys[i] = pair.first | pair.second;
        }
        QKeySequence seq = QKeySequence(keys[0], keys[1], keys[2], keys[3]);
        m_label->setText(seq.toString());
    }
    else m_label->clear();
}

1

QWidget's base class handler responds to ESC for pop-up windows.


We defined mouse event handlers, shown in Example 9.15, that tell you when the mouse pointer enters or leaves the widget.

Example 9.15. src/eventloop/keysequencelabel.cpp

[ . . . . ]

void KeySequenceLabel::enterEvent(QEvent* evt) {

    statusBar()->showMessage(tr("Mouse is in da house"));
    evt->accept();

}

void KeySequenceLabel::leaveEvent(QEvent* evt) {
    statusBar()->showMessage(tr("Mouse has left the building"));
    evt->accept();
}

In Example 9.16 the constructor makes use of the QObject built-in timer and calls QObject::startTimer(), which generates a timer event every 2 seconds. We handle the timerEvent() by updating a QLCDNumber, once every 2 seconds, with the number of paint events that have been performed so far. For example, whenever the value of the LCDNumber changes, this indirectly causes the widget to repaint. You can run this app and try moving and resizing the widget to see how it effects the number of paint events.

Example 9.16. src/eventloop/keysequencelabel.cpp

[ . . . . ]

KeySequenceLabel::KeySequenceLabel(QWidget* parent) :
    QMainWindow(parent), m_browserAction(new QObjectBrowserAction(this)) {

    setupUi(this);
    startTimer(2000); 1
    m_paints = 0;
}

void KeySequenceLabel::timerEvent(QTimerEvent*) {
    m_lcdNumber->display(m_paints);
}

void KeySequenceLabel::paintEvent(QPaintEvent* evt) {
    ++m_paints;
    QMainWindow::paintEvent(evt);
}

1

A timer event occurs every 2 seconds.


All of the GUI activity discussed so far was accomplished with events and event handlers. There is another interesting feature of this example that is worth mentioning.

From the Tools menu you can select Show ObjectBrowser and see the widget in Figure 9.24.

Figure 9.24.  QObjectBrowser Widget

QObjectBrowser Widget

QObjectBrowser is an open source debugging tool that gives a graphic display of properties, signals, and slots of a QObject tree during the execution of a program. [74] One easy way to use it is by adding the QObjectBrowserAction directly to the QMainWindow's menu.

A slightly modified version of this tool is included in src/libs.



[74] Contributed by Jeremy Magland, you can follow the development of this utility from its initial posting and see some community suggestions and the author's responses at http://tinyurl.com/2a6qkpy