6. Grundlegende Nutzung und Erweiterung von Signalen und Steckplätzen

Konzept

Signale und Slots sind einer der stolzen Mechanismen des Qt-Frameworks. Der sogenannte Signalslot ist eigentlich der Beobachtermodus. Wenn ein Ereignis eintritt , beispielsweise wenn eine Schaltfläche erkennt, dass darauf geklickt wurde, sendet sie ein Signal aus . Diese Art des Sendens hat keinen Zweck und ähnelt dem Senden. Wenn ein Objekt an diesem Signal interessiert ist, verwendet es die Verbindungsfunktion , was bedeutet, dass es das Signal, das es verarbeiten möchte, an eine seiner eigenen Funktionen (einen sogenannten Slot) bindet, um das Signal zu verarbeiten . Das heißt, wenn das Signal ausgegeben wird, wird die Funktion des verbundenen Steckplatzes automatisch zurückgerufen . Dies ähnelt dem Beobachtermuster: Wenn ein Ereignis von Interesse eintritt, wird automatisch eine Operation ausgelöst.

6.1. Die mit dem System gelieferten Signale und Steckplätze

connect()-Funktion

Als nächstes vervollständigen wir eine kleine Funktion. Wir haben oben gelernt, wie man Schaltflächen erstellt, haben uns aber noch nicht mit der Funktion der Schaltfläche befasst. Die größte Funktion der Schaltfläche besteht darin, etwas auszulösen, nachdem wir darauf geklickt haben. Zum Beispiel, wenn wir auf die Schaltfläche klicken , wir werden das aktuelle Fenster schließen. , also wie implementiert man eine solche Funktion in Qt?

Tatsächlich ist dies nicht mit nur zwei Codezeilen möglich. Schauen wir uns den folgenden Code an:

QPushButton * quitBtn = new QPushButton("关闭窗口",this);
connect(quitBtn,&QPushButton::clicked,this,&MyWidget::close);

Die erste Zeile besteht darin, eine Schaltfläche zum Schließen zu erstellen, was ich zuvor gelernt habe. Die zweite Zeile ist der Kern, nämlich die Verwendung des Signalsteckplatzes.

Die am häufigsten verwendete allgemeine Form der connect()-Funktion:

connect(sender, signal, receiver, slot);

Parametererklärung: senderEs ist das Objekt, das das Signal sendet, signales ist das vom sendenden Objekt gesendete Signal, receiveres ist das Objekt, das das Signal empfängt, slotes ist die Slot-Funktion (die Funktion, die das empfangende Objekt nach dem Empfang des Signals aufrufen muss). )


Signalsignal und Steckplatzfunktionssteckplatz finden

Wie finde ich die Signale und Steckplätze, die dem System beiliegen? Hierfür müssen Sie das Hilfedokument verwenden. Geben Sie im Hilfedokument beispielsweise das Klicksignal der Schaltfläche oben ein. Geben Sie im Hilfedokument QPushButton ein. Zuerst müssen wir können im Inhalt nach den Schlüsselwortsignalen suchen. Bedeutung, aber wir haben festgestellt, dass wir nicht gefunden haben

Zu diesem Zeitpunkt sollten wir darüber nachdenken, dass dieses Signal möglicherweise von der übergeordneten Klasse geerbt wird, sodass wir das Schlüsselwort in der übergeordneten Klasse QAbstractButton finden können. Klicken Sie auf den Signalindex, um die folgenden Signale zu finden, die mit dem System geliefert werden:

Klicken Sie hier, um das zu finden, was wir finden möchten. Die Slot-Funktion wird auf die gleiche Weise durchsucht wie das Signal, mit der Ausnahme, dass ihr Schlüsselwort Slot ist.


Fall: Klicken Sie auf die Schaltfläche -> Fenster schließen

Zuerst erstellen wir unseren eigenen Button:

//创建一个自己的按钮对象
    MyPushButton *myBtn=new MyPushButton;
    myBtn->setText("我自己的按钮");
    myBtn->move(200,0);
    myBtn->setParent(this);//设置到对象树中,窗口释放的时候会被自动释放

Dann nutzen wir die Connect-Funktion, um den Button mit dem geschlossenen Fenster zu verknüpfen

 //需求  点击我的按钮--->关闭窗口
    //参数1 信号的发送者 参数2 发送的信号(函数的地址) 参数3 信号的接收者  参数四  处理的槽函数
    connect(myBtn,&MyPushButton::clicked,this,&myWidget::close);
    //connect(myBtn,&QPushButton::clicked,this,&QWidget::close);

veranschaulichen:

  • MyPushButton ist eine benutzerdefinierte Klasse, die öffentlich von QPushButton geerbt wird. Wir haben seinem Konstruktor und Destruktor qDebug-Anweisungen hinzugefügt, um die Erstellung und Freigabe von Objekten zu erleichtern.

  • Der Absender ist der Name der von uns erstellten Schaltfläche myBtnund der Empfänger repräsentiert dieses Fenster.this
  • Für die gesendeten Signale und Slot-Funktionen besteht die Bereichsbeschränkung darin, dass wir beide verwenden können, da unsere benutzerdefinierte MyPushButtonKlasse von erbt .QPushbutton

6.2. Benutzerdefinierte Signale und Slots (keine Parameter)

Mit connect() können wir eine Verbindung zu den vom System bereitgestellten Signalen und Slots herstellen. Der Signal- und Slot-Mechanismus von Qt nutzt jedoch nicht nur den vom System bereitgestellten Teil, sondern ermöglicht es uns auch, unsere eigenen Signale und Slots zu entwerfen.

Werfen wir einen Blick auf die Signale und Slots mit Qt:

Erstellen Sie Lehrer- und Schülerklassen:

Letztendlich wollen wir folgende Effekte erzielen:

//Teacher类  老师类
//Student类  学生类
//下课后,老师触发一个信号,饿了,学生响应信号,请客吃饭

Dann erstellen wir eine Schülerklasse und eine Lehrerklasse:

Klicken Sie mit der rechten Maustaste auf den Dateinamen, wählen Sie aus add new, wählen Sie die C++-Klasse aus und fahren Sie fort

Dann benennen wir Teacherdie Klasse und entscheiden uns für die Erbung von QObject. Da wir keine Schaltflächen oder ähnliches verwenden, müssen wir nicht von erben QWidget, sodass wir direkt von der höchsten Klasse erben können.

Wählen Sie „Weiter“, um den Vorgang direkt abzuschließen. StudentDie Erstellung der Klasse erfolgt wie oben.

Benutzerdefiniertes Signal (keine Parameter)

Erstellen Sie zunächst eine Teacherneue Klasse und deklarieren Sie ein benutzerdefiniertes Signal in der Header-Datei – Hungryhungry

signals:
    //自定义信号  写道signals下
    //返回值是 void,只需要声明,不需要实现
    //可以有参数,可以发生重载
    void hungry();
};

Benutzerdefinierte Slot-Funktion (keine Parameter)

Erstellen Sie dann eine neue StudentKlasse und deklarieren Sie die Slot-Funktion in der Header-Datei – behandeln Sie michtreat

	//早期Qt版本 必须写到public slots,高级版本可以写到 public或者全局下
    //返回值是void,需要声明,也需要实现
    //可以有参数,可以发生重载
    void treat();
signals:

Implementieren Sie in der CPP-Datei des Schülers die Slot-Funktion

void Student:: treat()
{
    qDebug()<<"请老师吃饭";
}

Implementieren Sie die öffentliche Methode (keine Parameter)

Erstellen Sie in der Fenster- WidgetHeader-Datei Zeiger auf Teacherund StudentObjekte

private:
    Ui::Widget *ui;

    Teacher *zt;
    Student *st;

    void classIsOver();
};

Implementieren Sie eine öffentliche Methode im Fenster cpp, um die Klasse zu beenden ClassIsOver(). Der Aufruf dieser Methode löst ein Signal aus, dass der Lehrer hungrig ist, und die entsprechende Slot-Funktion behandelt die Schüler.

void Widget::classIsOver()
{
    //下函数,调用后,出发老师饿了的信号
    emit zt->hungry();
}

emit Benutzerdefinierte Signale.

Benutzerdefinierte Signale und Slots verbinden (keine Parameter)

Erstellen Sie Lehrer- und Schülerobjekte im Fenster cpp, verwenden Sie die Verbindungsfunktion, um eine Verbindung herzustellen, und rufen Sie die Funktion „Ende der Klasse verlassen“ auf, um das Signal auszulösen

   //创建一个老师对象
    this->zt=new Teacher(this);

    //创建一个学生对象
    this->st=new Student(this);

    //老师饿了,学生请客的连接
    connect(zt,&Teacher::hungry,st,&Student::treat);

    //调用下课函数
    classIsOver();

Das Ergebnis ist wie folgt:

Wir haben „Den Lehrer zum Abendessen einladen“ debuggt.


6.3. Benutzerdefinierte Signale und Slots (Überladung -> mit Parametern)

Überlastete benutzerdefinierte Signale und Slots (mit Parametern)

Nachladesignale:

signals:
    //自定义信号  写道signals下
    //返回值是 void,只需要声明,不需要实现
    //可以有参数,可以发生重载
    void hungry();

    void hungry(QString foodName);
void Student:: treat()
{
    qDebug()<<"请老师吃饭";
}

void Student::treat(QString foodName)
{
    qDebug()<<"请老师吃饭,老师要吃:"<<foodName;
}

Überlastete Slot-Funktion:

//早期Qt版本 必须写到public slots,高级版本可以写到 public或者全局下
    //返回值是void,需要声明,也需要实现
    //可以有参数,可以发生重载
    void treat();

    void treat(QString foodName);

signals:

Überladen Sie die Funktion zum Verlassen der Klasse (mit Parametern)

void Widget::classIsOver()
{
    //下函数,调用后,出发老师饿了的信号
    //emit zt->hungry();
    emit zt->hungry("宫保鸡丁");
}

Überlastete Signale und Slots verbinden (mit Parametern)

Da es zwei benutzerdefinierte Signale und benutzerdefinierte Steckplätze mit demselben Namen gibt, wird bei der direkten Verbindung ein Fehler gemeldet. Sie müssen daher einen Funktionszeiger verwenden, um auf die Funktionsadresse zu zeigen, und dann eine Verbindung herstellen

 //连接带参数的 信号和槽
    //指针->地址
    //函数指针->函数地址

    void(Teacher::*teacherSignal)(QString foodName)=&Teacher::hungry;
    void(Student::*studentSlot)(QString foodName)=&Student::treat;
    connect(zt,teacherSignal,st,studentSlot);
    classIsOver();

Ergebnis der Konvertierungsausgabe (QString->char *)

Das Ergebnis ist wie folgt:

Wie Sie sehen, enthält das Ergebnis Anführungszeichen, obwohl wir das Ergebnis korrekt ausgeben. Wenn wir die Anführungszeichen entfernen möchten, müssen wir eine Typkonvertierung durchführen.

Wir können es zuerst in konvertieren QByteArray (.toUtf8())und dann in konvertieren char *, wobei QByteArray ein Byte-Array in Qt ist.

void Student:: treat()
{
    qDebug()<<"请老师吃饭";
}

void Student::treat(QString foodName)
{
    //QString->char *  先转成QByteArray (.toUtf8()) 再转char * (.data)
    qDebug()<<"请老师吃饭,老师要吃:"<<foodName.toUtf8().data();
}

Vorsichtsmaßnahmen:

Was Sie beim Anpassen von Signalsteckplätzen beachten sollten:

  • Sowohl der Sender als auch der Empfänger müssen Unterklassen von QObject sein (natürlich, außer wenn die Slot-Funktion eine globale Funktion, ein Lambda-Ausdruck usw. ist und keinen Empfänger erfordert);
  • Der Rückgabewert von Signal- und Slotfunktionen ist ungültig
  • Signale müssen nur deklariert, nicht implementiert werden
  • Slot-Funktionen müssen deklariert und implementiert werden
  • Die Slot-Funktion ist eine gewöhnliche Mitgliedsfunktion. Als Mitgliedsfunktion wird sie von öffentlich, privat und geschützt beeinflusst;
  • Verwenden Sie Emit, um Signale an den richtigen Ort zu senden.
  • Verwenden Sie die Funktion connect(), um Signale und Slots zu verbinden.
  • Als Slot-Funktion können alle Mitgliedsfunktionen, statischen Funktionen, globalen Funktionen und Lambda-Ausdrücke verwendet werden
  • Das Signal und der Slot erfordern, dass die Parameter des Signals und des Slots konsistent sind. Die sogenannte Konsistenz bedeutet, dass die Parametertypen konsistent sind.
  • Wenn die Parameter des Signals und des Slots inkonsistent sind, ist es zulässig, dass die Slot-Funktion weniger Parameter als das Signal haben kann. Dennoch muss die Reihenfolge der Parameter der Slot-Funktion mit den ersten paar des Signals übereinstimmen. Dies liegt daran, dass Sie die Daten aus dem Signal in der Slot-Funktion ignorieren können (d. h. die Slot-Funktion hat weniger Parameter als das Signal).

6.4. Erweiterung der Signalsteckplätze

  • Ein Signal kann an mehrere Steckplätze angeschlossen werden

In diesem Fall werden die Slots nacheinander aufgerufen, die Reihenfolge in der sie aufgerufen werden ist jedoch nicht definiert.

  • An einen Steckplatz können mehrere Signale angeschlossen werden

Dieser Slot wird immer dann aufgerufen, wenn ein Signal ausgegeben wird .

  • Ein Signal kann mit einem anderen Signal verbunden werden

Wenn das erste Signal ausgesendet wird, wird das zweite Signal ausgesendet. Ansonsten gibt es keinen Unterschied zwischen dieser Signal-Signal-Form und der Signal-Slot-Form.

  • Die Verknüpfung von Slots kann aufgehoben werden

Diese Situation kommt nicht oft vor, denn wenn ein Objekt gelöscht wird, löscht Qt automatisch alle mit diesem Objekt verbundenen Slots .

  • Der Signalsteckplatz kann getrennt werden

Der Signalsteckplatz kann mit dem Schlüsselwort „disconnect“ getrennt werden.

  • Verwenden von Lambda-Ausdrücken

Bei Verwendung von Qt 5 unterstützen alle Compiler, die Qt 5 unterstützen können, Lambda-Ausdrücke.

Bei der Verbindung von Signalen und Slots können Slot-Funktionen mithilfe von Lambda-Ausdrücken verarbeitet werden. Wir werden später im Detail vorstellen, was ein Lambda-Ausdruck ist.

Legen Sie eine Schaltfläche fest, um das Ende des Verlassens des Unterrichts auszulösen (mit Parametern).

Wir kommentieren zunächst ClassIsOverdie Funktion aus, erstellen eine Schaltfläche und verbinden sie mit ClassIsOverder Funktion über Schaltfläche->ClassIsOver-Funktion->hungriges Signal->Slot-Funktion behandeln.

 //连接带参数的 信号和槽
    //指针->地址
    //函数指针->函数地址

    void(Teacher::*teacherSignal)(QString foodName)=&Teacher::hungry;
    void(Student::*studentSlot)(QString foodName)=&Student::treat;
    connect(zt,teacherSignal,st,studentSlot);
    //classIsOver();

    //点击一个下课的按钮,再触发下课
    QPushButton *btn=new QPushButton("下课",this);
    //重置窗口大小
    this->resize(600,400);

    //点击下课按钮,触发下课(有参)
    connect(btn,&QPushButton::clicked,this,&Widget::classIsOver);

Das Ergebnis ist wie folgt:

Klicken Sie auf die Schaltfläche, um die Slot-Funktion auszulösen

Signalverbindungssignal (keine Parameter)

Dieses Mal verwenden wir keine ClassIsOverFunktion zum Auslösen der Slot-Funktion, sondern verwenden zwei Verbindungsfunktionen, um den Effekt von button->teacherSignal2->studentSlot2 zu erzielen.

Wir kommentieren zunächst die Funktion des vorherigen Tastensteckplatzes aus und stellen dann zwei Steckplatzverbindungen ohne Parameter her.

 //点击下课按钮,触发下课(有参)
//    connect(btn,&QPushButton::clicked,this,&Widget::classIsOver);

    //无参信号和槽连接
    void(Teacher::*teacherSignal2)(void)=&Teacher::hungry;
    void(Student::*studentSlot2)(void)=&Student::treat;
    connect(zt,teacherSignal2,st,studentSlot2);

    //信号连接信号   按钮-->老师信号-->学生treat函数
    connect(btn,&QPushButton::clicked,zt,teacherSignal2);

    //拓展
    //1、信号可以连接多个信号
    //2、一个信号可以连接多个槽函数
    //3、多个信号可以连接同一个槽函数
    //4、信号和槽函数的参数,必须一一对应
    //5、信号和槽的参数个数 是不是要一致?-信号的参数个数  可以多余槽函数的参数个数

Der Effekt ist wie folgt:

Drücken Sie die Taste zum Essen und lösen Sie die Slot-Funktion ohne Parameter aus

Trennsignal

Mehrere Signale werden miteinander verbunden und wir können sie unabhängig voneinander trennen

	//断开信号  老师信号-->学生treat函数
    //disconnect(zt,teacherSignal2,st,studentSlot2);

6.5. Methode zum Schreiben der Qt4-Version

Q verwendet hier die beiden Makros SIGNAL und SLOT, um die beiden Funktionsnamen in Strings umzuwandeln . Beachten Sie, dass sowohl das Signal als auch der Slot der Funktion connect() Zeichenfolgen akzeptieren. Sobald die Verbindung fehlschlägt, kompiliert Qt4 keine Fehler (da alles eine Zeichenfolge ist und die Kompilierungszeit nicht prüft, ob die Zeichenfolge übereinstimmt) und gibt eine Fehlermeldung aus Fehler zur Laufzeit. Dies wird zweifellos die Instabilität des Programms erhöhen.

Qt5 ist syntaktisch vollständig kompatibel mit Qt4, umgekehrt ist dies jedoch nicht möglich.

  //Qt4版本以前的信号和槽连接方式
    //利用Qt4信号槽 连接无参版本
    //Qt4版本 底层SIGNAL("hungry")  SLOT("treat")
    connect(zt,SIGNAL(hungry(QString)),st,SLOT(treat(QString)));
    //Qt4版本优点:参数直观,缺点 : 类型不做检测
    //Qt5以上 支持Qt4的版本写法,反之不支持

6.6. Lambda-Ausdruck

Lambda-Ausdrücke in C++11 werden zum Definieren und Erstellen anonymer Funktionsobjekte verwendet, um die Programmierung zu vereinfachen. Schauen wir uns zunächst den grundlegenden Aufbau von Lambda-Ausdrücken an:

[函数对象参数](操作符重载函数参数)mutable ->返回值{函数体}

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

1. Funktionsobjektparameter;

[], identifiziert den Anfang eines Lambda . Dieser Teil muss vorhanden sein und kann nicht weggelassen werden . Funktionsobjektparameter werden an den Konstruktor der vom Compiler automatisch generierten Funktionsobjektklasse übergeben. Funktionsobjektparameter können nur lokale Variablen verwenden, die im Bereich des Lambda sichtbar sind, bis der Lambda definiert ist (einschließlich der Klasse, in der sich der Lambda befindet). Funktionsobjektparameter haben die folgenden Formen:

  • Null. Es werden keine Funktionsobjektparameter verwendet.
  • =. Alle sichtbaren lokalen Variablen im Geltungsbereich von Lambda (einschließlich der Klasse, in der sich Lambda befindet) können im Funktionskörper verwendet werden und werden als Wert übergeben (entspricht für uns der automatischen Übergabe aller lokalen Variablen durch den Compiler als Wert).
 [=] (){
        btn->setText("aaaa");
    }();
  • &. Alle sichtbaren lokalen Variablen im Geltungsbereich von Lambda (einschließlich der Klasse, in der sich Lambda befindet) können im Funktionskörper verwendet werden und werden per Referenz übergeben (entspricht für uns der automatischen Übergabe aller lokalen Variablen per Referenz durch den Compiler).

[&] (){
        btn->setText("aaaa");
    }();
  • Das. Mitgliedsvariablen in der Klasse, in der sich Lambda befindet, können im Funktionskörper verwendet werden.
  • A. Übergeben Sie einen Wert. Bei der Wertübergabe kann die Kopie eines übergebenen Werts nicht innerhalb des Funktionskörpers geändert werden, da die Funktion standardmäßig const ist. Um die Kopie einer übergebenen Datei zu ändern , können Sie den veränderbaren Modifikator hinzufügen.
 [btn] (){
        btn->setText("aaaa");
        //bt2->setText("aaaa");//btn2看不到
    }();
  • &A. Übergeben Sie a als Referenz.
  • a,&b. Übergeben Sie a als Wert und b als Referenz.
  • =, &a, &b. Mit Ausnahme von a und b, die als Referenz übergeben werden, werden alle anderen Parameter als Wert übergeben.
  • &, a, b. Mit Ausnahme von a und b, die als Wert übergeben werden, werden andere Parameter als Referenz übergeben.

2. Überladen von Funktionsparametern durch den Operator;

Identifiziert die Parameter des überladenen ()-Operators. Wenn keine Parameter vorhanden sind, kann dieser Teil weggelassen werden. Parameter können als Wert (z. B.: (a,b)) oder als Referenz (z. B.: (&a,&b)) übergeben werden.

3. Die Kennung kann geändert werden;

Bei einer veränderlichen Anweisung kann dieser Teil weggelassen werden. Wenn Funktionsobjektparameter als Wert übergeben werden, kann nach dem Hinzufügen des veränderlichen Modifikators die als Wert übergebene Kopie geändert werden (beachten Sie, dass die Kopie geändert werden kann, nicht der Wert selbst).

QPushButton * myBtn = new QPushButton (this);
    QPushButton * myBtn2 = new QPushButton (this);
    myBtn->move(200,200);
    myBtn2->move(100,100);
    int m = 10;

    //值传递默认是只读的  加上mutable关键字,可以修改值传递拷贝的值,而不是本体
    connect(myBtn,&QPushButton::clicked,this,[m] ()mutable {
        m = 100 + 10; 
        qDebug() << m;
    });
    connect(myBtn2,&QPushButton::clicked,this,[=] () {
        qDebug() << m;
    });

    qDebug() << m;

4. Funktionsrückgabewert;

->Rückgabewerttyp, identifiziert den Typ des Funktionsrückgabewerts. Wenn der Rückgabewert ungültig ist oder nur eine Rückgabestelle im Funktionskörper vorhanden ist (zu diesem Zeitpunkt kann der Compiler automatisch auf den Rückgabewerttyp schließen), ist dieser Teil kann ausgelassen werden.

    int ret=[]()->int {return 1000;}();
    qDebug()<<"ret = "<<ret;

5. Es ist der Funktionskörper;

{} identifiziert die Implementierung der Funktion. Dieser Teil kann nicht weggelassen werden, aber der Funktionskörper kann leer sein.

6. Der Lambda-Ausdruck implementiert eine Schaltfläche zum Schließen des Fensters

//利用lambda表达式 实现点击按钮 关闭窗口
    QPushButton *btn2=new QPushButton;
    btn2->setText("关闭");
    btn2->move(100,0);
    btn2->setParent(this);

    connect(btn2,&QPushButton::clicked,this,[=](){
        this->close();
        emit zt->hungry("宫保鸡丁");

        //btn2->setText("aaaa");//点击变为aaaa
    });

    //Lambda表达式,最常用,[](){}

Acho que você gosta

Origin blog.csdn.net/qq_63388834/article/details/135120966
Recomendado
Clasificación