Signal and slot mechanism

abstract

Signal slot is one of the mechanisms that Qt framework is proud of. The so-called signal slot is actually the observer mode. When an event occurs, for example, the button detects that it has been clicked, It will send a signal. This kind of sending has no purpose and is similar to broadcasting. If an object is interested in this signal, it will use the connect function, which means to connect the signal to be processed with its own function (called slot) (slot)) binding to process this signal. That is, when the signal is sent, the connected slot function will be called back automatically. This is similar to observer mode: when an event of interest occurs, an operation will be triggered automatically. (it is mentioned here that the signal slot of Qt uses additional processing, which is not the implementation of GoF's classic observer mode.)

1, Signal and slot mechanism analysis

Because the explanations in the book are too complicated, I choose to introduce this concept with a story of Aladdin's magic lamp, and first pull out the story:

However, we can find that the human friction lamp and the divine lamp are two unrelated things (for example, the human friction is not necessarily the divine lamp, and the divine lamp is not necessarily due to friction). Therefore, we can associate the two with the connect function.

connect(Signal sending object, signal sending object, signal receiving object, function to be called after receiving signal (slot function))

The most common general form of the connect() function:

connect(sender, signal(Signal), receiver, slot(Slot));

The signal slot requires that the parameters of the signal slot and the slot are consistent. The so-called consistency means that the parameter types are consistent. In case of inconsistency, it is allowed that the parameters of the slot function can be less than those of the signal. Even so, the order of those parameters of the slot function must be consistent with the first few of the signal. (some of the transmitted signal parameters can be ignored), but it can't be said that the signal doesn't have this data at all. You have to use it in the slot function (that is, the slot function has more parameters than the signal, which is not allowed)

💜💜 Example demonstration: (click the button to close the window)

According to the above steps, first pull out these functions:

  //Create first button
    QPushButton *btn=new QPushButton;
    //BTN - > show() cannot be used// Show is a top-level pop-up control
    //Let btn display in widget window
    btn->setParent(this);//this pointer to the current object (i.e. the address of the widget)
    //Display text
    btn->setText("close window");

    //Click the button to close the window
    connect(btn,&QPushButton::clicked,this,&QWidget::close);

2, Custom signal slot

Using connect() allows us to connect the signals and slots provided by the system. However, Qt's signal slot mechanism not only uses the part provided by the system, but also allows us to design our own signals and slots.

Let's take a look at the story of using Qt signal slot to realize Aladdin:

First, we need to build two classes: Aladdin class (custom signal) and magic lamp class (slot function). Both classes should inherit from QObject class.

Then build the scene: after dark, Aladdin will rub the magic lamp (custom signal trigger signal), and the magic lamp (slot function response signal) will appear to realize the wish.


one ️⃣ Define custom signals
Custom signal: only need to be declared in Aladdin There is no need to implement the signals under H. (the return value is void, which can have parameters and can be overloaded)

three ️⃣ Connect the signal and slot with connect

After defining the signal and slot, first in the widget H (header file of window class) and the trigger function (it's dark).

Then in widget Create an object in app (source file) and implement the trigger function, and then connect the signal with the slot with connect

Finally, call trigger function can be implemented.


Results achieved:

3, How to solve the overload of custom signal and slot?

As we have said above, custom signals and slots can be overloaded with parameters, but how to use connect Association after overloading (or with parameters)?

Then the above story of Aladdin's magic lamp: (if we bring parameters to the custom signal and slot, that is, when we make a wish for a mobile phone during friction, the magic lamp will give Aladdin a mobile phone when it appears)

💜💜 Code implementation:

Custom signal (only declaration, not implementation):

//Aladdin.h

signals:
    void chafe(QString wishes);//Declare custom signal (with parameters)
    void chafe();//Without parameters

Slot function (both declared and implemented):

//magicLamp.h

public:
    explicit magicLamp(QObject *parent = nullptr);
   void Godappears(QString wishes);//Create slot function (with parameters)
   void Godappears();//Create a slot function without parameters
//magicLamp.cpp

//Implement slot function (no parameters)
void magicLamp::Godappears()
{
  qDebug() <<"Djinn appears, realize the wish! !";
}
//Implement slot function (with parameters)
void magicLamp::Godappears(QString wishes)
{
    qDebug()<<"Djinn appears,here you are:"<<wishes;
}

Because the slot function is overloaded, when using connect for association, you need to use the pointer function to obtain the function address with parameters.

//Widget.cpp (partial)

   //Get the address of the function with parameters with the function pointer
    void (Aladdin::*AladdinSign)(QString)=&Aladdin::chafe; 
    void (magicLamp::*magicLampSign)(QString)=&magicLamp::Godappears;

Note: when declaring the function address of a member function, you need to put the scope of the member function in front of the pointer.

Widget. Complete code of CPP:

#include "widget.h"
#include "ui_widget.h"
#Include < QPushButton > / / header file of button control

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
   //Create an object of Aladdin class (directly specify the parent class as widget)
    this->ald=new Aladdin(this);
   //Create an object of the magic lamp class
    this->mlp=new magicLamp(this);
    //Get the address of the function with parameters with the function pointer
    void (Aladdin::*AladdinSign)(QString)=&Aladdin::chafe;
    void (magicLamp::*magicLampSign)(QString)=&magicLamp::Godappears;
    //Connecting signal and slot magicLampSign
    connect(ald,AladdinSign,mlp,magicLampSign);
    //Call trigger function
    dark();
}

Widget::~Widget()
{
    delete ui;
}

void Widget::dark()
{
    //Trigger friction function
    emit ald->chafe("iphone 12");
}

Effect achieved:

If you want to convert QString to char * (i.e. eliminate ""): first convert to QByteArray (. toUtf8()) and then char * (. data()).

That is, modify the slot function:

void magicLamp::Godappears(QString wishes)
{
    qDebug()<<"Djinn appears,here you are:"<<wishes.toUtf8().data();
}

4, Signal connection signal

The above codes are automatically triggered, that is, when the program runs, it makes a wish automatically. Then can I use the button to control the trigger signal (connect the signal with the signal).

Code implementation:

#include "widget.h"
#include "ui_widget.h"
#Include < QPushButton > / / header file of button control

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
   //Create an object of Aladdin class (directly specify the parent class as widget)
    this->ald=new Aladdin(this);
   //Create an object of the magic lamp class
    this->mlp=new magicLamp(this);
    //Get the address of parameterless function with function pointer
    void (Aladdin::*AladdinSign)(void)=&Aladdin::chafe;
    void (magicLamp::*magicLampSign)(void)=&magicLamp::Godappears;

    //Button to create trigger signal
     QPushButton *btn=new QPushButton("Make a wish",this);
     //Reset window size (resize is the method under widget)
     this->resize(400,400);
     //Button signal connection no parameter signal
     connect(btn,&QPushButton::clicked,ald,AladdinSign);
     //Connecting signal and slot magicLampSign
     connect(ald,AladdinSign,mlp,magicLampSign);


}


Note: call disconnect if you need to disconnect the signal.

disconnect(ald,AladdinSign,mlp,magicLampSign);

Summary:

Signals can be connected to signals
One signal can connect multiple slots (click the button to trigger the signal and close the window)
Multiple signals can be connected to the same slot (for example, multiple buttons can close the window)
The custom slot function can be written as:

  1. Any member function of a class
  2. Static function
  3. Global function
  4. lambda expressions

In the final analysis: the principle of connection is that the parameters of signal and slot must correspond one by one!!

5, lambad expression

Lambda expressions in C++11 are used to define and create anonymous function objects to simplify programming. First, let's look at the basic composition of lambda expression:

[Function object parameters](Operator overloading function parameters)mutable or exception->Return value
{
Function body
}

one ️⃣ Function object parameters

[] identifies the beginning of a Lambda. This part must exist and cannot be omitted. Function object parameters are passed to the constructor of the function object class automatically generated by the compiler. Function object parameters can only use local variables that are visible within the scope of Lambda (including this of Lambda's class) until Lambda is defined. Function object parameters have the following forms: (commonly used is = & this a)

Empty. No function object arguments were used.
=. All visible local variables within the scope of lambda (including this of lambda's class) can be used in the function body, and the value is passed (equivalent to that the compiler automatically passes all local variables by value for us). &. Lambda can be used in the function body
All visible local variables within the scope (including this of Lambda's class) are passed by reference (equivalent to that the compiler automatically
We passed all local variables by reference). this. In the function body, you can use the member variables in the class of Lambda. a. Will a
Pass by value. When passing by value, the copy of a passed in cannot be modified in the function body, because the function is const by default. To modify the a passed in
The mutable modifier can be added& a. Pass a by reference. a, &b. Pass a by value and B by reference.
=,&a, &b. Except that a and B are passed by reference, other parameters are passed by value. &, a, b. Except that a and B are passed by value, other parameters are passed by reference.

How to modify the name of a button with a lambda expression:

//Function object parameters:=
[=](){
         btn->setText("aaaa");
     }();

//Function object parameters: a
     [btn](){
          btn->setText("aaaa");
   //Since the function object parameter is btn, you can only operate on btn. Introducing btn1 will report an error
          //btn1->setText("bbbb");
      }();

Note: not adding () is only a declaration of lambad expression, and adding () is a call to it. (since btn is invisible within the scope of lambad when it is created, you need to use = to make the lambad expression recognize the local variable btn)

two ️⃣ Operator overloading function parameters

Identify the parameters of the overloaded () operator. If there are no parameters, this part can be omitted. Parameters can be passed by value (such as: (a,b)) and by reference (such as: (& A, & B))

three ️⃣ Modifiable identifier

Mutable declaration, which can be omitted. When passing function object parameters by value, the mutable modifier can be added to modify the copy passed in by value (note that the copy can be modified, not the value itself)

four ️⃣ Error throw identifier

Exception declaration, which can also be omitted. The exception declaration is used to specify the exceptions thrown by the function, such as throwing integer type exceptions. throw(int) can be used

five ️⃣ Function return value

->The return value type identifies the type of the return value of the function. When the return value is void or there is only one return in the function body (at this time, the compiler can automatically infer the return value type), this part can be omitted.

For example, int a ret to receive the result returned by the lanbda expression (Note: use - > to identify the type of the returned value)

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

six ️⃣ Function body

{} identifies the implementation of the function. This part cannot be omitted, but the function body can be empty

💛💛💛 Slot functions can also be processed using Lambda expressions:

   //Create two buttons
 QPushButton *myBtn=new QPushButton(this);
 QPushButton *myBtn1=new QPushButton(this);
 //Move second button
 myBtn1->move(100,100);
 int m =10;
 //Use the slot function (lambda expression) to change the copy value of m
 connect(myBtn,&QPushButton::clicked,this,[m]()mutable{m=100+10;qDebug()<<m;});
 connect(myBtn1,&QPushButton::clicked,this,[=]() {qDebug()<<m;});
 qDebug()<<m;

}

For the first connect function:

connect(myBtn,&QPushButton::clicked,this,[m]()mutable{m=100+10;qDebug()<<m;});

When the function object parameter is m, if you want to modify the copy passed in by the value, you need to add the mutable keyword. (note that only the copy can be modified, not the value itself)

Generally speaking, keywords are rarely added to lambda expressions, unless you have any special needs.

in general:

  • Using lambda to write slot function, you can write multiple functions in the function body of lambda expression. (for example, m=100+10; and qdebug() < < m;)
  • lambda common expressions:
[=](){}

Added by dull1554 on Tue, 04 Jan 2022 17:49:58 +0200