Widget-Klassen reagieren auf sogenannte Events, die in speziellen Methoden der Klasse abgearbeitet werden. Durch Ableiten der Klasse und Überschreiben der Methoden können wir das Verhalten von Widgets selbst festlegen. Die Methoden sind als protected
deklariert, wodurch sie auch in abgeleiteten Klassen verwendet werden können und bekommen ein Event-Objekt mit Informationen als Parameter. Event-Objekte liegen - wie allgemein in Qt üblich - immer in einem eigenen Header.
Wir werden an dieser Stelle nur die wichtigsten Events behandeln, weniger gebräuchliche können in der Dokumentation nachgelesen werden. Hier soll vor allem das Prinzip des Überschreibens von Event-Handlern erklärt werden.
Ein typisches Beispiel ist das „Wollen sie das Programm wirklich beenden?“-Popup, wenn man versucht das Programm zu schließen. Um das zu bewirken, müssen wir schlicht die Methode closeEvent()
überschreiben und darin eine Messagebox anzeigen.
// main.cpp #include "TextEdit.h" #include <QApplication> int main( int argc, char *argv[] ) { QApplication app( argc, argv ); TextEdit widget; widget.resize( 300, 300 ); widget.setWindowTitle( "Event-Test" ); widget.show(); return app.exec(); }
// TextEdit.h #ifndef TEXTEDIT_H #define TEXTEDIT_H #include <QTextEdit> #include <QCloseEvent> #include <QMessageBox> class TextEdit : public QTextEdit { Q_OBJECT protected: void closeEvent( QCloseEvent *event ); // Close-Event von QTextEdit überschreiben }; #endif
// TextEdit.cpp #include "TextEdit.h" void TextEdit::closeEvent( QCloseEvent *event ) { // Benutzer Fragen, ob er das Programm wirklich beenden will if( QMessageBox::question ( this, // Parent-Widget "Achtung", // Fenstertitel "Willst du dieses Programm wirklich beenden?", // Text QMessageBox::Yes | QMessageBox::No ) // Verfügbare Buttons == QMessageBox::Yes ) // Prüfen, ob "Yes" gedrückt wurde { // Event wird akzeptiert und muss nicht weiter verarbeitet werden. event->accept(); // Methode der Basisklasse aufrufen, damit das Widget geschlossen wird QTextEdit::closeEvent( event ); } else { // Event wird nicht akzeptiert und weiter verarbeitet. event->ignore(); } }
Als nächstes wollen wir das vorherige Beispiel dahingehend erweitern, dass es mit der Escape-Taste beendet werden kann. Tastatur-Events werden in der Methode keyPressEvent()
behandelt. Um an die gewünschten Informationen zu kommen überschreiben wir also diese Methode. Konstanten zur Identifikation von Tasten finden sich im Qt
-Namespace und haben ein Key_
-Präfix. Das bedeutet wir fragen in unserem Event-Handler auf Qt::Key_Escape
ab und schließen das Widget, falls diese Taste betätigt wurde. Die Konstante kann mit dem Rückgabewert der Methode key()
des Event-Objektes verglichen werden.
// TextEdit.h #ifndef TEXTEDIT_H #define TEXTEDIT_H #include <QTextEdit> #include <QCloseEvent> #include <QKeyEvent> #include <QMessageBox> class TextEdit : public QTextEdit { Q_OBJECT protected: void closeEvent( QCloseEvent *event ); // Close-Event von QTextEdit überschreiben void keyPressEvent( QKeyEvent *event ); // Tastatur-Event von QTextEdit überschreiben }; #endif
// TextEdit.cpp #include "TextEdit.h" void TextEdit::closeEvent( QCloseEvent *event ) { // Benutzer Fragen, ob er das Programm wirklich beenden will if( QMessageBox::question ( this, // Parent-Widget "Achtung", // Fenstertitel "Willst du dieses Programm wirklich beenden?", // Text QMessageBox::Yes | QMessageBox::No ) // Verfügbare Buttons == QMessageBox::Yes ) // Prüfen, ob "Yes" gedrückt wurde { // Event wird akzeptiert und muss nicht weiter verarbeitet werden. event->accept(); // Methode der Basisklasse aufrufen, damit das Widget geschlossen wird QTextEdit::closeEvent( event ); } else { // Event wird nicht akzeptiert und weiter verarbeitet. event->ignore(); } } void TextEdit::keyPressEvent( QKeyEvent *event ) { if( event->key() == Qt::Key_Escape ) // Prüfen ob Escape gedrückt wurde { event->accept(); // Event wird akzeptiert close(); // Widget schließen -> closeEvent() wird aufgerufen } else // Es wurde eine andere Taste als Escape gedrückt { event->ignore(); // Event wird nicht akzeptiert QTextEdit::keyPressEvent( event ); // Event an die Basisklasse weitergeben } }
Die main-Funktion und die Optik des Programmes ist identisch zum vorherigen Beispiel. Wird nun jedoch im Programm die Escape-Taste gedrückt, wird unsere closeEvent()
-Methode aufgerufen und die Messagebox angezeigt. Wichtig ist auch, dass wir in den anderen Fällen die Methode der Basisklasse aufrufen. Tun wir dies nicht, werden alle andern Tastendrücke ignoriert und es kann nichts in das Textfeld eingegeben werden.
Im Unterschied zu den bisherigen Events gibt es zur Behandlung von Maus-Events mehrere Methoden und Objekte:
mouseMoveEvent()
mousePressEvent()
, mouseReleaseEvent()
mouseDoubleClickEvent()
wheelEvent()
Alle Methoden bekommen einen Zeiger auf ein Objekt der Klasse QMouseEvent
übergeben. Ausnahme bildet die Methode wheelEvent()
, die mit QWheelEvent
arbeitet.
Zum Bewegungs-Event ist noch hinzuzufügen, dass die Methode standardmäßig nur aufgerufen, wenn während der Bewegung eine Taste gedrückt wird. Um dieses Verhalten zu ändern muss
QWidget::setMouseTracking( true );
aufgerufen werden.
Der Einfachheit halber geben wir im folgenden Beispiel einfach die Informationen zu den Events aus ohne sie weiter zu verarbeiten:
// main.cpp #include "MouseInfo.h" #include <QApplication> int main( int argc, char *argv[] ) { QApplication app( argc, argv ); MouseInfo m; m.show(); return app.exec(); }
// MouseInfo.h #ifndef MOUSEINFO_H #define MOUSEINFO_H #include <QWidget> #include <QMouseEvent> #include <QWheelEvent> #include <iostream> class MouseInfo : public QWidget { public: MouseInfo(); protected: void mouseMoveEvent( QMouseEvent *event ); // Mausbewegungs-Event überschreiben void mousePressEvent( QMouseEvent *event ); // Event für gedrückte Maustasten überschreiben void mouseReleaseEvent( QMouseEvent *event ); // Event für losgelassene Maustasten überschreiben void mouseDoubleClickEvent( QMouseEvent *event ); // Event für Doppelklicks überschreiben void wheelEvent( QWheelEvent *event ); // Mausrad-Event überschreiben }; #endif // MOUSEINFO_H
// MouseInfo.cpp #include "MouseInfo.h" MouseInfo::MouseInfo() { resize( 400, 400 ); } void MouseInfo::mouseMoveEvent( QMouseEvent *event ) { // Koordinaten der Maus ausgeben std::cout << "Maus wurde mit gedrueckter Taste bewegt; neue Koordinaten: " << event->x() << " / " << event->y() << std::endl; } void MouseInfo::mousePressEvent( QMouseEvent *event ) { // Name der Maustaste und Koordinaten ausgeben switch( event->button() ) { case Qt::LeftButton: std::cout << "Linke "; break; case Qt::RightButton: std::cout << "Rechte "; break; case Qt::MidButton: std::cout << "Mittlere "; break; default: std::cout << "Unbekannte "; break; } std::cout << "Maustaste wurde an den Koordinaten " << event->x() << " / " << event->y() << " gedrueckt" << std::endl; } void MouseInfo::mouseReleaseEvent( QMouseEvent *event ) { // Name der Maustaste und Koordinaten ausgeben switch( event->button() ) { case Qt::LeftButton: std::cout << "Linke "; break; case Qt::RightButton: std::cout << "Rechte "; break; case Qt::MidButton: std::cout << "Mittlere "; break; default: std::cout << "Unbekannte "; break; } std::cout << "Maustaste wurde an den Koordinaten " << event->x() << " / " << event->y() << " ausgelassen" << std::endl; } void MouseInfo::mouseDoubleClickEvent( QMouseEvent *event ) { // Koordinaten des Doppelklicks ausgeben std::cout << "Doppelklick an den Koordinaten " << event->x() << " / " << event->y() << std::endl; } void MouseInfo::wheelEvent( QWheelEvent *event ) { // Gradänderung ausgeben; Der Rückgabewert von 'delta' entspricht der 8-fachen Gradzahl std::cout << "Mausrad wurde um " << event->delta() / 8 << "° gedreht" << std::endl; }
Qt bietet uns bereits eine Vielzahl an fertigen Widgets, aber wir können natürlich auch selbst auf Widgets zeichnen. Möglich wird dies durch das Überschreiben des Event-Handlers paintEvent()
. Dazu müssen wir in dieser Methode eine Instanz von QPainter
mit unserem Widget (also this
) als Parameter für den Konstruktor erstellen.
QPainter
macht es uns recht einfach einfache geometrische Formen und Text auf Widgets zu zeichnen. Aufgrund der Qt-internen Architektur sollte man QPainter
-Objekte nur im Haupt-Thread verwenden!
Brush und Pen bestimmen die Farbe der Zeichnung, wobei Brush für die Füllfarbe und Pen für Linien und Rahmen verwendet wird. Dabei können wir entweder auf die Klasse QColor
zurückgreifen, oder diese implizit durch Verwendung des Enums Qt::GlobalColor
erstellen und an die jeweilige set-Methode übergeben:
QPainter painter( this ); painter.setPen( Qt::blue ); painter.setBrush( Qt::white );
QPainter
bietet uns fertige Funktionen zum Zeichnen von primitiven geometrischen Formen wie Linien, Kreisen, Ellipsen und Rechtecken. Als einfaches Beispiel zeichnen wir jetzt die Diagonalen des Widgets blau ein:
// main.cpp #include "PaintWidget.h" #include <QApplication> int main( int argc, char *argv[] ) { QApplication app( argc, argv ); PaintWidget widget; widget.resize( 300, 300 ); widget.setWindowTitle( "Paint-Test" ); widget.show(); return app.exec(); }
// PaintWidget.h #ifndef PAINTWIDGET_H #define PAINTWIDGET_H #include <QWidget> class PaintWidget : public QWidget { protected: void paintEvent( QPaintEvent *event ); }; #endif // PAINTWIDGET_H
// PaintWidget.cpp #include "PaintWidget.h" #include <QPainter> void PaintWidget::paintEvent( QPaintEvent *event ) { QPainter painter( this ); // Hintergrund weiß füllen painter.setBrush( Qt::white ); painter.drawRect( 0, 0, width(), height() ); // Blaue als Farbe für die Linien painter.setPen( Qt::blue ); // Blaue Linie von links oben nach rechts unten painter.drawLine( 0, 0, width(), height() ); // Blaue Linie von rechts oben nach links unten painter.drawLine( width(), 0, 0, height() ); }
Das manuelle Zeichnen von Text auf Widgets ist ebenfalls möglich und funktioniert analog zu geometrischen Formen. Pen dient dabei als Schriftfarbe, die Schriftart kann über ein QFont
-Objekt festgelegt werden.
Im nächste Beispiel wird der Text „proggen.org“ in blauer Monospace-Schrift, fett und mit Schrfitgröße 30 zentriert auf ein Widget gezeichnet:
// main.cpp #include "PaintWidget.h" #include <QApplication> int main( int argc, char *argv[] ) { QApplication app( argc, argv ); PaintWidget widget; widget.resize( 300, 150 ); widget.setWindowTitle( "Text-Test" ); widget.show(); return app.exec(); }
// PaintWidget.h #ifndef PAINTWIDGET_H #define PAINTWIDGET_H #include <QWidget> class PaintWidget : public QWidget { protected: void paintEvent( QPaintEvent *event ); }; #endif // PAINTWIDGET_H
// PaintWidget.cpp #include "PaintWidget.h" #include <QPainter> #include <QFont> void PaintWidget::paintEvent( QPaintEvent *event ) { QPainter painter( this ); // Blauer Text painter.setPen( Qt::blue ); // Schriftart: Monospace, 30 pt und fett painter.setFont( QFont( "Monospace", 30, QFont::Bold ) ); // Zentriert auf das Widget zeichnen painter.drawText( 0, 0, width(), height(), Qt::AlignHCenter | Qt::AlignVCenter, "proggen.org" ); }
Dieser Artikel soll nur einen Überblick über das Konzept der Event-Verarbeitung in Qt bieten. Es gibt noch ein paar andere Events und eine Vielzahl von Methoden die hier nicht besprochen wurden. Genauere Informationen finden sich wie immer in der Dokumentation.