QT-自定义信号槽与connect函数

本文最后更新于:1 年前

自定义信号函数

信号函数需要符合以下条件:

  • 定义在某个类中,该类直接或间接继承自 QObject 类
  • 用 signals 关键字修饰
  • 函数只需要声明,不需要定义(实现)
  • 函数的返回值类型为 void,参数的类型和个数不限

对于 Qt 提供的信号函数,其底层已经设置好了信号发出的时机,例如按下鼠标时、点击 Enter 回车键时等等。

对于自定义的信号,需要自行指定信号发出的时机,这就需要用到 emit 关键字。emit 是 Qt 在 C++ 基础上扩展的一个关键字,专门用来发射信号。

1
2
3
4
5
6
7
8
9
10
11
12
13
class MyWidget:public QWidget{
//Q_OBJECT 是一个宏,添加它才能正常使用 Qt 的信号和槽机制
Q_OBJECT
//自定义信号函数
signals:
void MySignal(QString mess);
public:
void emitSignal(){
emit MySignal(message);//当程序中执行 emitSingal() 函数时,就会发出 MySignal 信号,message 属性的值也会随信号一同发出,对应的槽函数可以接收到 message 的值。
}
private:
QString message;
};

自定义槽函数

槽函数需要符合以下条件:

  • 槽函数既可以是普通的全局函数、也可以是类的成员函数、静态成员函数、友元函数、虚函数,还可以用 lambda 表达式表示

    Qt4 中的槽函数只能是 slots 修饰的类成员函数,Qt5 中取消了这一限制

  • 槽函数的返回值必须和信号函数相同,由于信号函数的返回值一定是 void,所以槽函数的返回值也必须为 void

  • 对于带参的信号函数,槽函数可以选择接收所有参数,但参数的类型、顺序、个数都必须与信号函数相同;也可以选择接收前几个参数,这些参数的类型、顺序都必须与信号函数相同;还可以选择不接受任何参数

  • 槽函数的参数不能有默认值

下面的例子展示了响应 MySignal 信号的自定义槽函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class MyWidget:public QWidget{
//Q_OBJECT 是一个宏,添加它才能正常使用 Qt 的信号和槽机制
Q_OBJECT
signals:
void MySignal(QString mess1,QString mess2);
public:
void emitSignal(){
emit MySignal(message1,message2);
}

//类的成员函数
void recSlot1(QString mess){
qDebug() << "执行 recSlot1() 成员函数,输出" << mess;
}

//指明定义的是槽函数
public slots:
void recSlot2(QString mess1,QString mess2){
qDebug() << "执行 recSlot2() 槽函数,输出"<< mess1 << " " << mess2;
}
public:
QString message1;
QString message2;
};

//全局函数
void recSlot3(){
qDebug() << "执行 recSlot3() 全局函数";
}

程序中,重点关注 recSlot1()、recSlot2()、recSlot3() 这 3 个函数:

  • recSlot1() 是 MyWidget 类内部的 public 成员函数,可以当做槽函数使用;

    QObject::connect(&mywidget,&MyWidget::MySignal,&mywidget,&MyWidget::recSlot1);

  • recSlot2() 位于 MyWidget 类的内部,修饰它的关键字是 public slots。slots 和 emit 一样,是 Qt 扩展的一个关键字,专门用来修饰槽函数。也就是说,recSlot2() 是 MyWidget 类中的槽函数。

    QObject::connect(&mywidget,&MyWidget::MySignal,&mywidget,&MyWidget::recSlot2);

  • recSlot3() 是全局函数,可以当做槽函数使用

    QObject::connect(&mywidget,&MyWidget::MySignal,&recSlot3);

connect函数

用法一:使用SIGNAL和SLOT宏

connect函数用来连接信号和槽函数,在 Qt5 版本之前,其最常用的语法格式是:

QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection)

各个参数的含义分别是:

  • sender:指定信号的发送者;
  • signal:指定信号函数,信号函数必须用 SIGNAL() 宏括起来;
  • reveiver:指定信号的接收者;
  • method:指定接收信号的槽函数,槽函数必须用 SLOT() 宏括起来;
  • type: 用于指定关联方式,默认的关联方式为 Qt::AutoConnection,通常不需要手动设定。

用法二:使用&类名::函数名

Qt5 版本中,不再使用 SIGNAL() 和 SLOT() 宏,实现代码为:

connect(&But, &QPushButton::clicked, &widget, &QWidget::close);

可以看到,新版 connect() 函数指定信号函数和槽函数的语法格式是 &+函数所在类+函数名

用法三:Lambda表达式

在Pro项目文件中加入 CONFIG += C++ 11后,可以在connect函数中使用Lambda表达式,直接编写信号发射后要执行的代码,不需要定义槽函数。

1
2
3
connect(ui->pushButton,&QPushButton::clicked,[=](void){
        ui->label->setText("Hello Qt");
    });

connect在只有三个参数的情况下,默认第三个槽函数的对象是本类this,也就是第三个参数this被省略了。

用法四:省略connect函数,自动连接信号和槽

Qt信号和槽函数的命名是有规则的,组成为 on_对象名_信号,就可以省略connect而直接实现信号和槽的连接。以按钮为例:

void on_pushButton_clicked();



此外,一个 connect() 函数只能关联一个信号函数和一个槽函数,但程序中可以包含多个 connect() 函数,用于实现以下几种效果

  • 关联多个信号函数和多个槽函数;
  • 一个信号函数可以关联多个槽函数,当信号发出时,与之关联的槽函数会一个接一个地执行,但它们执行的顺序是随机的,无法人为指定哪个先执行、哪个后执行;
  • 多个信号函数可以关联同一个槽函数,无论哪个信号发出,槽函数都会执行。