QT:信号和槽学习笔记

Posted studying~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了QT:信号和槽学习笔记相关的知识,希望对你有一定的参考价值。

1.信号与槽的概念

信号槽是 Qt 框架引以为豪的机制之一。所谓信号槽,实际就是观察者模式。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,去关联对象和信号,并设置当对象接收到这个信号时应该进行什么操作(调用什么函数),这个调用的函数就叫做槽函数,意思是,将想要处理的信号和对象自己 的一个函数(称为槽(slot))绑定来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。

2.系统自带的信号与槽

每个控件都有自带的信号与槽,无需我们定义,当然也可以自己定义

connect()函数最常用的一般形式:connect(sender, signal, receiver, slot); 
参数解释:
sender:发出信号的对象 
signal:发送对象发出的信号 
receiver:接收信号的对象 
slot:接收对象在接收到信号之后所需要调用的函数(槽函数)

代码示例:

#include "widget.h"
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    button = new QPushButton("关闭窗口",this);
    //设置按钮的父对象和文本,并创建按钮
    connect(button,&QPushButton::clicked,this,&QWidget::close);
    //关联信号与对象,当按钮按下、信号被窗口接收到时,关闭窗口
}

3.按钮常用的信号

那么系统自带的信号和槽通常如何查找呢,这个就需要利用帮助文档 了,在帮助文档中比如我们上面的按钮的点击信号,在帮助文档中QPushButton,首先我们可以在Contents中寻找关键字 signals,信号 的意思,但是我们发现并没有找到,这时候我们应该想到也许这个信号 的被父类继承下来的,因此我们去他的父类QAbstractButton中就可以找到该关键字,点击signals索引到系统自带的信号有如下几个:

由此可见,信号和槽一样,都是函数,寻找槽函数和寻找信号一样,只不过他的关键字是slot

4.自定义的槽函数

widget.h头文件

#ifndef WIDGET_H
#define WIDGET_H
#include <QDebug>
#include <QWidget>
#include <QPushButton>
class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    QPushButton *button;
public slots:
    void print()  //自定义的槽函数
    {
        qDebug()<<"chm"<<endl;
    }
};
#endif // WIDGET_H

widget.cpp文件

#include "widget.h"
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    button = new QPushButton("打印",this);
    //设置按钮的父对象和文本,并创建按钮
    connect(button,&QPushButton::clicked,this,&Widget::print);
    //关联信号与对象,当按钮按下、信号被窗口接收到时,关闭窗口
}

5.自定义的信号

案例: 显示两个窗口,两个窗口都包含一个按钮组件,实现 按钮按下时,窗口切换(即一个窗口显示,另一个窗口隐藏)
思路:

widget.h头文件

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QPushButton>
#include "sonwidget.h"
class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    QPushButton  *button1;     //父窗口按钮
    SonWidget *sonwidget;      //子窗口
public slots:                  //定义槽函数
    void changefather();       //隐藏父窗口,显示子窗口
    void changechildr(int a);  //显示父窗口,隐藏子窗口
};
#endif // WIDGET_H

widget.cpp文件

#include "widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    this->setWindowTitle("父窗口");  //设置窗口的名字为父窗口
    this->setFixedSize(500,400);     //设置父窗口的大小
    sonwidget = new SonWidget;       //创建子窗口
    sonwidget->show();               //显示子窗口    
    //1

    button1 = new QPushButton("隐藏父窗口,显示子窗口",this);  
    //创建父窗口的按钮  
    //2

    connect(button1,&QPushButton::clicked,this,&Widget::changefather);    
    /*关联对象w和信号clicked,当按钮button1按下时,发出信号clicked,对象w收到信号后,隐藏父窗口,显示子窗口  */
    //3
    connect(sonwidget,&SonWidget::show_hide_signal,this,&Widget::changechildr);
    /*关联对象w和自定义信号show_hide_signal,当按钮button2按下时,发出信号clicked,对象sonwidget接收信号clicked,在对象sonwidget的槽函数中发出信号show_hide_signal,当对象w收到信号后,显示父窗口,隐藏子窗口  */
    //4
}
void Widget::changefather()
{
    this->hide();
    sonwidget->show();
}
void Widget::changechildr(int a)
{
    qDebug()<<a;
    this->show();
    sonwidget->hide();
}
Widget::~Widget()
{
}

sonwidget.h头文件

#ifndef SONWIDGET_H
#define SONWIDGET_H

#include <QWidget>
#include <QPushButton>
class SonWidget : public QWidget
{
    Q_OBJECT
public:
    explicit SonWidget(QWidget *parent = nullptr);
    QPushButton  *button2; //子窗口的按钮

signals: //定义信号
    //信号没有返回值 可以有参数 信号函数不需要定义 只需要声明
    void show_hide_signal(int a);
public slots: //定义槽函数
    void emit_mysignal(); //发送信号show_hide_signal的函数
};
#endif // SONWIDGET_H

sonwidget.cpp文件

#include "sonwidget.h"

SonWidget::SonWidget(QWidget *parent):QWidget(parent)
{
   this->setWindowTitle("子窗口");  //设置窗口的名字为子窗口
   this->setFixedSize(500,400);    //设置子窗口的大小
   //1
   
   button2 = new QPushButton("显示父窗口,隐藏子窗口",this);  
   //创建子窗口的按钮
   //2
  
   connect(button2,&QPushButton::clicked,this,&SonWidget::emit_mysignal);
   /*关联按钮2和对象sonwidget,当按钮2按下时,发出信号clicked,对象sonwidget接收到信号后,调用槽函数emit_mysignal()发出信号show_hide_signal*/      
   //4
}
void SonWidget::emit_mysignal()
{
   emit show_hide_signal(10); //发出信号show_hide_signal
}

main.cpp文件

#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

6.信号与槽需要注意的事项

自定义信号槽需要注意的事项
l 发送者和接收者都需要是QObject的子类(当然,槽函数是全局函数、Lambda 表达式等无需接收者的时候除外);
l 信号和槽函数返回值是 void
l 信号只需要声明,不需要实现
l 槽函数需要声明也需要实现
l 槽函数是普通的成员函数,作为成员函数,会受到 public、private、 protected 的影响;
l 使用 emit 在恰当的位置发送信号;
l 使用connect()函数连接信号和槽。
l 任何成员函数、static 函数、全局函数和 Lambda 表达式都可以作为槽函数 l 信号槽要求信号和槽的参数一致,所谓一致,是参数类型一致。
l 如果信号和槽的参数不一致,允许的情况是,槽函数的参数可以比信号的少, 即便如此,槽函数存在的那些参数的顺序也必须和信号的前面几个一致起来。这是因为,你可以在槽函数中选择忽略信号传来的数据(也就是槽函数的参数比信号的少)

7.信号槽的拓展

(1)一个信号可以和多个槽相连
如果是这种情况,这些槽会一个接一个的被调用,但是它们的调用顺序是 不确定的。
(2)多个信号可以连接到一个槽
只要任意一个信号发出,这个槽就会被调用。
(3)一个信号可以连接到另外的一个信号
当第一个信号发出时,第二个信号被发出。除此之外,这种信号-信号的形式 和信号-槽的形式没有什么区别。
(4)槽可以被取消链接
这种情况并不经常出现,因为当一个对象delete之后,Qt自动取消所有连接到 这个对象上面的槽。
(5)信号槽可以断开
利用disconnect关键字是可以断开信号槽的
(6)使用Lambda 表达式
在使用 Qt 5 的时候,能够支持 Qt 5 的编译器都是支持 Lambda 表达式的。 在连接信号和槽的时候,槽函数可以使用Lambda表达式的方式进行处理。后面我 们会详细介绍什么是Lambda表达式

8.信号与槽在QT4中的写法


SIGNAL和SLOT不会检查里面的字符串
这里使用了SIGNAL和SLOT这两个宏,将两个函数名转换成了字符串。注意到 connect()函数的 signal 和 slot 都是接受字符串,一旦出现连接不成功的情 况,Qt4是没有编译错误的(因为一切都是字符串,编译期是不检查字符串是否 匹配),而是在运行时给出错误。这无疑会增加程序的不稳定性。

9 lambda表达式


① 函数对象参数; [],标识一个Lambda的开始,这部分必须存在,不能省略。函数对象参数是传 递给编译器自动生成的函数对象类的构造函数的。函数对象参数只能使用那些到 定义Lambda为止时Lambda所在作用范围内可见的局部变量(包括Lambda所在类的 this)。函数对象参数有以下形式:
空。没有使用任何函数对象参数。

=。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括 Lambda所在类的this),并且是值传递方式(相当于编译器自动为我们按值传 递了所有局部变量)。

&。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括 Lambda所在类的this),并且是引用传递方式(相当于编译器自动为我们按引 用传递了所有局部变量)。

this。函数体内可以使用Lambda所在类中的成员变量。

a。将a按值进行传递。按值进行传递时,函数体内不能修改传递进来的a的拷 贝,因为默认情况下函数是const的。要修改传递进来的a的拷贝,可以添加 mutable修饰符。

&a。将a按引用进行传递。

a, &b。将a按值进行传递,b按引用进行传递。

=,&a, &b。除a和b按引用进行传递外,其他参数都按值进行传递。

&, a, b。除a和b按值进行传递外,其他参数都按引用进行传递。

② 操作符重载函数参数; 标识重载的()操作符的参数,没有参数时,这部分可以省略。参数可以通过按值 (如:(a,b))和按引用(如:(&a,&b))两种方式进行传递

③ 可修改标示符; mutable声明,这部分可以省略。按值传递函数对象参数时,加上mutable修饰符 后,可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)。

 QPushButton * myBtn = new QPushButton (this); 
 QPushButton * myBtn2 = new QPushButton (this); 
 myBtn2­>move(100,100);
 int m = 10; 
 connect(myBtn,&QPushButton::clicked,this,[m] ()mutable { 
        m = 100 + 10; 
        qDebug() <<}); 
 connect(myBtn2,&QPushButton::clicked,this,[=] () { 
        qDebug() <<}); 
 qDebug() <<

④ 函数返回值->返回值类型,标识函数返回值的类型,当返回值为void,或者函数体中只有一 处return的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略

⑤ 是函数体; {},标识函数的实现,这部分不能省略,但函数体可以为空。

代码示例:

Widget::Widget(QWidget *parent) : QWidget(parent) 
{
    button = new QPushButton("点我",this); 
    int a=10; 
    int b = 20; 
    //槽函数可以是一个Lambda表达式 8 //Lambda表达式中[]中写的是= ,代表将上面得函数中的局部变量以值传递的方式传入到L ambda表达式  
    //Lambda表达式中[]中写的是& ,代表将上面得函数中的局部变量以引用传递的方式传入 到Lambda表达式 
    //Lambda表达式中[]中写的是a ,代表将上面得函数中的局部变量a以值传递的方式传入 到Lambda表达式 
    // Lambda表达式中[]中写的是a,b ,代表将上面得函数中的局部变量a和b以值传递的方 式传入到Lambda表达式 
    //Lambda表达式中[]中写的是&a ,代表将上面得函数中的局部变量a以引用传递的方式传 入到Lambda表达式 
    //mutable修饰了,作用是可以在Lambda修改传入变量的值 
    //‐>int 代表Lambda表达式返回值是一个int类型
     connect(button,&QPushButton::clicked,this,[&]() mutable>int{ 
             a=100; 
             qDebug()<<a; 
             qDebug()<<b;  
             qDebug()<<"点我啊";
     }); 

}

以上是关于QT:信号和槽学习笔记的主要内容,如果未能解决你的问题,请参考以下文章

QT学习-标准信号窗口和槽

QT学习-标准信号窗口和槽

QT学习_常用类及信号和槽

初见QT---信号和槽

Qt信号和槽的问题

QT信号和槽机制