it-swarm.com.de

Qt-Ereignisse und Signal/Slots

Was ist in der Qt-Welt der Unterschied zwischen Ereignissen und Signalen/Slots? 

Ersetzt einer den anderen? Sind Ereignisse eine Abstraktion von Signalen/Slots?

78
Raphael

Die Qt-Dokumentation erklärt es wahrscheinlich am besten:

In Qt sind Ereignisse abgeleitete Objekte aus der abstrakten QEvent-Klasse das repräsentieren Dinge, die geschehen sind entweder innerhalb einer Anwendung oder als Ergebnis äußerer Aktivität, dass die Anwendung muss über ..__ wissen. Ereignisse können mit .__ empfangen und bearbeitet werden. jede Instanz einer QObject-Unterklasse, Sie sind jedoch besonders relevant für Widgets Dieses Dokument beschreibt, wie Ereignisse werden ausgeliefert und in einer .__ behandelt. typische anwendung.

Events und Signale/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 QApplication übermittelt. Wenn Sie den Code nicht einrichten, generieren Sie im Allgemeinen keine Ereignisse. Sie können sie durch QObject::installEventFilter() filtern oder Ereignisse in untergeordneten Objekten 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 anschließen. Sie werden über die Metaclass behandelt (sehen Sie dazu in Ihrer moc_classname.cpp-Datei nach), aber die meiste Interclass-Kommunikation, die Sie erzeugen werden, verwendet wahrscheinlich Signale und Slots. Signale können sofort geliefert oder über eine Warteschlange zurückgestellt werden (wenn Sie Threads verwenden).

Ein Signal kann erzeugt werden.

27

In Qt sind Signale und Ereignisse beide Implementierungen des Beobachter-Musters . Sie werden in unterschiedlichen Situationen eingesetzt, weil sie unterschiedliche Stärken und Schwächen haben.

Definieren wir zunächst genau, was wir unter "Qt-Ereignis" verstehen: eine virtuelle Funktion in einer Qt-Klasse, die Sie in einer Ihrer Basisklassen erneut implementieren müssen, wenn Sie das Ereignis behandeln möchten. Es ist verwandt mit dem Template Method pattern .

Beachten Sie, wie ich das Wort " handle " verwendet habe. In der Tat ist hier ein grundlegender Unterschied zwischen der Absicht von Signalen und Ereignissen:

  • Sie " behandeln " Ereignisse
  • Sie werden " über " Signalemissionen benachrichtigt

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, die eine Schaltfläche mit einer Nummer enthält. Die App muss den Benutzer die Schaltfläche fokussieren lassen und die Nummer durch Drücken der Tasten "Auf" und "Ab" ändern. Andernfalls sollte die Schaltfläche wie ein normales QPushButton funktionieren (sie kann angeklickt werden usw.). In Qt wird dazu eine eigene kleine wiederverwendbare "Komponente" (Unterklasse von QPushButton) erstellt, die QWidget::keyPressEvent neu implementiert. 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 stellt eine neue Abstraktion dar: Ein Widget, das wie eine Schaltfläche funktioniert, aber einige zusätzliche Funktionen bietet. Wir haben diese Funktionalität sehr bequem hinzugefügt:

  • Da wir eine virtuelle Version neu implementiert haben, wurde unsere Implementierung automatisch in unsere Klasse eingeschlossen. Wenn die Qt-Designer keyPressEvent zu einem Signal gemacht hätten, müssten wir entscheiden, ob wir QPushButton erben oder nur eine externe Verbindung zum Signal herstellen. 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 zu einem Ereignis machen, vermitteln sie ihre Absicht, dass keyPressEvent nur ein grundlegender Baustein der Funktionalität ist. Wenn es ein Signal wäre, würde es wie eine Sache mit Blick auf den Benutzer aussehen, wenn es nicht beabsichtigt ist.
  • Da die Implementierung der Funktion in der Basisklasse verfügbar ist, können wir das Chain-of-Responsibility-Muster einfach implementieren, indem wir unsere Sonderfälle (Auf- und Ab-Tasten) behandeln und den Rest der Basisklasse überlassen. Sie können sehen, dass dies nahezu unmöglich wäre, wenn keyPressEvent ein Signal wäre.

Das Design von Qt ist gut durchdacht - sie ließen uns in die Erfolgsgrube fallen, indem sie es einfach machten, das Richtige und das Falsche zu tun (indem sie keyPressEvent machten) ein Event).

Betrachten Sie andererseits die einfachste Verwendung von QPushButton - , indem Sie es nur instanziieren und benachrichtigt werden, wenn es angeklickt wird :

button = new QPushButton(this)
connect(button, SIGNAL(clicked()), SLOT(sayHello())

Dies ist eindeutig für den Benutzer der Klasse vorgesehen:

  • wenn wir jedes Mal, wenn wir möchten, dass ein Button uns über einen Klick benachrichtigt, QPushButton unterordnen müssten, würde dies ohne guten Grund eine Menge Unterklassen erfordern! Ein Widget, das zeigt immer eine "Hallo Welt" messagebox an, wenn geklickt wird, ist dies nur in einem Einzelfall sinnvoll - es ist also absolut nicht wiederverwendbar. Auch hier haben wir keine andere Wahl, als das Richtige zu tun - indem wir eine externe Verbindung herstellen.
  • möglicherweise möchten wir mehrere Steckplätze mit clicked() verbinden - oder mehrere Signale mit sayHello() verbinden. Mit Signalen gibt es keine Aufregung. Bei Unterklassen müssten Sie sich hinsetzen und über einige Klassendiagramme nachdenken, bis Sie sich für ein geeignetes Design entschieden haben.

Beachten Sie, dass eine der Stellen, die QPushButtonclicked() ausgibt, in der Implementierung mousePressEvent() enthalten ist. Das bedeutet nicht, dass clicked() und mousePressEvent() austauschbar sind - nur, dass sie miteinander verwandt sind.

Signale und Ereignisse dienen also unterschiedlichen Zwecken (hängen jedoch damit zusammen, dass Sie beide eine Benachrichtigung über ein Ereignis "abonnieren" können).

138
Stefan Monov

Ich mag die Antworten bisher nicht. - Lassen Sie mich mich auf diesen Teil der Frage konzentrieren:

Sind Ereignisse eine Abstraktion von Signal/Slots?

Kurze Antwort: nein. Die lange Antwort wirft eine "bessere" Frage: Wie hängen Signale und Ereignisse zusammen?

Eine Hauptschleife im Leerlauf (z. B. Qt) hängt normalerweise mit einem select () - Aufruf des Betriebssystems zusammen. Durch diesen Aufruf wird die Anwendung "in den Schlafmodus" versetzt, während sie eine Reihe von Sockets oder Dateien an den Kernel weitergibt. - Und der Kern weiß als Herr der Welt, wann das passiert.

Das Ergebnis dieses select () - Aufrufs könnte Folgendes sein: Neue Daten auf dem Socket stellen eine Verbindung zu X11 her, ein Paket zu einem UDP-Port, über den wir abhören, usw. Das Zeug ist weder ein Qt-Signal noch ein Qt-Ereignis, und die Qt-Hauptschleife entscheidet selbst, ob sie die neuen Daten in die eine oder die andere umwandelt oder sie ignoriert.

Qt könnte eine Methode (oder mehrere) wie keyPressEvent () aufrufen, um sie effektiv in ein Qt-Ereignis umzuwandeln. Oder Qt gibt ein Signal aus, das tatsächlich alle für dieses Signal registrierten Funktionen nachschlägt und nacheinander abruft.

Ein Unterschied zwischen diesen beiden Konzepten ist hier sichtbar: Ein Slot hat keine Abstimmung darüber, ob andere, für dieses Signal registrierte Slots aufgerufen werden oder nicht. - Ereignisse sind eher eine Kette, und der Ereignishandler entscheidet, ob er diese Kette unterbricht oder nicht. Signale sehen in dieser Hinsicht wie ein Stern oder Baum aus.

Ein Ereignis kann ausgelöst oder vollständig in ein Signal umgewandelt werden (geben Sie einfach eines aus und rufen Sie nicht "super ()" auf). Ein Signal kann in ein Ereignis umgewandelt werden (Event-Handler aufrufen).

Was abstrahiert, was vom Fall abhängt: Das angeklickte () - Signal abstrahiert Mausereignisse (eine Schaltfläche geht nach oben und unten, 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 dies tatsächlich der Fall ist.

38
Robert Siemer

Ereignisse werden von der Ereignisschleife ausgelöst. Jedes GUI-Programm benötigt eine Ereignisschleife, unabhängig davon, ob Sie es Windows oder Linux schreiben, mit Qt, Win32 oder einer anderen GUI-Bibliothek. Jeder Thread hat auch eine eigene Ereignisschleife. In Qt ist "GUI Event Loop" (die Hauptschleife aller Qt-Anwendungen) ausgeblendet, aber Sie starten es mit dem 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. Beim Compilieren mit Moc (Meta-Objekt-Compiler) werden sie in Callback-Funktionen geändert.

Event sollte einen Empfänger haben, der es versenden soll. Niemand sonst sollte diese Veranstaltung bekommen.

Alle mit dem ausgesendeten Signal verbundenen Steckplätze werden ausgeführt.

Signale sollten nicht als Ereignisse betrachtet werden, da dies in der Qt-Dokumentation nachzulesen ist:

Wenn ein Signal ausgegeben wird, werden die damit verbundenen Slots normalerweise wie ein normaler Funktionsaufruf sofort ausgeführt. In diesem Fall werden die Signale und Slots-Mechanismus ist völlig unabhängig einer beliebigen GUI-Ereignisschleife.

Wenn Sie ein Ereignis senden, muss es einige Zeit warten, bis die Ereignisschleife alle zuvor aufgetretenen Ereignisse auslöst. Daher ist die Ausführung des Codes nach dem Senden eines Ereignisses oder Signals unterschiedlich. Code nach dem Senden eines Ereignisses wird sofort ausgeführt. Bei den Signal- und Steckplatzmechanismen hängt es vom Verbindungstyp ab. Normalerweise wird es nach allen Slots ausgeführt. Mit Qt :: QueuedConnection wird es wie Ereignisse sofort ausgeführt. Überprüfen Sie alle Verbindungstypen in der Qt-Dokumentation .

13
firescreamer

In einem Artikel wird die Ereignisverarbeitung detailliert beschrieben: 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 die .__ durchgeführt wird. gleiche Sache. Als allgemeinen Unterschied sind Signale nützlich, wenn ein .__ verwendet wird. Widget, wohingegen Ereignisse beim Implementieren des Widgets hilfreich sind. Zum Wenn wir beispielsweise ein Widget wie QPushButton verwenden, sind wir mehr interessiert an seinem angeklickten () Signal als an der einfachen Mausklick oder Tastendruckereignisse, bei denen das Signal abgegeben wurde. Aber wenn wir implementieren die QPushButton-Klasse, wir sind mehr an der .__ interessiert. Implementierung von Code für Maus- und Schlüsselereignisse. Wir normalerweise auch Ereignisse behandeln, aber durch Signalemissionen benachrichtigt werden.

Dies scheint eine übliche Art zu sein, darüber zu sprechen, da die akzeptierte Antwort einige der gleichen Ausdrücke verwendet.


Beachten Sie bitte die hilfreichen Kommentare unten zu dieser Antwort von Kuba Ober, die mich fragen lassen, ob es etwas simpel sein könnte. 

6
neuronet

TL; DR: Signale und Slots sind indirekte Methodenaufrufe. Ereignisse sind Datenstrukturen. Das sind also ganz andere Tiere.

Sie kommen nur dann zusammen, wenn Slot-Aufrufe über Thread-Grenzen hinweg ausgeführt werden. Die Slot-Aufrufargumente werden in einer Datenstruktur zusammengefasst und als Ereignis an die Ereigniswarteschlange des empfangenden Threads gesendet. Im empfangenden Thread entpackt die QObject::event-Methode die Argumente, führt den Aufruf aus und gibt möglicherweise das Ergebnis zurück, wenn es eine blockierende Verbindung war.

Wenn wir bereit sind, auf das Vergessen zu verallgemeinern, könnte man sich Ereignisse als eine Möglichkeit vorstellen, die event-Methode des Zielobjekts aufzurufen. Dies ist ein indirekter Methodenaufruf nach einer Mode - aber ich denke nicht, dass es eine hilfreiche Denkweise ist, auch wenn es eine wahre Aussage ist.

4
Kuba Ober

Ereignisse (im allgemeinen Sinne einer 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 nur wenige standardisierte Datenpakete, über die das Framework mit Ihrem Code kommunizieren kann. Wenn Sie auf die Maus achten möchten, müssen Sie sich nur die QMouseEvent-API ansehen. Die Bibliotheksdesigner müssen das Rad nicht jedes Mal neu erfinden, wenn Sie herausfinden müssen, was die Maus in einer Ecke der Tastatur getan hat die Qt-API.

Es ist wahr, dass, wenn Sie auf Ereignisse (wiederum im allgemeinen Fall) einer bestimmten Art warten, Ihr Slot fast sicher eine QEvent-Unterklasse als Argument akzeptiert.

Damit können Signale und Slots sicherlich auch ohne QEvents verwendet werden, obwohl Sie feststellen, dass der ursprüngliche Anstoß zur Aktivierung eines Signals oft eine Art Benutzerinteraktion oder eine andere asynchrone Aktivität ist. Manchmal erreicht Ihr Code jedoch nur einen Punkt, an dem das Auslösen eines bestimmten Signals das Richtige ist. Das Auslösen eines Signals, das während eines langen Prozesses mit einem Fortschrittsbalken verbunden ist, beinhaltet zum Beispiel kein QEvent.

3
jkerian

Eine weitere unbedeutende pragmatische Überlegung: Das Senden oder Empfangen von Signalen erfordert das Erben von QObject, während ein Objekt jeglicher Vererbung ein Ereignis senden oder senden kann (da Sie QCoreApplication.sendEvent() oder postEvent() aufrufen) QObject die erste Superklasse sein, und Sie möchten möglicherweise nicht Ihre Vererbungsreihenfolge neu ordnen, nur um Signale senden zu können.)

1
bootchk

Diese Frage habe ich beim Lesen von 'Event processing' von Leow Wee Kheng gefunden. Es heißt auch:

enter image description here

0
francek

Meiner Meinung nach sind Ereignisse völlig überflüssig und könnten verworfen werden. Es gibt keinen Grund, warum Signale durch Ereignisse nicht durch Ereignisse oder Ereignisse ersetzt werden können, es sei denn, Qt ist bereits so eingerichtet, wie es ist. Warteschlangen-Signale werden von Ereignissen umschlossen, und Ereignisse könnten möglicherweise von Signalen umschlossen werden, zum Beispiel:

connect(this, &MyItem::mouseMove, [this](QMouseEvent*){});

Ersetzt die in QWidget (jedoch nicht mehr in QQuickItem) gefundene Komfortfunktion mouseMoveEvent() und würde mouseMove-Signale behandeln, die ein Szenenmanager für das Element ausgeben würde. Die Tatsache, dass das Signal von einer externen Entität für den Artikel ausgesendet wird, ist unwichtig und kommt in der Welt der Qt-Komponenten häufig vor, obwohl es angeblich nicht zulässig ist (Qt-Komponenten umgehen diese Regel oft). Aber Qt ist ein Konglomerat aus vielen verschiedenen Designentscheidungen und ziemlich aus Stein gemeißelt, aus Angst, alten Code zu brechen (was oft genug vorkommt).

0
user1095108