Das Ereignissystem

In Qt sind Ereignisse Objekte, die von der abstrakten Klasse QEvent abgeleitet sind. Sie repräsentieren Dinge, die entweder innerhalb einer Anwendung oder als Ergebnis einer externen Aktivität geschehen sind und über die die Anwendung informiert werden muss. Ereignisse können von jeder Instanz einer QObject Unterklasse empfangen und verarbeitet werden, aber sie sind besonders relevant für Widgets. In diesem Dokument wird beschrieben, wie Ereignisse in einer typischen Anwendung geliefert und behandelt werden.

Wie Ereignisse ausgeliefert werden

Wenn ein Ereignis eintritt, erstellt Qt ein Ereignisobjekt, um es zu repräsentieren, indem eine Instanz der entsprechenden QEvent Unterklasse konstruiert wird, und übergibt es an eine bestimmte Instanz von QObject (oder eine ihrer Unterklassen), indem die Funktion event() aufgerufen wird.

Diese Funktion behandelt nicht das Ereignis selbst; basierend auf dem Typ des übermittelten Ereignisses ruft sie einen Event-Handler für diesen speziellen Ereignistyp auf und sendet eine Antwort, je nachdem, ob das Ereignis akzeptiert oder ignoriert wurde.

Einige Ereignisse, wie z. B. QMouseEvent und QKeyEvent, kommen vom Fenstersystem, andere, wie z. B. QTimerEvent, kommen von anderen Quellen, wieder andere kommen von der Anwendung selbst.

Ereignistypen

Die meisten Ereignistypen haben spezielle Klassen, vor allem QResizeEvent, QPaintEvent, QMouseEvent, QKeyEvent und QCloseEvent. Jede Klasse ist eine Unterklasse von QEvent und fügt ereignisspezifische Funktionen hinzu. Zum Beispiel fügt QResizeEvent size () und oldSize() hinzu, damit Widgets feststellen können, wie sich ihre Abmessungen geändert haben.

Einige Klassen unterstützen mehr als einen Ereignistyp. QMouseEvent unterstützt das Drücken von Maustasten, Doppelklicks, Verschiebungen und andere verwandte Operationen.

Jedes Ereignis hat einen zugehörigen Typ, der in QEvent::Type definiert ist, und dieser kann als bequeme Quelle für Laufzeit-Typinformationen verwendet werden, um schnell festzustellen, aus welcher Unterklasse ein bestimmtes Ereignisobjekt konstruiert wurde.

Da Programme in vielfältiger und komplexer Weise reagieren müssen, sind die Qt-Ereignisübertragungsmechanismen flexibel. Die Dokumentation für QCoreApplication::notify() erzählt die ganze Geschichte kurz und bündig; der Qt Quarterly Artikel Another Look at Events fasst sie weniger kurz zusammen. Hier werden wir genug für 95% der Anwendungen erklären.

Ereignishandler

Normalerweise wird ein Ereignis durch den Aufruf einer virtuellen Funktion ausgelöst. Zum Beispiel wird QPaintEvent durch den Aufruf von QWidget::paintEvent() ausgelöst. Diese virtuelle Funktion ist für eine angemessene Reaktion verantwortlich, normalerweise durch Neuzeichnen des Widgets. Wenn Sie nicht alle erforderlichen Arbeiten in Ihrer Implementierung der virtuellen Funktion durchführen, müssen Sie möglicherweise die Implementierung der Basisklasse aufrufen.

Der folgende Code behandelt beispielsweise Klicks mit der linken Maustaste auf ein benutzerdefiniertes Kontrollkästchen-Widget, während er alle anderen Klicks an die Basisklasse QCheckBox weiterleitet:

void MyCheckBox::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        // handle left mouse button here
    } else {
        // pass on other buttons to base class
        QCheckBox::mousePressEvent(event);
    }
}

Wenn Sie die Funktion der Basisklasse ersetzen wollen, müssen Sie alles selbst implementieren. Wenn Sie jedoch nur die Funktionalität der Basisklasse erweitern wollen, dann implementieren Sie, was Sie wollen, und rufen die Basisklasse auf, um das Standardverhalten für alle Fälle zu erhalten, die Sie nicht behandeln wollen.

Gelegentlich gibt es keine solche ereignisspezifische Funktion, oder die ereignisspezifische Funktion ist nicht ausreichend. Das häufigste Beispiel ist das Drücken der Tabulatortaste. Normalerweise fängt QWidget diese ab, um den Tastaturfokus zu verschieben, aber einige Widgets benötigen die Tabulator-Taste für sich selbst.

Diese Objekte können QObject::event(), die allgemeine Ereignisbehandlung, neu implementieren und ihre Ereignisbehandlung entweder vor oder nach der üblichen Behandlung durchführen, oder sie können die Funktion vollständig ersetzen. Ein sehr ungewöhnliches Widget, das sowohl die Tabulatortaste interpretiert als auch ein anwendungsspezifisches Ereignis hat, könnte die folgende event()-Funktion enthalten:

bool MyWidget::event(QEvent *event)
{
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent *ke = static_cast<QKeyEvent *>(event);
        if (ke->key() == Qt::Key_Tab) {
            // special tab handling here
            return true;
        }
    } else if (event->type() == MyCustomEventType) {
        MyCustomEvent *myEvent = static_cast<MyCustomEvent *>(event);
        // custom event handling here
        return true;
    }

    return QWidget::event(event);
}

Beachten Sie, dass QWidget::event() immer noch für alle nicht behandelten Fälle aufgerufen wird und dass der Rückgabewert angibt, ob ein Ereignis behandelt wurde; ein true Wert verhindert, dass das Ereignis an andere Objekte weitergegeben wird.

Ereignis-Filter

Manchmal muss ein Objekt die Ereignisse, die an ein anderes Objekt geliefert werden, betrachten und möglicherweise abfangen. Zum Beispiel wollen Dialoge häufig Tastendrücke für einige Widgets filtern; zum Beispiel, um die Handhabung der Return-Taste zu ändern.

Die Funktion QObject::installEventFilter() ermöglicht dies, indem sie einen Ereignisfilter einrichtet, der ein benanntes Filterobjekt dazu veranlasst, die Ereignisse für ein Zielobjekt in seiner Funktion QObject::eventFilter() zu empfangen. Ein Ereignisfilter kann Ereignisse vor dem Zielobjekt verarbeiten, so dass er die Ereignisse bei Bedarf prüfen und verwerfen kann. Ein vorhandener Ereignisfilter kann mit der Funktion QObject::removeEventFilter() entfernt werden.

Wenn die Implementierung eventFilter() des Filterobjekts aufgerufen wird, kann sie das Ereignis annehmen oder ablehnen und die weitere Verarbeitung des Ereignisses erlauben oder verweigern. Wenn alle Ereignisfilter die weitere Verarbeitung eines Ereignisses erlauben (indem sie jeweils false zurückgeben), wird das Ereignis an das Zielobjekt selbst gesendet. Wenn einer von ihnen die Verarbeitung stoppt (indem er true zurückgibt), bekommen das Zielobjekt und alle späteren Ereignisfilter das Ereignis überhaupt nicht zu sehen.

bool FilterObject::eventFilter(QObject *object, QEvent *event)
{
    if (object == target && event->type() == QEvent::KeyPress) {
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
        if (keyEvent->key() == Qt::Key_Tab) {
            // Special tab handling
            return true;
        } else
            return false;
    }
    return false;
}

Der obige Code zeigt eine weitere Möglichkeit, Ereignisse zum Drücken der Tabulatortaste abzufangen, die an ein bestimmtes Ziel-Widget gesendet werden. In diesem Fall behandelt der Filter die relevanten Ereignisse und gibt true zurück, damit sie nicht weiter verarbeitet werden. Alle anderen Ereignisse werden ignoriert, und der Filter gibt false zurück, um zu ermöglichen, dass sie über andere Ereignisfilter, die auf dem Ziel-Widget installiert sind, an dieses gesendet werden.

Es ist auch möglich, alle Ereignisse für die gesamte Anwendung zu filtern, indem ein Ereignisfilter für das Objekt QApplication oder QCoreApplication installiert wird. Solche globalen Ereignisfilter werden vor den objektspezifischen Filtern aufgerufen. Dies ist sehr leistungsfähig, verlangsamt aber auch die Ereigniszustellung jedes einzelnen Ereignisses in der gesamten Anwendung; stattdessen sollten in der Regel die anderen besprochenen Techniken verwendet werden.

Senden von Ereignissen

Viele Anwendungen möchten ihre eigenen Ereignisse erstellen und senden. Sie können Ereignisse auf genau die gleiche Weise wie die Qt-eigene Ereignisschleife senden, indem Sie geeignete Ereignisobjekte konstruieren und sie mit QCoreApplication::sendEvent() und QCoreApplication::postEvent() senden.

sendEvent() verarbeitet das Ereignis sofort. Wenn es zurückkehrt, haben die Ereignisfilter und/oder das Objekt selbst das Ereignis bereits verarbeitet. Für viele Ereignisklassen gibt es eine Funktion namens isAccepted(), die Ihnen mitteilt, ob das Ereignis vom letzten Handler, der aufgerufen wurde, angenommen oder abgelehnt wurde.

postEvent() stellt das Ereignis in eine Warteschlange zur späteren Verarbeitung. Wenn die Haupt-Ereignisschleife von Qt das nächste Mal ausgeführt wird, werden alle geposteten Ereignisse mit einigen Optimierungen versendet. Wenn es zum Beispiel mehrere Resize-Ereignisse gibt, werden sie zu einem komprimiert. Dasselbe gilt für Malereignisse: QWidget::update() ruft postEvent() auf, was ein Flackern verhindert und die Geschwindigkeit erhöht, indem es ein mehrfaches Neufärben vermeidet.

postEvent() wird auch während der Objektinitialisierung verwendet, da das gebuchte Ereignis in der Regel sehr bald nach Abschluss der Initialisierung des Objekts ausgelöst wird. Bei der Implementierung eines Widgets ist es wichtig zu wissen, dass Ereignisse sehr früh in seiner Lebensdauer ausgelöst werden können. Stellen Sie daher sicher, dass die Mitgliedsvariablen in seinem Konstruktor früh initialisiert werden, bevor die Möglichkeit besteht, dass es ein Ereignis erhält.

Um Ereignisse eines benutzerdefinierten Typs zu erstellen, müssen Sie eine Ereignisnummer definieren, die größer als QEvent::User sein muss, und Sie müssen möglicherweise eine Unterklasse QEvent erstellen, um spezifische Informationen über Ihr benutzerdefiniertes Ereignis zu übergeben. Weitere Einzelheiten finden Sie in der Dokumentation QEvent.

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.