Antworten:
Die Qt-Dokumentation erklärt es wahrscheinlich am besten:
In Qt sind Ereignisse Objekte, die von der abstrakten
QEvent
Klasse abgeleitet sind und Dinge darstellen, die entweder innerhalb einer Anwendung oder als Ergebnis externer Aktivitäten geschehen sind, über die die Anwendung Bescheid wissen muss. Ereignisse können von jeder Instanz einerQObject
Unterklasse empfangen und verarbeitet werden , sind jedoch für Widgets besonders relevant. In diesem Dokument wird beschrieben, wie Ereignisse in einer typischen Anwendung übermittelt und behandelt werden.
Ereignisse und Signal / Slots sind also zwei parallele Mechanismen, die das Gleiche bewirken. Im Allgemeinen wird ein Ereignis von einer externen Entität (z. B. Tastatur oder Mausrad) generiert und über die Ereignisschleife in ausgeliefert QApplication
. Im Allgemeinen generieren Sie keine Ereignisse, es sei denn, Sie richten den Code ein. Sie können sie durch QObject::installEventFilter()
Ereignisse in untergeordneten Objekten filtern oder behandeln, indem Sie die entsprechenden Funktionen überschreiben.
Signale und Slots sind viel einfacher zu generieren und zu empfangen und Sie können zwei beliebige QObject
Unterklassen verbinden. Sie werden über die Metaklasse abgewickelt (weitere Informationen finden Sie in Ihrer Datei moc_classname.cpp), aber der größte Teil der von Ihnen erzeugten Kommunikation zwischen Klassen verwendet wahrscheinlich Signale und Slots. Signale können sofort übermittelt oder über eine Warteschlange zurückgestellt werden (wenn Sie Threads verwenden).
Ein Signal kann erzeugt werden.
In Qt sind Signale und Ereignisse beide Implementierungen des Observer-Musters . Sie werden in unterschiedlichen Situationen eingesetzt, weil sie unterschiedliche Stärken und Schwächen haben.
Zunächst definieren wir genau, was wir unter "Qt-Ereignis" verstehen: eine virtuelle Funktion in einer Qt-Klasse, die Sie voraussichtlich in einer Ihrer Basisklassen erneut implementieren, wenn Sie das Ereignis behandeln möchten. Es hängt mit dem Muster der Vorlagenmethode zusammen .
Beachten Sie, wie ich das Wort " Handle " verwendet habe. In der Tat ist hier ein grundlegender Unterschied zwischen der Absicht von Signalen und Ereignissen:
Der Unterschied besteht darin, dass Sie, wenn Sie das Ereignis "behandeln", die Verantwortung übernehmen, mit einem Verhalten zu "antworten", das außerhalb der Klasse nützlich ist. Stellen Sie sich beispielsweise eine App vor, auf der sich eine Schaltfläche mit einer Nummer befindet. Die App muss den Benutzer die Taste fokussieren lassen und die Nummer durch Drücken der Tastaturtasten "Auf" und "Ab" ändern. Andernfalls sollte die Schaltfläche wie normal funktionieren QPushButton
(sie kann angeklickt werden usw.). In Qt wird dazu eine eigene kleine wiederverwendbare "Komponente" (Unterklasse von QPushButton
) erstellt, die neu implementiert wird 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)
Sehen? Dieser Code enthält eine neue Abstraktion: ein Widget, das sich wie eine Schaltfläche verhält, jedoch einige zusätzliche Funktionen bietet. Wir haben diese Funktionalität sehr bequem hinzugefügt:
keyPressEvent
ein Signal gemacht hätten, müssten wir entscheiden, ob wir das Signal erben QPushButton
oder nur extern verbinden wollen. Aber das wäre dumm, da in Qt immer erwartet wird, dass Sie erben, wenn Sie ein Widget mit einem benutzerdefinierten Verhalten schreiben (aus gutem Grund - Wiederverwendbarkeit / Modularität). Indem sie keyPressEvent
eine Veranstaltung durchführen, vermitteln sie ihre Absicht, keyPressEvent
die nur ein grundlegender Baustein der Funktionalität ist. Wenn es ein Signal wäre, würde es wie eine benutzerbezogene Sache aussehen, wenn es nicht beabsichtigt ist.keyPressEvent
ein Signal wäre.Das Design von Qt ist gut durchdacht - sie haben uns in die Erfolgsgrube geraten lassen, indem sie es einfach gemacht haben, das Richtige und das Falsche zu tun (indem sie keyPressEvent zu einem Ereignis gemacht haben).
Betrachten Sie auf der anderen Seite die einfachste Verwendung von QPushButton
- einfach instanziieren und benachrichtigt werden, wenn darauf geklickt wird :
button = new QPushButton(this)
connect(button, SIGNAL(clicked()), SLOT(sayHello())
Dies ist eindeutig für den Benutzer der Klasse gedacht :
QPushButton
jedes Mal eine Unterklasse erstellen müssten, wenn eine Schaltfläche uns über einen Klick benachrichtigen soll, wären ohne guten Grund viele Unterklassen erforderlich ! Ein Widget, das messagebox
beim Klicken immer eine "Hallo Welt" anzeigt, ist nur in einem einzigen Fall nützlich - es ist also absolut nicht wiederverwendbar. Auch hier haben wir keine andere Wahl, als das Richtige zu tun - indem wir uns extern mit ihm verbinden.clicked()
- oder mehrere Signale anschließen sayHello()
. Mit Signalen gibt es keine Aufregung. Mit Unterklassen müssten Sie sich hinsetzen und über einige Klassendiagramme nachdenken, bis Sie sich für ein geeignetes Design entscheiden.Beachten Sie, dass sich einer der QPushButton
ausgegebenen Orte clicked()
in der mousePressEvent()
Implementierung befindet. Das heißt nicht clicked()
und mousePressEvent()
sind austauschbar - nur dass sie verwandt sind.
Signale und Ereignisse haben also unterschiedliche Zwecke (aber sie hängen damit zusammen, dass Sie beide eine Benachrichtigung über ein Ereignis "abonnieren" können).
Die Antworten gefallen mir bisher nicht. - Lassen Sie mich auf diesen Teil der Frage konzentrieren:
Sind Ereignisse eine Abstraktion von Signalen / Slots?
Kurze Antwort: nein. Die lange Antwort wirft eine „bessere“ Frage auf: Wie hängen Signale und Ereignisse zusammen?
Eine inaktive Hauptschleife (z. B. Qt) steckt normalerweise in einem select () -Aufruf des Betriebssystems fest. Dieser Aufruf lässt die Anwendung "schlafen", während sie eine Reihe von Sockets oder Dateien oder was auch immer an den Kernel weiterleitet und fragt: Wenn sich daran etwas ändert, lassen Sie den Aufruf select () zurückkehren. - Und der Kernel als Meister der Welt weiß, wann das passiert.
Das Ergebnis dieses select () -Aufrufs könnte sein: Neue Daten auf dem Socket verbinden sich mit X11, ein Paket zu einem UDP-Port, den wir abhören, ist eingegangen usw. - Dieses Zeug ist weder ein Qt-Signal noch ein Qt-Ereignis, und das Die Qt-Hauptschleife entscheidet selbst, ob sie die frischen Daten in die eine, die andere umwandelt oder ignoriert.
Qt kann eine Methode (oder mehrere) wie keyPressEvent () aufrufen und so effektiv in ein Qt-Ereignis verwandeln. Oder Qt sendet ein Signal aus, das praktisch alle für dieses Signal registrierten Funktionen nachschlägt und nacheinander aufruft.
Ein Unterschied dieser beiden Konzepte ist hier sichtbar: Ein Slot hat keine Stimme darüber, ob andere Slots, die für dieses Signal registriert sind, angerufen werden oder nicht. - Ereignisse ähneln eher einer Kette, und der Ereignishandler entscheidet, ob er diese Kette unterbricht oder nicht. Signale sehen in dieser Hinsicht wie ein Stern oder ein Baum aus.
Ein Ereignis kann ausgelöst werden oder vollständig in ein Signal umgewandelt werden (senden Sie einfach eines aus und rufen Sie nicht „super ()“ auf). Ein Signal kann in ein Ereignis umgewandelt werden (einen Ereignishandler aufrufen).
Was abstrahiert, was vom Fall abhängt: Das clicked () - Signal abstrahiert Mausereignisse (eine Taste bewegt sich nach unten und oben, ohne sich zu viel zu bewegen). Tastaturereignisse sind Abstraktionen von niedrigeren Ebenen (Dinge wie 果 oder é sind mehrere Tastenanschläge auf meinem System).
Vielleicht ist focusInEvent () ein Beispiel für das Gegenteil: Es könnte das clicked () -Signal verwenden (und damit abstrahieren), aber ich weiß nicht, ob es tatsächlich funktioniert.
Ereignisse werden von der Ereignisschleife ausgelöst. Jedes GUI-Programm benötigt eine Ereignisschleife, unabhängig davon, ob Sie sie unter Windows oder Linux mit Qt, Win32 oder einer anderen GUI-Bibliothek schreiben. Außerdem hat jeder Thread eine eigene Ereignisschleife. In Qt ist "GUI Event Loop" (die Hauptschleife aller Qt-Anwendungen) ausgeblendet, aber Sie starten den Aufruf:
QApplication a(argc, argv);
return a.exec();
Nachrichten, die das Betriebssystem und andere Anwendungen an Ihr Programm senden, werden als Ereignisse gesendet.
Signale und Slots sind Qt-Mechanismen. Bei der Kompilierung mit moc (Metaobjekt-Compiler) werden diese in Rückruffunktionen geändert.
Ereignis sollte einen Empfänger haben, der es versenden sollte. Niemand sonst sollte dieses Ereignis bekommen.
Alle mit dem ausgesendeten Signal verbundenen Steckplätze werden ausgeführt.
Sie sollten Signale nicht als Ereignisse betrachten, denn wie Sie in der Qt-Dokumentation lesen können:
Wenn ein Signal ausgegeben wird, werden die daran angeschlossenen Slots normalerweise sofort ausgeführt, genau wie bei einem normalen Funktionsaufruf. In diesem Fall ist der Signal- und Slot-Mechanismus völlig unabhängig von einer GUI-Ereignisschleife.
Wenn Sie ein Ereignis senden, muss es einige Zeit warten, bis die Ereignisschleife alle zuvor aufgetretenen Ereignisse auslöst. Aus diesem Grund ist die Ausführung des Codes nach dem Senden eines Ereignisses oder Signals unterschiedlich. Der Code nach dem Senden eines Ereignisses wird sofort ausgeführt. Bei den Signal- und Slot-Mechanismen kommt es auf den Verbindungstyp an. Normalerweise wird es nach allen Slots ausgeführt. Mit Qt :: QueuedConnection wird es genau wie Ereignisse sofort ausgeführt. Überprüfen Sie alle Verbindungstypen in der Qt-Dokumentation .
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
Es gibt einen Artikel, in dem die Ereignisverarbeitung ausführlich beschrieben wird: http://www.packtpub.com/article/events-and-signals
Hier wird der Unterschied zwischen Ereignissen und Signalen diskutiert:
Ereignisse und Signale sind zwei parallele Mechanismen, mit denen dasselbe erreicht wird. Im Allgemeinen sind Signale bei der Verwendung eines Widgets nützlich, während Ereignisse bei der Implementierung des Widgets nützlich sind. Wenn wir beispielsweise ein Widget wie QPushButton verwenden, sind wir mehr an seinem clicked () -Signal interessiert als an den Maus- oder Tastendruckereignissen auf niedriger Ebene, die zur Ausgabe des Signals geführt haben. Wenn wir jedoch die QPushButton-Klasse implementieren, sind wir mehr an der Implementierung von Code für Maus- und Schlüsselereignisse interessiert. Außerdem behandeln wir normalerweise Ereignisse, werden jedoch durch Signalemissionen benachrichtigt.
Dies scheint eine übliche Art zu sein, darüber zu sprechen, da die akzeptierte Antwort einige der gleichen Sätze verwendet.
Bitte beachten Sie die hilfreichen Kommentare zu dieser Antwort von Kuba Ober, die mich fragen lassen, ob sie möglicherweise etwas simpel sind.
event
. Die Signale und Slots sind konkret Methoden, während der Verbindungsmechanismus eine Datenstruktur ist, mit der das Signal einen oder mehrere Slots aufrufen kann, die als damit verbunden aufgeführt sind. Ich hoffe, Sie sehen, dass es Unsinn ist, von Signal / Slots als einer "Teilmenge" oder "Variante" von Ereignissen zu sprechen, und umgekehrt. Sie sind wirklich verschiedene Dinge , die passieren zu ähnlichen Enden im Zusammenhang mit einigen Widgets verwendet werden. Das ist es. Je mehr Sie verallgemeinern, desto weniger hilfreich werden Sie IMHO.
TL; DR: Signale und Slots sind indirekte Methodenaufrufe. Ereignisse sind Datenstrukturen. Sie sind also ganz andere Tiere.
Das einzige Mal, wenn sie zusammenkommen, ist, wenn Slot-Aufrufe über Thread-Grenzen hinweg getätigt werden. Die Slot-Call-Argumente werden in eine Datenstruktur gepackt und als Ereignis an die Ereigniswarteschlange des empfangenden Threads gesendet. Im empfangenden Thread QObject::event
entpackt die Methode die Argumente, führt den Aufruf aus und gibt möglicherweise das Ergebnis zurück, wenn es sich um eine blockierende Verbindung handelt.
Wenn wir bereit sind, das Vergessen zu verallgemeinern, könnte man sich Ereignisse als eine Möglichkeit vorstellen, die event
Methode des Zielobjekts aufzurufen . Dies ist nach einer Mode ein indirekter Methodenaufruf - aber ich denke nicht, dass es eine hilfreiche Art ist, darüber nachzudenken, selbst wenn es eine wahre Aussage ist.
Ereignisse (im allgemeinen Sinne der Benutzer- / Netzwerkinteraktion) werden normalerweise in Qt mit Signalen / Slots behandelt, aber Signale / Slots können viele andere Dinge tun.
QEvent und seine Unterklassen sind im Grunde genommen nur kleine standardisierte Datenpakete, mit denen das Framework mit Ihrem Code kommunizieren kann. Wenn Sie auf irgendeine Weise auf die Maus achten möchten, müssen Sie sich nur die QMouseEvent-API ansehen, und die Bibliotheksdesigner müssen das Rad nicht jedes Mal neu erfinden, wenn Sie herausfinden müssen, was die Maus in einer Ecke von getan hat die Qt-API.
Es ist wahr, dass Ihr Slot mit ziemlicher Sicherheit eine QEvent-Unterklasse als Argument akzeptiert, wenn Sie auf Ereignisse warten (im allgemeinen Fall).
Vor diesem Hintergrund können Signale und Slots sicherlich ohne QEvents verwendet werden, obwohl Sie feststellen werden, dass der ursprüngliche Anstoß zur Aktivierung eines Signals häufig eine Art Benutzerinteraktion oder eine andere asynchrone Aktivität ist. Manchmal erreicht Ihr Code jedoch gerade einen Punkt, an dem das Abfeuern eines bestimmten Signals das Richtige ist. Zum Beispiel ein Signal an einen angeschlossenen abzufeuern Fortschrittsbalken während eines langen Prozesses beinhalten keine QEvent bis zu diesem Punkt.
Ich habe diese Frage beim Lesen von 'Event Processing' von Leow Wee Kheng gefunden. Es heißt auch:
Jasmine Blanchette sagt:
Der Hauptgrund, warum Sie Ereignisse anstelle von Standardfunktionsaufrufen oder Signalen und Slots verwenden würden, besteht darin, dass Ereignisse sowohl synchron als auch asynchron verwendet werden können (abhängig davon, ob Sie sendEvent () oder postEvents () aufrufen, während Sie eine Funktion aufrufen oder aufrufen Ein Steckplatz ist immer synchron. Ein weiterer Vorteil von Ereignissen besteht darin, dass sie gefiltert werden können.
Eine weitere kleine pragmatische Überlegung: Das Senden oder Empfangen von Signalen erfordert das Erben, QObject
während ein Objekt jeder Vererbung ein Ereignis posten oder senden kann (seit Sie es aufrufen QCoreApplication.sendEvent()
oder postEvent()
). Dies ist normalerweise kein Problem, aber: um Signale zu verwenden, muss PyQt seltsamerweise QObject
die erste Superklasse sein. und Sie möchten möglicherweise Ihre Vererbungsreihenfolge nicht neu anordnen, nur um Signale senden zu können.)
Meiner Meinung nach sind Ereignisse völlig überflüssig und könnten verworfen werden. Es gibt keinen Grund, warum Signale nicht durch Ereignisse oder Ereignisse durch Signale ersetzt werden könnten, außer dass Qt bereits so eingerichtet ist, wie es ist. Signale in der Warteschlange werden von Ereignissen umschlossen, und Ereignisse können möglicherweise von Signalen umschlossen werden, zum Beispiel:
connect(this, &MyItem::mouseMove, [this](QMouseEvent*){});
Würde die mouseMoveEvent()
in QWidget
(aber nicht QQuickItem
mehr in) enthaltene Komfortfunktion ersetzen und mouseMove
Signale verarbeiten, die ein Szenenmanager für das Objekt ausgeben würde. Die Tatsache, dass das Signal von einer externen Entität im Namen des Elements ausgesendet wird, ist unwichtig und kommt in der Welt der Qt-Komponenten häufig vor, obwohl dies angeblich nicht zulässig ist (Qt-Komponenten umgehen diese Regel häufig). Aber Qt ist ein Konglomerat aus vielen verschiedenen Designentscheidungen und aus Angst, alten Code zu brechen (was sowieso oft genug passiert), ziemlich in Stein gemeißelt.
QObject
, dass sie bei Bedarf die Eltern-Kind-Hierarchie weitergeben. Signal- / Steckplatzverbindungen sind nur ein Versprechen, eine Funktion direkt oder indirekt aufzurufen, wenn eine bestimmte Bedingung erfüllt ist. Es gibt keine zugeordnete Handhabungshierarchie mit Signalen und Slots.
accepted
einem Signal und den Slots, die es verarbeiten , einen Referenzparameter hinzufügen oder eine Struktur als Referenzparameter mit einem accepted
Feld haben. Aber Qt hat die Designentscheidungen getroffen und sie sind jetzt in Stein gemeißelt.