前言
信号与槽机制是QT非常核心的东西,通过信号与槽我们可以将不同的部分有机的结合起来,使得各个组件之间的交互简单高效,信号与曹槽像是设计模式中的观察者模式(我自己觉得是这样),只关心信号何时发来,以及做如何相应,组件之间可以异步或者同步去处理事情。信号(Signal):就是在特定情况下被发射的事件,例如PushButton 最常见的信号就是鼠标单击时发射的 clicked() 信号,一个 ComboBox 最常见的信号是选择的列表项变化时发射的 CurrentIndexChanged() 信号。GUI 程序设计的主要内容就是对界面上各组件的信号的响应,只需要知道什么情况下发射哪些信号,合理地去响应和处理这些信号就可以了。
槽(Slot):就是对信号响应的函数。槽就是一个函数,与一般的C++函数是一样的,可以定义在类的任何部分(public、private 或 protected),可以具有任何参数,也可以被直接调用。槽函数与一般的函数不同的是:槽函数可以与一个信号关联,当信号被发射时,关联的槽函数被自动执行。
一、基本形式
官方文档弄下来的:
[static] QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection);
默认是五个参数,关于最后一个参数的具体使用说明参考:信号与槽的连接方式
这个函数的原型就这样,试着看了一下有8个该函数的重载,第一个参数发送者,第二个信号,第三个接收者,第四个接收者的响应函数。
发送者和接受者可以是同一个QObject,在GUI里面我们可以认为一个界面是一个QObject,注意信号与槽的参数要对应上。
界面:
为此我做个一个小demo,自己测试也通了:
如下头文件:
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_pushButton_5_clicked(); //自动生成的槽函数
void pushButon1_clicked(); //自定义的1-4按钮的槽函数
void pushButon2_clicked();
void pushButon3_clicked();
void pushButon4_clicked(int i);
signals:
// void onClicked(); //重载信号
void sig_add(int i);
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
.CPP文件
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "qglobal.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//第一种方式
connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(pushButon1_clicked()));
// connect(ui->pushButton,SIGNAL(clicked),ui->pushButton,SLOT(pushButon1_clicked()));
//第二种方式
connect(ui->pushButton_2,&QPushButton::clicked,this,&::MainWindow::pushButon2_clicked);
// connect(ui->pushButton_2, static_cast<void (QPushButton::*)(bool)>(&QPushButton::clicked), this, SLOT(pushButon2_clicked()));
//第三种方式
// connect(ui->pushButton_2, static_cast<void (QPushButton::*)(bool)>(&QPushButton::clicked),
// this,SLOT(pushButon2_clicked()));
//第2参数转换其实是一个函数指针,等价下面的代码
//void (QPushButton::*ff)(bool)=&QPushButton::clicked;
// connect(ui->pushButton_2, ff, this, SLOT(pushButon2_clicked()));
connect(ui->pushButton_3, QOverload<bool>::of(&QPushButton::clicked),this,&::MainWindow::pushButon3_clicked);
//第四种方式
connect(ui->pushButton_4, QOverload<bool>::of(&QPushButton::clicked),[=](bool check){
ui->textBrowser->setText("按钮4信号绑定成功");
});
// connect(ui->pushButton_4, static_cast<void (QPushButton::*)(bool)>(&QPushButton::clicked), this, [=](bool check){
// ui->textBrowser->setText("按钮4信号绑定成功");
// });
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_5_clicked()
{
ui->textBrowser->setText(QString("按钮5系统生成信号绑定成功"));
}
void MainWindow::pushButon1_clicked()
{
ui->textBrowser->setText("按钮1信号绑定成功");
}
void MainWindow::pushButon2_clicked()
{
ui->textBrowser->setText("按钮2信号绑定成功");
}
void MainWindow::pushButon3_clicked()
{
ui->textBrowser->setText("按钮3信号绑定成功");
emit sig_add(4);
}
void MainWindow::pushButon4_clicked(int i)
{
ui->textBrowser->setText("按钮4信号绑定成功");
}
二、Connect几种方式
方式一
这应该是QT4.0最为传统的方法,其实很多时候用这个还是很清晰的:
//第一种方式
connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(pushButon1_clicked()));
// connect(ui->pushButton,SIGNAL(clicked),ui->pushButton,SLOT(pushButon1_clicked()));
方式二
这应该是QT5.0之后退出来的绑定方法,去掉了之前的宏,感觉更清爽了,我用的还是比较多了
connect(ui->pushButton_2,&QPushButton::clicked,this,&::MainWindow::pushButon2_clicked);
方式三
C++11出来之后,新的特性就出来,static_cast 用法
//第三种方式
// connect(ui->pushButton_2, static_cast<void (QPushButton::*)(bool)>(&QPushButton::clicked),
// this,SLOT(pushButon2_clicked()));
//第2参数转换其实是一个函数指针,等价下面的代码
//void (QPushButton::*ff)(bool)=&QPushButton::clicked;
// connect(ui->pushButton_2, ff, this, SLOT(pushButon2_clicked()));
connect(ui->pushButton_3, QOverload<bool>::of(&QPushButton::clicked),this,&::MainWindow::pushButon3_clicked);
方式四
目前我用得最多的方式,一开始对lamda表达式感觉困惑,用多了发现还真的很方便,最大的好处就是不需要去定义槽函数了,很简洁。
connect(ui->pushButton_4, QOverload<bool>::of(&QPushButton::clicked),[=](){
ui->textBrowser->setText("按钮4信号绑定成功");
});
// connect(ui->pushButton_4, static_cast<void (QPushButton::*)(bool)>(&QPushButton::clicked), this, [=](bool check){
// ui->textBrowser->setText("按钮4信号绑定成功");
// });
方法四。在后面lamda表达式 前面可以使用前面几种方法任何部分,相当于槽函数变成一个表达式。
比如可以这样:
connect(ui->pushButton_2,&QPushButton::clicked,this,[=](bool check){
ui->textBrowser->setText("按钮4信号绑定成功");
其实在正常使用过程中,比如添加一个按钮,需要点击按钮去执行相应的事情,就会右键,选择相对应的信号,系统会自动把你信号与槽函数对应上,很多情况下是不需要去手动去写的。但是在VS下开发QT的时候(有人喜欢VS的界面,同时VS在调试时候比QT更方便),信号与槽需要手动去绑定,这就需要在写槽函数的时候如果按照系统的规则命名,则会自动的帮你和对应的信号连接起来;如果我们在Qt中自定义信号与槽函数的时候最好不要按照系统的命名去做,省去可能绑定的不确定。
总结
QT在不断发展壮大中,其实内部有很多值得学习借鉴的地方,在跨平台编译过程中,Qt有着很大优势,在绑定信号与槽函数的时候一定要注意函数是不是带括号的问题。有可能就会出现不匹配的错误。