===== Évènements et filtres d'évènements =====
Dans Qt, les évènements sont des objets, dérivés depuis la classe abstraite [[QEvent]], qui représentent des choses qui ont eu lieu soit à l'intérieur d'une application, soit comme résultat d'une activité extérieure à l'application.
==== Introduction ====
Les évènements peuvent être reçus et pris en charge par n'importe quelle instance d'une sous-classe de [[QObject]]. Cela est particulièrement significatif pour les widgets. Ce document décrit la manière dont les évènements sont délivrés et gérés dans le cadre d'une application classique.
* [[#Comment sont fournis les évènements]]
* [[#Types d'évènements]]
* [[#Gestionnaire d'évènements]]
* [[#Filtres d'évènements]]
* [[#L'envoi des évènements]]
==== Comment sont fournis les évènements ====
Quand un évènement a lieu, Qt crée un objet d'évènement pour le représenter en construisant une instance de la sous-classe appropriée de [[QEvent]], et la délivre à une instance particulière de [[QObject]] (ou à l'une de ses sous-classes) en appelant sa fonction [[qobject#event()]].
Cette fonction ne manipule pas l'événement lui-même; en se basant sur le type d'événement délivré, elle appelle un gestionnaire d'évènements pour ce type spécifique d'événement, et envoie une réponse basée sur l'acceptation de l'évènement.
Certains évènements, comme [[QMouseEvent]] et [[QKeyEvent]], proviennent du système de fenêtrage. D'autres, comme [[QTimerEvent]] sont issus de sources différentes ; d'autres encore proviennent de l'application elle-même.
==== Types d'évènements ====
La plupart des types d'évènements ont des classes spéciales, en particulier [[QResizeEvent]], [[QPaintEvent]], [[QMouseEvent]], [[QKeyEvent]], et [[QCloseEvent]]. Chaque classe dérive de [[QEvent]] et y ajoute des fonctions spécifiques à l'évènement. Par exemple, [[QResizeEvent]] ajoute les fonctions [[qresizeevent#size()|size()]] et [[qresizeevent#oldSize()|oldSize()]] qui permettent aux widgets de découvrir comment leurs dimensions ont été modifiées.
Certaines classes supportent plusieurs types d'évènements. [[QMouseEvent]] gère les simples clics, les double clics, les déplacements et les autres opérations apparentées.
Chaque évènement a un type associé, défini dans [[qevent#Type-enum|QEvent::Type]], qui peut être utilisé de façon adéquate comme source de "run-time type information" (RTTI) pour déterminer rapidement à partir de quelle sous-classe un objet d'évènement a été construit.
Dans la mesure où les programmes doivent réagir de façons très variées et complexes, les mécanismes d'envoi d'évènements de Qt sont flexibles. La documentation de [[qcoreapplication#notify|QCoreApplication::notify()]] explique de façon concise la totalité du sujet. L'article //Qt Quarterly// [[qq11-events|Another Look at Events]] l'explique de façon plus détaillée. Ici, nous détaillerons suffisamment les choses pour couvrir 95% des applications.
==== Gestionnaire d'évènements ====
La méthode classique pour délivrer un évènement est l'appel d'une fonction virtuelle. Par exemple, [[QPaintEvent]] est délivré en appelant [[qwidget#paintEvent|QWidget::paintEvent()]]. Cette fonction virtuelle porte la responsabilité de réagir convenablement à cet évènement, normalement en re-peignant le widget. Si vous ne réalisez pas tout le travail nécessaire dans votre implémentation de la fonction virtuelle, il se peut que vous ayez besoin d'appeler l'implémentation de la classe de base.
Par exemple, le code suivant gère les cliques gauches de la souris sur un widget personnalisé de checkbox tout en passant les autres cliques de boutons à la classe [[QCheckBox]] de base :
void MyCheckBox::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
// traiter le clique du bouton gauche de la souris ici
} else {
// propager les autres boutons à la classe de base
QCheckBox::mousePressEvent(event);
}
}
Si vous voulez remplacer la fonction de la classe de base, vous devez implémenter tout vous-même. Cependant, si vous avez juste besoin d'étendre les fonctionnalités de la classe de base, alors il suffit d'implémenter ce dont vous avez besoin et d'appeler la classe de base pour obtenir le comportement par défaut pour tous les cas que vous ne voulez pas gérer.
Dans certains cas, la fonction spécifique à un évènement n'existe pas, ou peut ne pas être suffisante. L'exemple le plus courant concerne les appuis sur la touche **Tab**. Normalement, [[QWidget]] les intercepte pour modifier le focus du clavier mais certains widgets ont besoin de la touche **Tab** pour eux-mêmes.
Ces objets peuvent ré-implémenter le manager d'évènement général [[qobject#event|QObject::event()]] et traiter l'évènement, soit avant, soit après le traitement habituel. Ils peuvent également remplacer complètement la fonction. Un widget très inhabituel qui interprèterait à la fois **Tab** et qui aurait un évènement personnalisé spécifique à l'application pourrait contenir la fonction [[qobject#event|event()]] suivante :
bool MyWidget::event(QEvent *event)
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent *ke = static_cast(event);
if (ke->key() == Qt::Key_Tab) {
// traitement spécial de la touche tab ici
return true;
}
} else if (event->type() == MyCustomEventType) {
MyCustomEvent *myEvent = static_cast(event);
// traitement de l'évènement personnalisé ici
return true;
}
return QWidget::event(event);
}
Il faut noter que [[qwidget#event|QWidget::event()]] est toujours appelée pour les cas non-traités et la valeur de retour indique si l'évènement a été traité avec celle-ci; une valeur à ''true'' évite que l'évènement ne soit envoyé à d'autres objets.
==== Filtres d'évènements ====
Parfois un objet a besoin de regarder, et peut-être intercepter, les évènements qui sont fournis à un autre objet. En particulier, les boites de dialogue vont classiquement filtrer les frappes du clavier pour certains widgets; par exemple, pour modifier la gestion de la touche **Entrée**.
La fonction [[qobject#installEventFilter|QObject::installEventFilter()]] active cela en installant un //filtre d'évènement//, créant ainsi un objet dédié au filtrage, qui recevra les évènements de l'objet cible avec sa fonction [[qobject#eventFilter|QObject::eventFilter()]]. Un filtre d'évènement va traiter les évènements avant que l'objet destinataire ne le fasse. Cela permet au filtre d'inspecter et/ou de retirer les évènements selon les besoins. Un filtre d'évènement existant peut être retiré en utilisant la fonction [[qobject#removeEventFilter|QObject::removeEventFilter()]].
Quand l'implémentation [[qobject#eventFilter|eventFilter()]] de l'objet filtre est appelée, le filtre peut accepter ou rejeter l'évènement et donc autoriser ou refuser des traitements ultérieurs. Si tous les filtres d'évènement autorisent les traitements ultérieurs d'un évènement (en retournant chacun ''false''), l'évènement est envoyé à l'objet destinataire lui-même. Si l'un d'entre eux arrête le traitement de l'évènement (en retournant ''true''), le destinataire et n'importe quel autre filtre d'évènement ne verront pas du tout l'évènement.
bool FilterObject::eventFilter(QObject *object, QEvent *event)
{
if (object == target && event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast(event);
if (keyEvent->key() == Qt::Key_Tab) {
// Traitement spécial de tab
return true;
} else
return false;
}
return false;
}
Le code ci-dessus montre une autre façon d'intercepter les évènements d'appui sur la touche **Tab** envoyés à un widget particulier. Dans ce cas, le filtre gère les évènements pertinents et retourne ''true'' pour éviter leur traitement ultérieur. Tous les autres évènements sont ignorés, et le filtre retourne ''false'' pour autoriser leur envoi au widget destinataire, via n'importe quels autres filtres d'évènement qui leurs seraient appliqués.
Il est également possible de filtrer tous les évènements pour la totalité de l'application en installant un filtre d'évènement sur la [[QApplication]] ou sur l'objet [[QCoreApplication]]. De tels filtres globaux d'évènement sont appelés avant les filtres spécifiques à l'objet. C'est très puissant mais cela ralentit la distribution de l'évènement pour chaque évènement de l'application; les autres techniques présentées doivent généralement être utilisées à la place.
==== L'envoi des évènements ====
Beaucoup d'applications ont besoin de créer et d'envoyer leurs propres évènements. Vous pouvez envoyer des évènements exactement de la même façon que la boucle évènementielle de Qt. Il faut pour cela construire des objets évènements correspondants et les envoyer avec [[qcoreapplication#sendEvent|QCoreApplication::sendEvent()]] et [[qcoreapplication#postEvent|QCoreApplication::postEvent()]].
[[qcoreapplication#sendEvent()]] traite l'évènement immédiatement. Quand la fonction se termine, les filtres d'évènement et/ou l'objet lui-même ont déjà traités l'évènement. Pour de nombreuses classes d'évènement, il existe une fonction nommée isAccepted() qui indique si un évènement a été accepté ou rejeté par le dernier gestionnaire appelé.
[[qcoreapplication#postEvent()]] place l'évènement dans une file pour un envoi ultérieur. Lors de son prochain appel, la boucle évènementielle principale de Qt enverra tous les évènements de la file avec quelques optimisations. Par exemple, dans le cas de plusieurs évènements de redimensionnement, ils seront compressés en un seul. Cela s'applique également aux évènements de dessin : [[qwidget#update|QWidget::update()]] appelle [[qcoreapplication#postEvent()]], ce qui supprime les scintillements et améliore les performances en évitant les repeintes multiples.
[[qcoreapplication#postEvent()]] est également utilisé durant l'initialisation de l'objet. En effet, l'évènement posté sera généralement envoyé très tôt une fois l'initialisation de l'objet terminée. Lors de l'implémentation d'un widget, il est important de comprendre que les évènements sont délivrés très tôt dans leur durée de vie. Il faut donc être sûr, dans le constructeur, d'initialiser les variables membres très tôt, avant qu'il n'y ait la moindre chance qu'un évènement puisse être reçu par l'objet.
Pour créer des évènements de type personnalisé, vous devrez définir un numéro d'évènement qui soit supérieur à [[qevent#Type-enum|QEvent::User]]. Vous pouvez également être amené à dériver [[QEvent]] pour passer des informations spécifiques concernant votre évènement personnalisé. Voir la documentation de [[QEvent]] pour plus de détails.
==== Remerciements ====
Merci à pour la traduction et à ainsi qu'à pour leur relecture !