Réponses:
La documentation Qt l' explique probablement le mieux:
Dans Qt, les événements sont des objets, dérivés de la
QEvent
classe abstraite , qui représentent des choses qui se sont produites soit au sein d'une application, soit à la suite d'une activité extérieure que l'application doit connaître. Les événements peuvent être reçus et gérés par n'importe quelle instance d'uneQObject
sous - classe, mais ils sont particulièrement pertinents pour les widgets. Ce document décrit comment les événements sont livrés et traités dans une application typique.
Ainsi, les événements et les signaux / créneaux sont deux mécanismes parallèles qui accomplissent les mêmes choses. En général, un événement sera généré par une entité extérieure (par exemple, le clavier ou la molette de la souris) et sera diffusé via la boucle d'événements dans QApplication
. En général, à moins que vous ne configuriez le code, vous ne générerez pas d'événements. Vous pouvez les filtrer QObject::installEventFilter()
ou gérer les événements dans un objet sous-classé en remplaçant les fonctions appropriées.
Les signaux et les slots sont beaucoup plus faciles à générer et à recevoir et vous pouvez connecter deux QObject
sous-classes quelconques . Ils sont gérés via la Metaclass (consultez votre fichier moc_classname.cpp pour en savoir plus), mais la plupart des communications interclasses que vous produirez utiliseront probablement des signaux et des slots. Les signaux peuvent être livrés immédiatement ou différés via une file d'attente (si vous utilisez des threads).
Un signal peut être généré.
Dans Qt, les signaux et les événements sont tous deux des implémentations du modèle Observer . Ils sont utilisés dans différentes situations car ils ont des forces et des faiblesses différentes.
Tout d'abord, définissons exactement ce que nous entendons par «événement Qt»: une fonction virtuelle dans une classe Qt, que vous êtes censé réimplémenter dans une de vos classes de base si vous souhaitez gérer l'événement. Il est lié au modèle de méthode de modèle .
Notez comment j'ai utilisé le mot « poignée ». En effet, voici une différence fondamentale entre l'intention des signaux et des événements:
La différence est que lorsque vous «gérez» l'événement, vous assumez la responsabilité de «répondre» avec un comportement utile en dehors de la classe. Par exemple, considérons une application qui a un bouton avec un numéro dessus. L'application doit permettre à l'utilisateur de se concentrer sur le bouton et de modifier le numéro en appuyant sur les touches du clavier «haut» et «bas». Sinon, le bouton devrait fonctionner normalement QPushButton
(il peut être cliqué, etc.). Dans Qt, cela se fait en créant votre propre petit "composant" réutilisable (sous-classe de QPushButton
), qui se réimplémente QWidget::keyPressEvent
. Pseudocode:
class NumericButton extends QPushButton
private void addToNumber(int value):
// ...
reimplement base.keyPressEvent(QKeyEvent event):
if(event.key == up)
this.addToNumber(1)
else if(event.key == down)
this.addToNumber(-1)
else
base.keyPressEvent(event)
Voir? Ce code présente une nouvelle abstraction: un widget qui agit comme un bouton, mais avec quelques fonctionnalités supplémentaires. Nous avons ajouté cette fonctionnalité très facilement:
keyPressEvent
un signal, nous devrons décider s'il faut hériter QPushButton
ou simplement se connecter extérieurement au signal. Mais ce serait stupide, car dans Qt on s'attend toujours à ce que vous héritiez lors de l'écriture d'un widget avec un comportement personnalisé (pour une bonne raison - réutilisabilité / modularité). Ainsi, en créant keyPressEvent
un événement, ils transmettent leur intention qui keyPressEvent
n'est qu'un élément de base de la fonctionnalité. S'il s'agissait d'un signal, cela ressemblerait à une chose face à l'utilisateur, alors que ce n'est pas prévu.keyPressEvent
s'agissait d'un signal.La conception de Qt est bien pensée - ils nous ont fait tomber dans le gouffre du succès en rendant facile de faire la bonne chose et difficile de faire la mauvaise chose (en faisant de keyPressEvent un événement).
D'un autre côté, considérez l'utilisation la plus simple de QPushButton
- simplement l'instancier et être notifié quand on clique dessus :
button = new QPushButton(this)
connect(button, SIGNAL(clicked()), SLOT(sayHello())
Ceci est clairement destiné à être fait par l' utilisateur de la classe:
QPushButton
chaque fois que nous voulons qu'un bouton nous avertisse d'un clic, cela nécessiterait beaucoup de sous-classes sans raison valable! Un widget qui affiche toujours un "Hello world" messagebox
lorsque vous cliquez dessus n'est utile que dans un seul cas - il n'est donc absolument pas réutilisable. Encore une fois, nous n'avons pas d'autre choix que de faire la bonne chose - en nous y connectant de l'extérieur.clicked()
- ou connecter plusieurs signaux à sayHello()
. Avec les signaux, il n'y a pas de problème. Avec le sous-classement, vous devrez vous asseoir et réfléchir à certains diagrammes de classe jusqu'à ce que vous décidiez d'une conception appropriée.Notez que l'un des lieux QPushButton
émet clicked()
est dans son mousePressEvent()
implémentation. Cela ne veut pas dire clicked()
et mousePressEvent()
sont interchangeables - juste qu'ils sont liés.
Ainsi, les signaux et les événements ont des objectifs différents (mais sont liés en ce que les deux vous permettent de «vous abonner» à une notification de quelque chose qui se passe).
Je n'aime pas les réponses pour l'instant. - Permettez-moi de me concentrer sur cette partie de la question:
Les événements sont-ils une abstraction du signal / des créneaux?
Réponse courte: non. La longue réponse soulève une «meilleure» question: comment les signaux et les événements sont-ils liés?
Une boucle principale inactive (Qt par exemple) est généralement "bloquée" dans un appel select () du système d'exploitation. Cet appel fait «dormir» l'application, alors qu'elle passe un tas de sockets ou de fichiers ou autre au noyau demandant: si quelque chose change sur ceux-ci, laissez l'appel select () revenir. - Et le noyau, en tant que maître du monde, sait quand cela se produit.
Le résultat de cet appel à select () pourrait être: de nouvelles données sur le socket se connectent à X11, un paquet vers un port UDP sur lequel nous écoutons est entré, etc. - Ce truc n'est ni un signal Qt, ni un événement Qt, et le La boucle principale Qt décide elle-même si elle transforme les nouvelles données en l'une, l'autre ou l'ignore.
Qt pourrait appeler une méthode (ou plusieurs) comme keyPressEvent (), la transformant effectivement en événement Qt. Ou Qt émet un signal, qui en effet recherche toutes les fonctions enregistrées pour ce signal et les appelle l'une après l'autre.
Une différence de ces deux concepts est visible ici: un slot n'a pas de vote pour savoir si d'autres slots enregistrés sur ce signal seront appelés ou non. - Les événements ressemblent plus à une chaîne et le gestionnaire d'événements décide s'il interrompt cette chaîne ou non. Les signaux ressemblent à une étoile ou à un arbre à cet égard.
Un événement peut se déclencher ou être entièrement transformé en signal (n'en émettez qu'un seul et n'appelez pas «super ()»). Un signal peut être transformé en événement (appelez un gestionnaire d'événements).
Ce qui résume ce qui dépend du cas: le signal clicked () - résume les événements de la souris (un bouton descend et remonte sans trop bouger). Les événements de clavier sont des abstractions de niveaux inférieurs (des choses comme 果 ou é sont plusieurs touches sur mon système).
Peut-être que focusInEvent () est un exemple du contraire: il pourrait utiliser (et donc abstrait) le signal clicked (), mais je ne sais pas si c'est le cas.
Les événements sont distribués par la boucle d'événements. Chaque programme GUI a besoin d'une boucle d'événements, quoi que vous l'écriviez sous Windows ou Linux, en utilisant Qt, Win32 ou toute autre bibliothèque GUI. De plus, chaque thread a sa propre boucle d'événements. Dans Qt, "GUI Event Loop" (qui est la boucle principale de toutes les applications Qt) est masquée, mais vous la lancez en appelant:
QApplication a(argc, argv);
return a.exec();
Les messages que le système d'exploitation et les autres applications envoient à votre programme sont envoyés sous forme d'événements.
Les signaux et les slots sont des mécanismes Qt. Dans le processus de compilations utilisant moc (compilateur de méta-objets), ils sont modifiés en fonctions de rappel.
L'événement doit avoir un récepteur, qui doit le distribuer. Personne d'autre ne devrait avoir cet événement.
Tous les slots connectés au signal émis seront exécutés.
Vous ne devriez pas considérer les signaux comme des événements, car comme vous pouvez le lire dans la documentation Qt:
Lorsqu'un signal est émis, les slots qui lui sont connectés sont généralement exécutés immédiatement, tout comme un appel de fonction normal. Lorsque cela se produit, le mécanisme des signaux et des slots est totalement indépendant de toute boucle d'événement GUI.
Lorsque vous envoyez un événement, il doit attendre un certain temps jusqu'à ce que la boucle d'événements distribue tous les événements qui sont venus plus tôt. Pour cette raison, l'exécution du code après l'envoi d'un événement ou d'un signal est différente. Le code suivant l'envoi d'un événement sera exécuté immédiatement. Avec les mécanismes de signaux et de slots, cela dépend du type de connexion. Normalement, il sera exécuté après tous les emplacements. En utilisant Qt :: QueuedConnection, il sera exécuté immédiatement, tout comme les événements. Vérifiez tous les types de connexion dans la documentation Qt .
When you send an event, it must wait for time when event loop dispatch all events that came earlier. Because of this, execution of the cod after sending event or signal is different
Il existe un article qui traite du traitement des événements en détail: http://www.packtpub.com/article/events-and-signals
Il discute de la différence entre les événements et les signaux ici:
Les événements et les signaux sont deux mécanismes parallèles utilisés pour accomplir la même chose. En règle générale, les signaux sont utiles lors de l'utilisation d'un widget, tandis que les événements sont utiles lors de l'implémentation du widget. Par exemple, lorsque nous utilisons un widget comme QPushButton, nous sommes plus intéressés par son signal clicked () que par les événements de bas niveau de pression de la souris ou de touche qui ont provoqué l'émission du signal. Mais si nous implémentons la classe QPushButton, nous sommes plus intéressés par l'implémentation de code pour les événements de souris et de touches. En outre, nous gérons généralement les événements, mais nous sommes notifiés par des émissions de signaux.
Cela semble être une façon courante d'en parler, car la réponse acceptée utilise certaines des mêmes phrases.
Remarque, s'il vous plaît voir les commentaires utiles ci-dessous sur cette réponse de Kuba Ober, qui me font me demander si cela pourrait être un peu simpliste.
event
. Les signaux et slots, sont, concrètement , des méthodes, tandis que le mécanisme de connexion est une structure de données qui permet au signal d'invoquer un ou plusieurs slots listés comme connectés à lui. J'espère que vous voyez que parler de signal / slots comme un "sous-ensemble" ou une "variante" d'événements est absurde, et vice versa. Ils sont vraiment différentes choses qui arrivent à être utilisées à des fins similaires dans le contexte de certains widgets. C'est tout. Plus vous généralisez, moins vous devenez utile à mon humble avis.
TL; DR: les signaux et les slots sont des appels de méthode indirects. Les événements sont des structures de données. Ce sont donc des animaux très différents.
Le seul moment où ils se rejoignent est lorsque les appels d'emplacement sont effectués au-delà des limites de thread. Les arguments d'appel d'emplacement sont regroupés dans une structure de données et sont envoyés en tant qu'événement à la file d'attente d'événements du thread récepteur. Dans le thread de réception, la QObject::event
méthode décompresse les arguments, exécute l'appel et retourne éventuellement le résultat s'il s'agissait d'une connexion bloquante.
Si nous voulons généraliser à l'oubli, on pourrait considérer les événements comme un moyen d'invoquer la event
méthode de l'objet cible . C'est un appel de méthode indirect, en quelque sorte - mais je ne pense pas que ce soit une façon utile d'y penser, même si c'est une vraie déclaration.
Les événements (dans un sens général d'interaction utilisateur / réseau) sont généralement traités dans Qt avec des signaux / slots, mais les signaux / slots peuvent faire beaucoup d'autres choses.
QEvent et ses sous-classes ne sont en fait que de petits paquets de données standardisés permettant au framework de communiquer avec votre code. Si vous voulez faire attention à la souris d'une manière ou d'une autre, il vous suffit de regarder l'API QMouseEvent, et les concepteurs de la bibliothèque n'ont pas à réinventer la roue à chaque fois que vous devez comprendre ce que la souris a fait dans un coin de l'API Qt.
Il est vrai que si vous attendez des événements (encore une fois dans le cas général) d'une certaine sorte, votre slot acceptera presque certainement une sous-classe QEvent comme argument.
Cela dit, les signaux et les slots peuvent certainement être utilisés sans QEvents, bien que vous constatiez que l'impulsion initiale pour activer un signal sera souvent une sorte d'interaction utilisateur ou une autre activité asynchrone. Parfois, cependant, votre code atteindra juste un point où déclencher un certain signal sera la bonne chose à faire. Par exemple, le déclenchement d'un signal connecté à une barre de progression pendant un long processus n'implique pas jusqu'à ce point un QEvent.
J'ai trouvé cette question en lisant «Traitement des événements» par Leow Wee Kheng. Il dit aussi:
Jasmine Blanchette dit:
La principale raison pour laquelle vous utiliseriez des événements plutôt que des appels de fonction standard, ou des signaux et des slots, est que les événements peuvent être utilisés à la fois de manière synchrone et asynchrone (selon que vous appelez sendEvent () ou postEvents ()), tout en appelant une fonction ou en invoquant un slot est toujours synchrone. Un autre avantage des événements est qu'ils peuvent être filtrés.
Autre considération pragmatique mineure: émettre ou recevoir des signaux nécessite d'hériter QObject
alors qu'un objet de n'importe quel héritage peut poster ou envoyer un événement (puisque vous invoquez QCoreApplication.sendEvent()
ou postEvent()
) Ce n'est généralement pas un problème mais: pour utiliser des signaux PyQt nécessite étrangement QObject
d'être la première super classe, et vous ne voudrez peut-être pas réorganiser votre ordre d'héritage juste pour pouvoir envoyer des signaux.)
À mon avis, les événements sont complètement redondants et pourraient être rejetés. Il n'y a aucune raison pour que les signaux ne puissent pas être remplacés par des événements ou des événements par des signaux, sauf que Qt est déjà configuré tel quel. Les signaux en file d'attente sont enveloppés par des événements et les événements pourraient éventuellement être enveloppés par des signaux, par exemple:
connect(this, &MyItem::mouseMove, [this](QMouseEvent*){});
Remplacerait la mouseMoveEvent()
fonction de commodité trouvée dans QWidget
(mais plus QQuickItem
maintenant) et gérerait les mouseMove
signaux qu'un gestionnaire de scène émettrait pour l'élément. Le fait que le signal soit émis au nom de l'élément par une entité extérieure est sans importance et se produit assez souvent dans le monde des composants Qt, même si cela n'est pas autorisé (les composants Qt contournent souvent cette règle). Mais Qt est un conglomérat de nombreuses décisions de conception différentes et à peu près coulé dans la pierre de peur de casser l'ancien code (ce qui arrive assez souvent de toute façon).
QObject
hiérarchie parent-enfant, si nécessaire. Les connexions signal / créneau ne sont qu'une promesse d'appeler une fonction, directement ou indirectement, lorsqu'une certaine condition est remplie. Il n'y a pas de hiérarchie de traitement associée avec les signaux et les slots.
accepted
paramètre de référence à un signal et aux slots le traitant ou vous pouvez avoir une structure comme paramètre de référence avec un accepted
champ. Mais Qt a fait les choix de conception qu'il a faits et ils sont désormais gravés dans la pierre.