QT基础:信号槽
1. 信号和槽概述
信号和槽是QT自行定义的一种通信机制,实现对象之间的数据交互
信号和槽的连接是通过connect函数实现的,该函数将发送者的信号与接收者的槽函数连接起来。当发送者发出信号时,所有与之连接的槽函数都会被调用。这种连接可以是多对多的,即一个信号可以连接多个槽,一个槽也可以接收多个信号。
信号的本质就是事件
-
当用户在图形用户界面(GUI)中进行某些操作时,比如点击按钮、移动鼠标或输入键盘等,这些操作会产生特定的事件。Qt框架中的窗口或控件在识别到这些事件后,会发出相应的信号。
-
信号的呈现形式就是函数, 也就是说某个事件产生了, Qt框架就会调用某个对应的信号函数, 通知使用者。
注意:信号函数可以只声明,不需要实现
槽函数是一类特殊的功能的函数
槽函数个职责就是对Qt框架中产生的信号进行处理。
槽和普通成员函数几乎没有太多区别,可以是共有的、保护的或私有的,可以被重载,也可以被覆盖,其参数可以是任意类型,并可以像普通成员函数一样调用
2. 信号和槽的使用
如何实现点击按钮后关闭整个窗口呢?
功能实现: 点击窗口上的按钮, 关闭窗口
功能分析:
- 按钮: 信号发出者 -> QPushButton 类型
- 窗口: 信号的接收者和处理者 -> QWidget 类型
需要使用的标准信号槽函数
// 单击按钮发出的信号
[signal] void QAbstractButton::clicked(bool checked = false)
// 关闭窗口的槽函数
[slot] bool QWidget::close();
只需要写一句代码就能实现这个效果
// 单击按钮关闭窗口
connect(ui->closewindow, &QPushButton::clicked, this, &MainWindow::close);
2.1 connect()函数
QMetaObject::Connection QObject::connect(
const QObject *sender, PointerToMemberFunction signal,
const QObject *receiver, PointerToMemberFunction method,
Qt::ConnectionType type = Qt::AutoConnection);
// PointerToMemberFunction 是一个指向函数地址的指针
/*参数:
- sender: 发出信号的对象
- signal: 属于sender对象, 信号是一个函数, 这个参数的类型是函数
指针, 信号函数地址
- receiver: 信号接收者
- method: 属于receiver对象, 当检测到sender发出了signal信号,
receiver对象调用method方法,信号发出之后的处理动作
*/
// 参数 signal 和 method 都是函数地址, 因此简化之后的 connect() 如下:
connect(const QObject *sender, &QObject::signal,
const QObject *receiver, &QObject::method);
注意事项:
connect函数相对于做了信号处理动作的注册- 槽函数本质是一个回调函数,。当信号产生之后, 由Qt框架来执行对槽函数的调用
connect中的sender和recever两个指针必须被实例化了, 否则conenct不会成功connect()操作一般写在窗口的构造函数中, 相当于事件产生之前在qt框架中先进行注册
使用方式
- 一个信号可以连接多个槽函数, 发送一个信号有多个处理动作
- 需要写多个
connect()连接 - 槽函数的执行顺序是随机的, 和connect函数的调用顺序没有关系
- 信号的接收者可以是一个对象, 也可以是多个对象
- 需要写多个
- 一个槽函数可以连接多个信号, 多个不同的信号, 处理动作是相同的
- 需要写多个
connect()连接
- 需要写多个
信号可以连接信号
- 信号接收者可以不处理接收的信号, 而是继续发射新的信号,这相当于传递了数据, 并没有对数据进行处理
//信号之间可以连接(信号级联)
QObject::connect(A1, SIGNAL(sigfun(int)), A2, SIGNAL(sigfun(int)) );
信号槽是可以断开的
disconnect(const QObject *sender, &QObject::signal,
const QObject *receiver, &QObject::method);
- 注意:
connect连接的信号函数和槽函数的参数需要匹配- 槽函数的参数数量可以少于信号函数的参数数量
2.2 信号槽的连接方式
使用
SIGNAL和SLOT宏
SIGNAL和SLOT宏无法检查函数名拼写是否正确,信号和槽的参数类型是否匹配,编译时无法发现错误,增加了调试难度
connect(sender, SIGNAL(signalSignature()), receiver, SLOT(slotSignature()));
// 单击按钮关闭窗口
connect(ui->testWindow, SIGNAL(clicked(void)), this, SLOT(close(void)) );
信号槽新语法
Qt 5开始支持新的语法,使用函数指针来作为参数。
推荐使用新的信号槽语法,它更加类型安全,并且支持Lambda表达式
connect(sender, &SenderClass::signal, receiver, &ReceiverClass::slot);
// 单击按钮关闭窗口
connect(ui->closewindow, &QPushButton::clicked, this, &MainWindow::close);
// Lambda表达式
connect(ui->closewindow, &QPushButton::clicked, [=](){ this->close(); });
2.2.1 信号槽函数存在重载
// 信号和槽函数存在重载
class myclass : public QDialog{
...
signals:
void sig(QString const& str);
void sig();
public slots:
void slo(QString const& str);
void slo();
...};
- 当槽函数存在重载时,使用函数指针作为参数,不能直接对函数名取地址
方式一、定义函数指针
// 举例:
void (myclass::*sigString)(const QString &) = &myclass::sig; //信号函数指针
void (myclass::*sloString)(const QString &) = &myclass::slo; //槽函数指针
// 使用定义的函数指针完成信号槽的连接
connect(this, sigString, this, sloString);
方式二、static_cast函数指针类型转换
通过显示类型转换可以让编译器找到对应的重载函数
connect(this,static_cast<void (myclass::*)(const QString &)>(&myclass::sig),
this,static_cast<void (myclass::*)(const QString &)>(&myclass::slo));
3. 自定义信号槽
Qt 的元对象系统是实现信号和槽机制的基础。每个使用信号和槽的 Qt 类都需要从 QObject 类继承,并且使用 Q_OBJECT 宏。这个宏在编译时会生成一些额外的代码,包括元对象信息(meta-object information),这些信息在运行时用于信号和槽的连接。
自定义信号槽, 需要满足一些条件, 并且有些事项也需要注意:
- 要编写新的类并且让其继承Qt的某些标准类
- 这个新的子类必须从
QObject类或者是QObject子类进行派生 - 在定义类的头文件中加入
Q_OBJECT宏
// 在头文件派生类的时候,首先像下面那样引入Q_OBJECT宏:
class MyMainWindow : public QWidget
{
Q_OBJECT
......
}
3.1 自定义信号
信号函数只有声明, 无需定义
class X:public QObject{
signals:
void signal_func(...); // 信号函数
};
自定义信号的要求和注意事项:
-
信号是类的成员函数,支持重载,返回值必须是
void类型 -
信号需要使用
signals关键字进行声明- 早期版本中(如Qt 4),信号被默认设置为受保护的(protected)
- 从Qt 5.0开始,信号被更改为公有(public)成员
-
信号函数只需要声明, 不需要定义(没有函数体实现)
-
发送信号的本质就是调用信号函数
- 关键字
emit显示声明信号被发射
- 关键字
// 举例: 信号重载
// Qt中的类想要使用信号槽机制必须要从QObject类派生(直接或间接派生都可以)
class Test : public QObject
{
Q_OBJECT
signals:
void signal_func();
// 参数的作用是数据传递, 谁调用信号函数谁就指定实参
// 实参最终会被传递给槽函数
void signal_func(int a);
};
4.2 自定义槽
自定义槽函数和自定义的普通函数写法是一样的。
从Qt 5开始,对于槽函数的声明,可以省略slots关键字。普通成员函数也可以连接到信号,不再需要声明为槽。
// 槽函数书写格式举例
// 类中的这三个函数都可以作为槽函数来使用
class Test : public QObject
{
public:
void testSlot();
static void testFunc();
public slots:
void testSlot(int id);
};
自定义槽的要求和注意事项:
-
槽函数支持重载,返回值必须是
void类型 -
槽函数的参数是用来接收信号函数的参数
-
槽函数的类型可以是
类的成员函数、全局函数、静态函数、Lambda表达式(匿名函数)
5. Lambda表达式
Lambda表达式是 C++ 11 最重要也是最常用的特性之一,是现代编程语言的一个特点,简洁,提高了代码的效率并且可以使程序更加灵活,Qt是完全支持c++语法的, 因此在Qt中也可以使用Lambda表达式。
5.1 语法格式
Lambda表达式就是一个匿名函数, 语法格式如下:
[capture](params) opt -> ret {body;};
- capture: 捕获列表
- params: 参数列表
- opt: 函数选项
- ret: 返回值类型
- body: 函数体
关于Lambda表达式的细节介绍:
- 捕获列表: 捕获一定范围内的变量
[]- 不捕捉任何变量[&]- 捕获外部作用域中所有变量, 并作为引用在函数体内使用 (按引用捕获)[=]- 捕获外部作用域中所有变量, 并作为副本在函数体内使用 (按值捕获)- 拷贝的副本在匿名函数体内部是只读的
[=, &foo]- 按值捕获外部作用域中所有变量, 并按照引用捕获外部变量foo[bar]- 按值捕获bar变量, 同时不捕获其他变量[&bar]- 按引用捕获 bar 变量, 同时不捕获其他变量[this]- 捕获当前类中的this指针- 让
lambda表达式拥有和当前类成员函数同样的访问权限 - 如果已经使用了
&或者=, 默认添加此选项
- 让
- 参数列表: 和普通函数的参数列表一样
- opt 选项 –>
可以省略mutable: 可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)exception: 指定函数抛出的异常,如抛出整数类型的异常,可以使用throw();
- 返回值类型:
- 标识函数返回值的类型,当返回值为
void,或者函数体中只有一处return的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略
- 标识函数返回值的类型,当返回值为
- 函数体:
- 函数的实现,这部分不能省略,但函数体可以为空。
5.2 定义和调用
因为Lambda表达式是一个匿名函数, 因此是没有函数声明的, 直接在程序中进行代码的定义即可, 但是如果只定义匿名函数在程序执行过程中是不会被调用的。
// 匿名函数的定义, 程序执行这个匿名函数是不会被调用的
[](){
qDebug() << "hello, 我是一个lambda表达式...";
};
// 匿名函数的定义+调用:
int ret = [](int a) -> int
{
return a+1;
}(100); // 100是传递给匿名函数的参数
在
Lambda表达式的捕获列表中也就是[]内部添加不同的关键字, 就可以在函数体中使用外部变量了。
// 在匿名函数外部定义变量
int a=100, b=200, c=300;
// 调用匿名函数
[](){
// 打印外部变量的值
qDebug() << "a:" << a << ", b: " << b << ", c:" << c; // error, 不能使用任何外部变量
}
[&](){
qDebug() << "hello, 我是一个lambda表达式...";
qDebug() << "使用引用的方式传递数据: ";
qDebug() << "a+1:" << a++ << ", b+c= " << b+c;
}();
// 值拷贝的方式使用外部数据
[=](int m, int n)mutable{
qDebug() << "hello, 我是一个lambda表达式...";
qDebug() << "使用拷贝的方式传递数据: ";
// 拷贝的外部数据在函数体内部是只读的, 如果不添加 mutable 关键字是不能修改这些只读数据的值的
// 添加 mutable 允许修改的数据是拷贝到函数内部的副本, 对外部数据没有影响
qDebug() << "a+1:" << a++ << ", b+c= " << b+c;
qDebug() << "m+1: " << ++m << ", n: " << n;
}(1, 2);

浙公网安备 33010602011771号