Liste der Qt-Signalsteckplätze

Liste der Qt-Signalsteckplätze

Signale und Slots dienen der Kommunikation zwischen Objekten. Der Signal- und Slot-Mechanismus ist eine Kernfunktion von Qt und unterscheidet sich wahrscheinlich am meisten von den Funktionen anderer Frameworks. Signale und Slots werden durch das Metaobjektsystem von Qt ermöglicht.

Wenn wir bei der GUI-Programmierung ein Widget ändern, möchten wir normalerweise, dass ein anderes Widget benachrichtigt wird. Im Allgemeinen möchten wir, dass Objekte jeglicher Art miteinander kommunizieren können. Wenn der Benutzer beispielsweise auf die Schaltfläche „Schließen“ klickt, möchten wir möglicherweise die Funktion „Close()“ des Fensters aufrufen.

Andere Toolkits verwenden Rückrufe, um diese Kommunikation zu erreichen. Eine Callback-Funktion ist ein Zeiger auf eine Funktion. Wenn Sie also möchten, dass ein Handler Sie über ein Ereignis benachrichtigt, müssen Sie dem Handler einen Zeiger auf eine andere Funktion (die Callback-Funktion) übergeben. Anschließend ruft die Handlerfunktion gegebenenfalls den Rückruf auf. Es gibt zwar erfolgreiche Frameworks, die diesen Ansatz verwenden, Rückrufe können jedoch nicht intuitiv sein und es kann Probleme geben, die Typkorrektheit der Rückrufparameter sicherzustellen.

In Qt haben wir eine Alternative zur Callback-Technik: Wir verwenden Signale und Slots. Geben Sie ein Signal aus, wenn ein bestimmtes Ereignis eintritt. Die Widgets von Qt verfügen über viele vordefinierte Signale, aber wir können Widgets jederzeit in Unterklassen unterteilen, um ihnen unsere eigenen Signale hinzuzufügen. Slots sind Funktionen, die als Reaktion auf bestimmte Signale aufgerufen werden. Die Widgets von Qt verfügen über eine Reihe vordefinierter Slots. Es ist jedoch üblich, das Widget in Unterklassen zu unterteilen und eigene Slots hinzuzufügen, um interessante Signale zu verarbeiten.

Fügen Sie hier eine Bildbeschreibung ein

Der Signal- und Slot-Mechanismus ist typsicher: Die Signatur des Signals muss mit der Signatur des Empfangsslots übereinstimmen. (Tatsächlich kann die Signatur eines Slots kürzer sein als das von ihm empfangene Signal, da die zusätzlichen Argumente ignoriert werden können.) Da die Signaturen kompatibel sind, kann uns der Compiler dabei helfen, Typkonflikte zu erkennen, wenn wir eine auf Funktionszeigern basierende Syntax verwenden. Die stringbasierte SIGNAL- und SLOT-Syntax erkennt Typkonflikte zur Laufzeit. Signale und Slots sind lose gekoppelt: Eine Klasse, die ein Signal aussendet, weiß weder, noch kümmert es sie, welcher Slot das Signal empfängt. **Der Signal- und Slot-Mechanismus von Qt stellt sicher, dass, wenn Sie ein Signal mit einem Slot verbinden, der Slot zum richtigen Zeitpunkt mit den Argumenten des Signals aufgerufen wird. ** Signale und Slots können eine beliebige Anzahl von Argumenten jeglichen Typs akzeptieren. Sie sind absolut typsicher.

Alle Klassen, die von QObject oder seinen Unterklassen (z. B. QWidget) erben, können Signale und Slots enthalten. Signale werden ausgesendet, wenn ein Objekt seinen Zustand auf eine Weise ändert, die für andere Objekte von Interesse sein könnte. Das ist alles, was die Objekte tun, um zu kommunizieren. Es weiß es nicht und es ist ihm egal, ob irgendetwas das Signal empfängt, das es sendet. Dies ist eine echte Informationskapselung und stellt sicher, dass Objekte für komponentenbasiertes Software-Engineering verwendet werden können.

Slots können zum Empfangen von Signalen verwendet werden, sie sind aber auch normale Mitgliedsfunktionen. So wie ein Objekt nicht weiß, ob irgendetwas sein Signal empfängt, weiß ein Steckplatz nicht, ob ein Signal mit ihm verbunden ist. Dadurch wird sichergestellt, dass mit Qt wirklich eigenständige Komponenten erstellt werden können.

Sie können beliebig viele Signale an einen einzelnen Steckplatz anschließen, und Sie können beliebig viele Signale an beliebig viele Steckplätze anschließen. Es ist sogar möglich, ein Signal direkt mit einem anderen zu verbinden. (Sobald das erste Signal ausgegeben wird, wird sofort das zweite Signal ausgegeben.)

Signale und Slots bilden einen leistungsstarken Komponentenprogrammierungsmechanismus.

Signale

Ein Signal wird ausgegeben, wenn sich der interne Zustand eines Objekts auf eine Weise geändert hat, die für den Client oder Besitzer des Objekts von Interesse sein könnte. Signale sind öffentlich zugängliche Funktionen und können von überall ausgegeben werden. Wir empfehlen jedoch, sie nur von der Klasse auszugeben, die das Signal und seine Unterklassen definiert.

Wenn ein Signal ausgegeben wird, wird der damit verbundene Slot normalerweise sofort ausgeführt, genau wie ein normaler Funktionsaufruf. In diesem Fall ist der Signal- und Slotmechanismus völlig unabhängig von einer GUI-Ereignisschleife. Sobald alle Slots zurückgegeben wurden, wird der Code nach der Emit-Anweisung ausgeführt. Die Situation ist etwas anders, wenn Verbindungen in der Warteschlange verwendet werden. In diesem Fall wird der Code, der dem Schlüsselwort „emit“ folgt, sofort fortgesetzt und der Slot wird später ausgeführt.

Wenn mehrere Slots mit einem Signal verbunden sind, werden diese Slots nacheinander in der Reihenfolge ausgeführt, in der sie zum Zeitpunkt der Ausgabe des Signals verbunden waren.

Signale werden automatisch von moc generiert und können nicht in sein. Cpp-Dateien. Sie können niemals einen Rückgabetyp haben (d. h. void verwenden).

Hinweis zu Parametern: Unsere Erfahrung zeigt, dass Signale und Slots besser wiederverwendbar sind, wenn sie keine speziellen Typen verwenden. Wenn QScrollBar::valueChanged() einen speziellen Typ verwenden würde, beispielsweise den hypothetischen QScrollBar::Range, dann könnte er nur mit einem Slot verbunden werden, der speziell für QScrollBar entwickelt wurde. Es ist nicht möglich, verschiedene Eingabe-Widgets miteinander zu verbinden.

Schlüssel

Ein Slot wird aufgerufen, wenn das mit dem Slot verbundene Signal ausgegeben wird. Slots sind normale C++-Funktionen und können normal aufgerufen werden; ihre einzige Eigenschaft besteht darin, dass mit ihnen Signale verbunden werden können.

Da es sich bei Slots um normale Memberfunktionen handelt, folgen sie beim direkten Aufruf den üblichen C++-Regeln. Als Slots können sie jedoch von jeder Komponente, unabhängig von ihrer Zugriffsebene, über eine Signal-Slot-Verbindung aufgerufen werden. Dies bedeutet, dass ein von einer Instanz einer beliebigen Klasse gesendetes Signal dazu führen kann, dass ein privater Slot in einer Instanz einer nicht verwandten Klasse aufgerufen wird.

Sie können Slots auch als virtuell definieren, was sich in der Praxis als sehr nützlich erwiesen hat.

Signale und Slots sind etwas langsamer als Callbacks, da sie mehr Flexibilität bieten, obwohl der Unterschied in dieser Hinsicht bei realen Anwendungen nicht so groß ist. Im Allgemeinen ist das Senden eines mit einem Slot verbundenen Signals etwa zehnmal langsamer als der direkte Aufruf eines nicht-virtuellen Funktionsaufrufs an den Empfänger. Dies ist der Overhead, der erforderlich ist, um das Verbindungsobjekt zu lokalisieren, alle Verbindungen sicher zu durchlaufen (d. h. zu prüfen, ob nachfolgende Empfänger während eines Sendevorgangs zerstört werden) und alle Parameter auf generische Weise zu marsalieren. Auch wenn 10 nicht-virtuelle Funktionsaufrufe nach viel klingen, ist der Overhead weitaus geringer als bei jedem Neu- oder Löschvorgang. Sobald Sie eine String-, Vektor- oder Listenoperation ausgeführt haben, muss der Hintergrund neu sein oder gelöscht werden, und der Signal- und Slot-Overhead macht nur einen kleinen Bruchteil der Kosten des gesamten Funktionsaufrufs aus. Es ist dasselbe, egal ob Sie einen Systemaufruf in einem Slot ausführen oder zehn oder mehr Funktionen indirekt aufrufen. Die Einfachheit und Flexibilität des Signal- und Slot-Mechanismus ist den Aufwand wert, und Ihre Benutzer werden es nicht einmal bemerken.

Beachten Sie, dass andere Bibliotheken, die Variablen namens Signale oder Slots definieren, beim Kompilieren mit Qt-basierten Anwendungen Compilerwarnungen und -fehler verursachen können. Um dies zu beheben, verwenden Sie das Präprozessorsymbol # undef.

Die Verwendung von connect in Qt

Die alte Version der Schreibmethode ist komplizierter und erfordert eine klare Angabe des Signals und des Steckplatzes, einschließlich formaler Parameter.

class MyButton : public QWidget
{
    Q_OBJECT
public:
    explicit MyButton(QWidget *parent = nullptr);

signals:
    void sigClicked();
    void sigClicked(bool check);
};

Die alte Version ist auf einen Blick geschrieben, aber sie ist problematischer

connect(m_pBtn,SIGNAL(sigClicked()),this,SLOT(onClicked()));
connect(m_pBtn,SIGNAL(sigClicked(bool)),this,SLOT(onClicked(bool)));

So schreiben Sie nach Qt5.0

connect(m_pBtn,&MyButton::sigClicked,this,&Widget::onClicked);

Bei Überlastung des Signals wird ein Fehler gemeldet

Fehler: Keine passende Mitgliedsfunktion für Aufruf von „connect“ connect(m_pBtn,&MyButton::sigClicked,this,&Widget::onClicked);

Verwenden Sie die folgende Schreibmethode, um die angegebene Version der Signalfunktion anzugeben. Wenn die Slot-Funktion überlastet ist, kann auch die alte Methode verwendet werden

connect(m_pBtn, static_cast<void (MyButton::*)(bool)>(&MyButton::sigClicked), this, &Widget::onClicked);

Optimieren Sie die Überlastung der Signalfunktion

Die überlastete Slot-Funktion kann jedoch immer noch nicht verknüpft werden.

connect(m_pBtn, QOverload<bool>::of(&MyButton::sigClicked),this,&Widget::onClicked);

Methode zum Schreiben der Lambda-Funktion

Wenn der Inhalt der Slot-Funktion einfach ist, ist es nicht erforderlich, einen Slot-Link separat zu definieren. Es ist praktisch, die Lambda-Funktion direkt zu verwenden.

connect(m_pBtn, QOverload<bool>::of(&MyButton::sigClicked),

               [=](bool check){

                /* do something.. */

                });

connect(m_pBtn, static_cast<void (MyButton::*)(bool)>(&MyButton::sigClicked), this, [=](bool check){

                 //do something

                 });

Lambda-Funktion

[capture](parameters) mutable ->return-type{statement}

1.[Capture]: Capture-Liste. Die Erfassungsliste erscheint immer am Anfang der Lambda-Funktion. Tatsächlich ist [] ein Lambda-Zitat. Der Compiler beurteilt anhand des Auslösers, ob der folgende Code eine Lambda-Funktion ist. Die Erfassungsliste kann Variablen im Kontext zur Verwendung durch Lambda-Funktionen erfassen;

2. (Parameter): Parameterliste. Entspricht der Parameterliste gewöhnlicher Funktionen. Wenn die Parameterübergabe nicht erforderlich ist, kann sie zusammen mit den Klammern „()“ weggelassen werden;

3. veränderlich: veränderlicher Modifikator. Standardmäßig ist eine Lambda-Funktion immer eine konstante Funktion und veränderbar kann ihre Konstanz aufheben. Bei Verwendung dieses Modifikators kann die Parameterliste nicht weggelassen werden (auch wenn der Parameter leer ist);

4.->return-type: Rückgabetyp. Deklarieren Sie den Rückgabetyp einer Funktion mit der Trace-Rückgabetypform. Wir können es auch zusammen mit dem Symbol „->“ weglassen, wenn wir keinen Rückgabewert benötigen. Wenn der Rückgabetyp klar ist, kann dieser Teil außerdem weggelassen werden, damit der Compiler den Rückgabetyp ableiten kann.

5. {Anweisung}: Funktionskörper. Der Inhalt entspricht dem einer normalen Funktion, jedoch können neben den Parametern auch alle erfassten Variablen verwendet werden.

Der größte Unterschied zu gewöhnlichen Funktionen besteht darin, dass Lambda-Funktionen neben Parametern in manchen Kontexten auch über Erfassungslisten auf Daten zugreifen können. Insbesondere beschreibt die Erfassungsliste, welche Daten im Kontext von Lambda verwendet werden können und wie sie verwendet werden (nach Wert oder nach Referenz). Syntaktisch gesehen ist in „[]“ eine Erfassungsliste enthalten, und die Erfassungsliste besteht aus mehreren durch Kommas getrennten Erfassungselementen. Erfassungslisten gibt es in den folgenden Formen:

1. [var] gibt an, dass die Wertübertragungsmethode die Variable var erfasst.
2. [=] gibt an, dass die Wertübertragungsmethode alle Variablen im übergeordneten Bereich (einschließlich dieser) erfasst.
3. [&var] gibt an, dass die Referenzübertragung erfasst die Variable var;
4. [&] bedeutet, dass die Referenzübertragungsmethode alle Variablen im übergeordneten Bereich (einschließlich this) erfasst.
5. [this] bedeutet, dass die Wertübertragungsmethode den aktuellen this-Zeiger erfasst.

Der oben erwähnte übergeordnete Bereich ist der Anweisungsblock, der die Lambda-Funktion enthält. Für den Laien ist es der Codeblock „{}“, der Lambda enthält. Die oben genannten Capture-Listen können auch kombiniert werden, zum Beispiel:

1.[=,&a,&b] bedeutet, die Variablen a und b per Referenz zu erfassen und alle anderen Variablen nach Wert zu erfassen; 2.[&,a,this] bedeutet, die Variablen a und this nach Wert zu erfassen, das
Vorbeigehen -reference-Methode erfasst alle anderen Variablen.

Es ist jedoch zu beachten, dass die Erfassungsliste die wiederholte Übergabe von Variablen nicht zulässt. Die folgenden Beispiele sind typische Duplikate, die zu Fehlern bei der Kompilierung führen. Zum Beispiel:

3. [=, a] hier hat alle Variablen durch Wertübertragung erfasst, aber wenn a wiederholt erfasst wird, wird ein Fehler gemeldet;
4. [&,&this] hier & hat alle Variablen durch Referenzübertragung erfasst und erfasst diese dann Es ist auch eine Wiederholung.

Beispiel einer Lambda-Funktion

[Erfassung] Erklärung

Fügen Sie basierend auf dem Projekt im vorherigen Abschnitt die Schaltfläche mybutton_1 hinzu, drücken Sie die Schaltfläche und ändern Sie den Schaltflächentext. Gemäß dem grundlegenden Kompositionscode des obigen Lambda-Ausdrucks kann er wie folgt geschrieben werden (falsche Schreibweise):

QPushButton *mybutton_1=new QPushButton(this);
mybutton_1->setText("关注<程序媛讲QT>");
mybutton_1->move(150,100);   //移动按键
connect(mybutton_1,&QPushButton::pressed,
        []()
        {
             mybutton_1->setText("Lambda表达式");
         }
);

Zu diesem Zeitpunkt wird beim Kompilieren die Fehlermeldung „Fehler: ‚mybutton_1‘ wird nicht erfasst“ angezeigt, was bedeutet, dass mybutton_1 nicht in meinem Bereich liegt. Zu diesem Zeitpunkt müssen Sie [capture] verwenden, um externe Variablen zu übergeben. Zu diesem Zeitpunkt müssen wir nur noch mybutton_1 übergeben, und der Code kann kompiliert und normal ausgeführt werden. Der Funktionscode connect() wird wie folgt geändert:

connect(mybutton_1,&QPushButton::pressed,
        [mybutton_1]()
        {
              mybutton_1->setText("Lambda表达式");
        }
);

An diesem Punkt kann der Code kompiliert und normal ausgeführt werden. Wenn sich dann mehrere Variablen außerhalb befinden, ist es zu mühsam, sie einzeln in [] zu schreiben. Zu diesem Zeitpunkt können wir „=“ verwenden. Zum Beispiel:

QPushButton *mybutton_1=new QPushButton(this);
mybutton_1->setText("关注<程序媛讲QT>");
mybutton_1->move(150,100);   //移动按键
int a=2,b=7;
connect(mybutton_1,&QPushButton::pressed,
        [=]()
         {
               mybutton_1->setText("Lambda表达式");
               qDebug()<<a+b;
          }
);

qDebug() ist ein Ausdruck, ähnlich wie printf in der C-Sprache. Sie müssen „QDebug“ #include verwenden, wenn Sie es verwenden.
  Kompilieren und ausführen, nachdem die Schaltfläche gedrückt wurde, ändert sich der Schaltflächentext und der Text „9“ wird ausgegeben. Es ist ersichtlich, dass „=“ die Werte der externen Variablen a, b und mybutton_1 übergeben hat.
  Es ist ersichtlich, dass „=“ alle externen lokalen Variablen und alle Mitglieder der Klasse als Wert übergeben kann. Ebenso bedeutet „this“, dass alle Mitglieder der Klasse als Wert übergeben werden; „&“ bedeutet, dass auf alle externen lokalen Variablen verwiesen wird.

Zusammenfassen:

  • [capture]: Im Lambda-Ausdruck muss dieser Teil vorhanden sein und darf nicht weggelassen werden.
  • =: Übergeben Sie alle externen lokalen Variablen und alle Mitglieder der Klasse nach Wert an den Lambda-Ausdruck. Der Standardwert ist schreibgeschützt und der Wert der Variablen kann nicht geändert werden.
  • &: Alle lokalen Variablen (einschließlich dieser in der Klasse, in der sich der Lambda-Ausdruck befindet), die Übertragungsmethode der Referenz.
  • Dies: Mitgliedsvariablen in der Klasse, in der sich Lambda befindet, können im Funktionskörper verwendet werden.
  • „=“ wird empfohlen.

Veränderlich erklärt

Wie oben erwähnt, wird „=" als Wert übergeben. Es ist standardmäßig schreibgeschützt. Wenn wir die externe Variable ändern möchten, können wir zu diesem Zeitpunkt veränderbar verwenden. Der Code lautet wie folgt:

connect(mybutton_1,&QPushButton::pressed,
        [=]() mutable
        {
              b=3;
              mybutton_1->setText("Lambda表达式");
              qDebug()<<a+b;
         }
);
  • Zusammenfassen:
  • In Lambda-Ausdrücken kann veränderbar weggelassen werden. Wenn eine Variable als Wert übergeben wird, kann durch Hinzufügen des veränderlichen Modifikators der Wert der Variablen im Lambda-Ausdruck geändert werden, der Wert selbst wird jedoch nicht geändert.

(Parameter) Erklärung

Wenn der Lambda-Ausdruck keine Parameter hat, kann dieser Teil ignoriert werden; wenn das Signal Parameter hat, können Sie () verwenden, um die Funktionsparameter zu überladen. Zum Beispiel:

connect(mybutton_1,&QPushButton::clicked,
             [=](bool Check)
             {
                  qDebug()<<Check;
             }
         );

Das Signal void QAbstractButton::clicked(bool reviewed = false) von QPushButton verfügt über Parameter, und die Parameter können über () an den Lambda-Ausdruck übergeben werden.

Zusammenfassen:

utton::clicked,
[=](bool Check)
{ qDebug()<<Check; } );



 QPushButton的信号void QAbstractButton::clicked(bool checked = false)是有参数的,通过()可将参数传入Lambda表达式中。

**总结:**

- **在Lambda表达式中无参数时,(parameters)部分是可以省略的。**

Supongo que te gusta

Origin blog.csdn.net/weixin_43925768/article/details/131124411
Recomendado
Clasificación