====== Events verarbeiten (Event-Handler) ====== 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. Sie bekommen ein Objekt mit Informationen über das zu verarbeitende Event 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. ===== Close-Events ===== 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. #include #include "TextEdit.h" 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(); } #ifndef TEXTEDIT_H #define TEXTEDIT_H #include #include #include class TextEdit : public QTextEdit { Q_OBJECT protected: void closeEvent( QCloseEvent *event ); // Close-Event von QTextEdit überschreiben }; #endif #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(); } } {{:frameworks:qt:gui:basic:close.png?|}} ===== Tastatur-Events ===== 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. #ifndef TEXTEDIT_H #define TEXTEDIT_H #include #include #include #include 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 #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. ===== Maus-Events ===== Im Unterschied zu den bisherigen Events gibt es zur Behandlung von Maus-Events mehrere Methoden und Objekte: * Mausbewegung: ''mouseMoveEvent()'' * Maustasten (jeweils für Drücken und Loslassen): ''mousePressEvent()'', ''mouseReleaseEvent()'' * Doppelklick: ''mouseDoubleClickEvent()'' * Mausrad: ''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: #include #include "MouseInfo.h" int main( int argc, char *argv[] ) { QApplication app( argc, argv ); MouseInfo m; m.show(); return app.exec(); } #ifndef MOUSEINFO_H #define MOUSEINFO_H #include #include #include 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 #include "MouseInfo.h" #include MouseInfo::MouseInfo() { resize( 400, 400 ); } void MouseInfo::mouseMoveEvent( QMouseEvent *event ) { qDebug() << event; } void MouseInfo::mousePressEvent( QMouseEvent *event ) { qDebug() << event; } void MouseInfo::mouseReleaseEvent( QMouseEvent *event ) { qDebug() << event; } void MouseInfo::mouseDoubleClickEvent( QMouseEvent *event ) { qDebug() << event; } void MouseInfo::wheelEvent( QWheelEvent *event ) { qDebug() << event; } ===== Paint-Events ===== Qt bietet uns bereits eine Vielzahl an fertigen Widgets, aber wir können 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'' ermöglicht es uns, in wenigen Zeilen Code simple 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 ==== 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 ); ==== Primitive Formen zeichnen ==== ''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: #include #include "PaintWidget.h" 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(); } #ifndef PAINTWIDGET_H #define PAINTWIDGET_H #include class PaintWidget : public QWidget { protected: void paintEvent( QPaintEvent *event ); }; #endif // PAINTWIDGET_H #include "PaintWidget.h" #include 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() ); } {{:frameworks:qt:gui:basic:paint.png?|}} ==== Text auf Widgets zeichnen ==== 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: #include #include "PaintWidget.h" 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(); } #ifndef PAINTWIDGET_H #define PAINTWIDGET_H #include class PaintWidget : public QWidget { protected: void paintEvent( QPaintEvent *event ); }; #endif // PAINTWIDGET_H #include "PaintWidget.h" #include #include 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" ); } {{:frameworks:qt:gui:basic:text.png?|}} ===== Abschließendes ===== 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 [[https://doc.qt.io/|Dokumentation]].