信号和槽用于对象之间的通信。信号和槽机制是Qt的核心功能,是与其他框架提供的功能最不同的部分。
在GUI编程中,当更改了某个窗口部件的状态时,需要通知另外一个窗口部件做出相应的操作。例如,如果点击了“关闭”按钮,软件需要调用close()函数来关闭当前窗口。
其它开发工具可能使用回调实现这种通信,回调是指向函数的指针,因此如果希望处理函数通知您某些事件,则将指针传递给处理函数的另一个函数(回调),然后处理函数在适当时调用回调。但回调不直观,并且在确保回调参数的类型正确性方面存在问题。
我们通常使用的是信号和槽是一一对应的,其实也可以一对多。
信号:
Qt内部自带了各种窗口部件的信号,最常见的就是按钮的clicked()。但是也可以自己声明信号,声明信号需要使用signals关键字,在signals前面不能用public、private和protected等限定符,信号默认是public函数,可以从任何地方进行发射。
槽:
槽就是普通的C++函数,可以像一般的函数一样使用。声明槽函数要使用slots关键字,可以是private、public或者protected类型的,槽也可以被声明为虚函数。它最大的特点就是可以和信号关联,这是其它函数比不了的。
信号和槽使用注意事项:
1、需要继承自QObject或其子类;
2、 使用信号和槽必须在类声明的最开始处添加Q_OBJECT宏;
3、槽中参数类型要和信号参数的类型相对应,且不能比信号的参数多;
4、信号只有声明,没有定义,且返回值为void类型;
5、槽有声明,也有定义。
信号和槽的关联:
信号和槽关联使用的是QObject类的connect()函数,需要注意的是,connect()函数中信号和槽的参数只能为类型,不能有变量名。
Qt5版本函数原型
static QMetaObject::Connection connect(const QObject *sender, const QMetaMethod &signal,
const QObject *receiver, const QMetaMethod &method,
Qt::ConnectionType type = Qt::AutoConnection);
第一个参数:发射信号的对象
第二个参数:发射的信号
第三个参数:接受信号的对象
第四个参数:要执行的槽
第五个参数:信号和槽的关联方式,默认为自动关联
Qt5版本使用示例
connect(ui->pushButton,&QPushButton::clicked,this,&MainWindow::closeWindow);
Qt4版本函数原型
static QMetaObject::Connection connect(const QObject *sender, const char *signal,
const QObject *receiver, const char *member, Qt::ConnectionType = Qt::AutoConnection);
Qt4版本使用示例
connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(closeWindow()));
两个版本的区别:
Qt4的SIGNAL和SLOT两个宏实际上是将其参数转换为相应的字符串。在编译之前,Qt的moc工具从源代码中提取出所需的元数据,形成一张由使用了signals和slots修饰的所有函数组成的字符串表。connect函数将与信号关联起来的槽的字符串,同这张字符串表中的信息比较,从而知道在发出信号时知道需要调用哪个槽函数。
这种信号和槽的语法造成了两个问题:
1、没有编译器检查;
2、无法使用相容类型的参数。
为了解决这两个问题,Qt5对信号和槽的语法进行了改进。发送信号的类型是Sender,接受信号的类型是Receiver。改进后的优点如下所示:
1、支持编译期检查。信号和槽的拼写检查、参数匹配检查。
2、支持相容类型参数的自动转换。支持使用typedef或者命名空间,还支持使用隐式类型转换。
3、允许连接到任意函数。
尽管新版本有所改进,但是Qt4的语法还是可以用在Qt5的代码中的。
1、打开Qt Creator开发工具,新建一个Qt Widgets Application项目,在主窗口上添加一个pushButton按钮。
2、添加Q_OBJECT宏:使用信号和槽必须在类声明的最开始处添加Q_OBJECT宏,一般自己生成。
Q_OBJECT
3、声明槽函数:和声明普通的成员函数差不多,只不过多了一个slots关键字
private slots:
void closeWindow();
4、在mainwindow.cpp中添加如下代码,实现的功能是点击一下“关闭窗口”按钮,就调用槽,关闭主窗口,相当于执行了窗口右上角那个红叉的操作。
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
setWindowTitle(tr("信号和槽"));
ui->pushButton->setText(tr("关闭窗口"));
//Qt5
connect(ui->pushButton,&QPushButton::clicked,this,&MainWindow::closeWindow);
//Qt4
//connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(closeWindow()));
}
//关闭窗口
void MainWindow::closeWindow()
{
close();
}
MainWindow::~MainWindow()
{
delete ui;
}
效果图
还可以通过Qt Designer添加信号槽,选中按钮点击鼠标右键,选择“转到槽”,进入“转到槽”页面,选择一个信号,系统会自动生成相应的槽函数on_pushButton_cliceked(),由字符串on、部件的对象名和信号名称三部分组成,因此如果用户自定义槽,就不允许使用此种命名方式,以免冲突,此种方法添加信号槽不需要使用关联函数。