QT Tutorial—1.3 Signals and Slots in QT

1. Overview of signals and slots

The signal slot is one of the mechanisms that the Qt framework is proud of. The so-called signal slot is actually the observer model (publish-subscribe model). When something 事件happens, for example, if the button detects that it has been clicked, it will send out a signal. This kind of transmission is purposeless, similar to broadcasting. If an object is interested in this signal, it will use the connect function, which means that it binds the signal it wants to handle with a function of its own (called a slot) to process this signal. In other words, when the signal is sent, the connected slot function will automatically be called back. This is similar to the observer mode: when an event of interest occurs, an operation will be automatically triggered.

1.1 The nature of the signal

The signal is due to the user performing certain operations on the window or control, causing the window or control to generate a specific event. At this time, the corresponding window class of Qt will send a signal to respond to the user's choice.

Therefore, based on the above description, we have reached a conclusion: the essence of signals is events, such as:

  • Button click, double click

  • Window refresh

  • Mouse movement, mouse down, mouse release

  • keyboard input

So in what form is the signal presented to the user in Qt?

  • Which window we operate on, which window can capture these triggered events.
  • For the user to trigger an event, we can get a specific signal sent to us by the Qt framework.
  • The presentation form of the signal is a function, that is to say, when an event occurs, the Qt framework will call a corresponding signal function to notify the user.

在QT中信号的发出者是某个实例化的类对象,对象内部可以进行相关事件的检测。

1.2 The nature of the slot

In Qt 槽函数是一类特殊的功能的函数, during the encoding process 也可以作为类的普通成员函数来使用. They are called slot functions because they have another responsibility to process the signals generated in the Qt framework.

Give a simple example:

My girlfriend said: "I'm hungry!" So I took her to dinner.

The example above is equivalent to a signal sent by my girlfriend. I received the signal and processed it.

  • Girlfriend -> The person who sends the signal, the content of the signal: I'm hungry
  • I -> the person who received the signal and processed the signal, processing action: take her to dinner

在Qt中槽函数的所有者也是某个类的实例对象。

1.3 The relationship between signals and slots

In Qt, the signal and slot functions are independent individuals, and there is no connection between them, but due to certain characteristics, we can connect the two together. For example, if the Cowherd and the Weaver Girl want to meet, they must have a magpie to bridge them. . In Qt, we need QOjbect类the connectfunction in use to associate the two.

QMetaObject::Connection QObject::connect(
    	const QObject *sender, PointerToMemberFunction signal, 
        const QObject *receiver, PointerToMemberFunction method, 
		Qt::ConnectionType type = Qt::AutoConnection);
- 参数:
	- sender: 发出信号的对象
	- signal: 属于sender对象, 信号是一个函数, 这个参数的类型是函数指针, 信号函数地址
    - receiver: 接收信号的对象
	- method: 属于receiver对象, 当检测到sender发出了signal信号, 
              receiver对象调用method方法,信号发出之后的处理动作
                  
// connect函数相对于做了信号处理动作的注册
// 调用conenct函数的sender对象的信号并没有产生, 因此receiver对象的method也不会被调用
// method槽函数本质是一个回调函数, 调用的时机是信号产生之后, 调用是Qt框架来执行的
// connect中的sender和recever两个指针必须被实例化了, 否则conenct不会成功
connect(const QObject *sender, &QObject::signal, 
        const QObject *receiver, &QObject::method);

The signal slot requires that the parameters of the signal and the slot are consistent. The so-called consistency means that the parameter types are consistent. If they are inconsistent, it is allowed that the parameters of the slot function can be less than that of the signal. Even so, the order of the parameters of the slot function must be consistent with the first few of the signal. This is because you can choose to ignore the data passed by the signal in the slot function (that is, the parameter of the slot function is less than that of the signal), but it cannot be said that the signal does not have this data at all. You have to use it in the slot function (that is, the slot The function has more parameters than the signal, which is not allowed).

If the signal slot does not match, or the signal or slot function cannot be found at all, for example, we change it to:

connect(&button, &QPushButton::clicked, &QApplication::quit2);

Since QApplication does not have a function like quit2, there will be compilation errors during compilation:

'quit2' is not a member of QApplication

In this way, using member function pointers, we will not worry about function errors when writing signal slots.

Qt4's way of writing:

int main(int argc, char *argv[]) 
{
    
     
        QApplication a(argc, argv); 
        QPushButton *button = new QPushButton("Quit"); 
        connect(button, SIGNAL(clicked()), &a, SLOT(quit())); 
        button->show(); 
        return a.exec(); 
}

Used hereSIGNALwithSLOTThese two macros convert two function names into strings. Note that the signal and slot of the connect() function both accept strings. Once the connection is unsuccessful, Qt4 has no compilation errors (because everything is a string, the compile time does not check whether the string matches), and Is giving an error at runtime. This will undoubtedly increase the instability of the program. Qt5 is syntactically fully compatible with Qt4.

2. Use of standard signal slots

2.1 Standard signal/slot

In many standard classes provided by Qt, certain specific events triggered by the user can be detected. Therefore, after the user performs these operations, the corresponding signals will be generated inside the triggered class of the event. These signals are all internal to the Qt class. It comes with it, so it is called a standard signal.

Similarly, many functions in many classes of Qt are provided for me, and these functions can also be used as trigger signal processing actions. Functions with such characteristics are called standard slot functions in Qt.

How to find the signals and slots that come with the system? We need to use the help file. In the help file, for example, the click signal of the button above, enter QPushButton in the help file. First of all, we can Contentssearch for keywords signalsand signals in the help file. Means, but we found that we did not find it. At this time, we should look at which signals the current class inherited from the parent class, so we can find the keyword in his parent class QAbstractButton, and click the signals index to the system's own The signals are as follows

Insert picture description here

Query the help document of the parent class

2.2 Use

Function realization: Click the button on the window to close the window

  • Button: Signal sender -> QPushButton
  • Window: Receiver and processor of signal -> QWidget
[signal] void QAbstractButton::clicked(bool checked = false)// 单击按钮发出的信号
[slot] bool QWidget::close();// 关闭窗口的槽函数
connect(ui->closewindow, &QPushButton::clicked, this, &MainWindow::close);// 单击按钮关闭窗口

3. Use of custom signal slots

The signal slots provided by the Qt framework cannot meet the needs of our project in some specific scenarios, so we also design the signals and slots we need, and also use connect() to connect the custom signal slots.

/*
如果想要使用自定义的信号槽, 首先要编写新的类并且让其继承Qt的某些标准类,
我们自己编写的类想要在Qt中使用使用信号槽机制, 那么必须要满足的如下条件: 
	- 这个类必须从QObject类或者是其子类进行派生
	- 在定义类的头文件中加入 Q_OBJECT 宏
*/

// 在头文件派生类的时候,首先像下面那样引入Q_OBJECT宏:
class MyMainWindow : public QWidget
{
    
    
    Q_OBJECT
    ......
}

3.1 Custom signal

/*
要求:
	1. 信号是类的成员函数
	2. 返回值是 void 类型
	3. 信号的名字可以根据实际情况进行指定
	4. 参数可以随意指定, 信号也支持重载
	5. 信号需要使用 signals 关键字进行声明, 使用方法类似于public等关键字
	6. 信号函数只需要声明, 不需要定义(没有函数体实现)
	7. 在程序中发送自定义信号: 发送信号的本质就是调用信号函数
		- 习惯性在信号函数前加关键字: emit
		- emit只是显示的声明一下信号要被发送, 没有特殊含义
		- 底层 emit == #define emit 
*/
// 举例: 信号重载
// Qt中的类想要使用信号槽机制必须要从QObject类派生(直接或间接派生都可以)
class Test : public QObject
{
    
    
    Q_OBJECT
signals:
    void testsignal();
	// 参数的作用是数据传递, 谁调用信号函数谁就指定实参
	// 实参最终会被传递给槽函数
    void testsignal(int a);
};

3.2 Custom slot

The slot function is the signal processing action, and the custom slot function is written in the same way as the custom ordinary function.

/*
要求:
	1. 返回值是 void 类型
	2. 槽也是函数, 因此也支持重载
		- 槽函数需要指定多少个参数, 需要看连接的信号的参数个数
		- 槽函数的参数是用来接收信号发送的数据的, 信号发送的数据就是信号的参数
		- 举例:
			- 信号函数: void testsig(int a, double b);
			- 槽函数:   void testslot(int a, double b);
		- 总结:
			- 槽函数的参数应该和对应的信号的参数个数, 类型一一对应
			- 信号的参数可以大于等于槽函数的参数个数 == 信号传递的数据被忽略了
				- 信号函数: void testsig(int a, double b);
				- 槽函数:   void testslot(int a);
	3. Qt中槽函数的类型:
		- 类的成员函数
		- 全局函数
		- 静态函数
		- lambda表达式(匿名函数)
	4. 槽函数可以使用关键字进行声明: slots (Qt5中slots可以省略不写)
		- public slots:
		- private slots:
		- protected slots:
*/

// 举例
// 类中的这三个函数都可以作为槽函数来使用
class Test : public QObject
{
    
    
public:
    void testSlot();
    static void testFunc();

public slots:
    void testSlot(int id);
};

Examples of scenarios

// 女朋友饿了, 我请她吃饭
// class GirlFriend
// class Me

4. Signal slot expansion

4.1 Use and Expansion of Signal Slot

  • A signal can be connected to multiple slot functions, sending a signal has multiple processing actions

    • Need to write multiple connectconnections
    • The execution order of the slot function is random, and has nothing to do with the call order of the connect function
    • The receiver of the signal can be one object or multiple objects
  • A slot function can connect multiple signals, multiple different signals, and the processing actions are the same

    • Write more connecton it
  • Signal can be connected to signal

    • The receiver of the signal can continue to send a new signal without coming out to receive the signal -> the data is passed, and no processing is performed
   connect(const QObject *sender, &QObject::signal, 
           const QObject *receiver, &QObject::siganl-new);
   ```

- 信号槽是可以断开的

```c
 disconnect(const QObject *sender, &QObject::signal, 
         const QObject *receiver, &QObject::method);

4.2 Connection mode of signal slot

  • Qt5 connection method
 // 语法:
 QMetaObject::Connection QObject::connect(
     	const QObject *sender, PointerToMemberFunction signal, 
         const QObject *receiver, PointerToMemberFunction method, 
 		Qt::ConnectionType type = Qt::AutoConnection);
 
 // 信号和槽函数也就是第2,4个参数传递的是地址, 编译器在编译过程中会对数据的正确性进行检测
 connect(const QObject *sender, &QObject::signal, 
         const QObject *receiver, &QObject::method);
  • Qt4 connection method

    This old signal slot connection method is supported in Qt5, but it is not recommended, because in this way, when the signal slot connection is made, the signal slot function is converted to a string type through a macro SIGNALand SLOT.

    Because the conversion of the signal slot function is carried out by the macro, the data passed into the macro function will not be checked. If the user transmits the wrong data, the compiler will not report an error, but in fact the connection of the signal slot It's not right, the problem can only be found after the program is running, and the problem is not easy to locate.

    // Qt4的信号槽连接方式
    [static] QMetaObject::Connection QObject::connect(
        const QObject *sender, const char *signal, 
        const QObject *receiver, const char *method, 
        Qt::ConnectionType type = Qt::AutoConnection);
    
    connect(const QObject *sender,SIGNAL(信号函数名(参数1, 参数2, ...)),
            const QObject *receiver,SLOT(槽函数名(参数1, 参数2, ...)));
    
  • Application examples

  class Me : public QObject
  {
    
    
      Q_OBJECT
  // Qt4中的槽函数必须这样声明, qt5中的关键字 slots 可以被省略
  public slots:
     	void eat();
      void eat(QString somthing);
  signals:
  	void hungury();
      void hungury(QString somthing);
  };
  
  // 基于上边的类写出解决方案
  // 处理如下逻辑: 我饿了, 我要吃东西
  // 分析: 信号的发出者是我自己, 信号的接收者也是我自己
  Me m;
  // Qt4处理方式
  connect(&m, SIGNAL(eat()), &m, SLOT(hungury()));
  connect(&m, SIGNAL(eat(QString)), &m, SLOT(hungury(QString)));
  
  // Qt5处理方式
  connect(&m, &Me::eat, &m, &Me::hungury);	// error
  /* 
  错误原因:
  	上边的写法之所以错误是因为这个类中信号槽都是重载过的, 信号和槽都是通过函数名去关联函数的地址, 但是
  	这个同名函数对应两块不同的地址, 一个带参, 一个不带参, 因此编译器就不知道去关联哪块地址了,所以如果
  	我饿们在这种时候通过以上方式进行信号槽连接, 编译器就会报错。
  	
  解决方案:
  	我们可以通过定义函数指针的方式指定出函数的具体参数,这样就可以确定函数的具体地址了。
  	定义函数指针指向重载的某个信号或者槽函数,在connect()函数中将函数指针名字作为实参就可以了。
  	举例:
  		void (Me::*func1)(QString) = &Me::eat;		--> func1指向带参的信号
  		void (Me::*func2)() = &Me::hungury;			--> func2指向不带参的槽函数
  */
  // Qt正确的处理方式
  void (Me::*func1)(QString) = &Me::eat;
  void (Me::*func2)(QString) = &Me::hungury;
  connect(&m, func1, &m, func2);
  • to sum up

    • Because the signal slot connection method of Qt4 uses macro functions, the macro function does not perform error detection on the signal slot passed by the user, and it is easy to cause bugs.
    • Qt5's signal slot connection method, passing the address of the signal slot function, the compiler will do error detection, reducing the generation of bugs
    • When the signal slot function is overloaded, Qt4's signal slot connection method is not affected
    • When the signal slot function is overloaded, Qt5 needs to define function pointers for the overloaded signal or slot

4.3 Lambda expression

Lambda expressions are one of the most important and commonly used features of C++11. It is a feature of modern programming languages. It is concise, improves code efficiency and makes programs more flexible. Qt fully supports C++ syntax, so in Lambda expressions can also be used in Qt.

Lambda expression is an anonymous function, the syntax format is as follows:

[capture](params) opt -> ret {
    
    body;};
	- capture: 捕获列表
    - params: 参数列表
    - opt: 函数选项
    - ret: 返回值类型
    - body: 函数体
        
// 示例代码->匿名函数的调用:
int ret = [](int a) -> int
{
    
    
	return a+1;
}(100);

Details about Lambda expressions:

  1. Capture list: capture variables within a certain range
    • []-Do not capture any variables
    • [&]-Capture all variables in the external scope and use them as references in the function body ( 按引用捕获)
    • [=]-Capture all variables in the external scope and use them as a copy in the function body ( 按值捕获)
      • The copied copy is read-only inside the anonymous function body
    • [=, &foo] -Capture all variables in the external scope by value, and capture the external variable foo by reference
    • [bar] -Capture the bar variable by value, while not capturing other variables
    • [&bar] -Capture the bar variable by value, while not capturing other variables
    • [this] -Capture the this pointer in the current class
      • Let the lambda expression have the same access rights as the current class member function
      • If & or = has been used, this option is added by default
  2. Parameter list: the same as the parameter list of a normal function
  3. opt option --> 可以省略
    • mutable: can modify the copy passed in by value (note that the copy can be modified, not the value itself)
    • exception: Specify the exception thrown by the function, such as throwing an integer type exception, you can use throw();
  4. 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 place in the function body (the compiler can automatically infer the return value type at this time), this part can be omitted
  5. Function body:
    • The realization of the function, this part cannot be omitted, but the function body can be empty.

Guess you like

Origin blog.csdn.net/qq_39400113/article/details/113256479