一.信号和槽概述
谈及信号,很容易联想到在Linux系统中所分享到的信号。那么Linux信号和Qt信息有什么不同?
在 Qt 中,用户和控件的每次交互过程称为⼀个事件。比如 "用户点击按钮" 是⼀个事件,"用户关 闭窗口" 也是⼀个事件。每个事件都会发出⼀个信号,例如用户点击按钮会发出 "按钮被点击" 的信 号,用户关闭窗口会发出 "窗口被关闭" 的信号。
Qt信号同样包含三个要素:
- 由哪个控件发出。
- 信号的类型,如点击按钮信号,移动输入框光标信号等等。
- 信号的处理方式:槽(slot),即函数。
槽函数,就是对信号进行响应的函数,将信号与对应的槽函数关联之后,只要信号发出,就能执行对应的槽函数功能。
二.connect关联
在Qt中,想要将信号和槽函数关联起来,可以使用函数connect。
connect(const QObject *sender,
const char *signal,
const QObject *receiver,
const char *method,
Qt::ConnectionType type = Qt::AutoConnection);
sender:信号的发送者;signal:发送的信号(信号函数);receiver:信号的接收者;method:接收信号的槽函数;type: 用于指定关联方式,默认关联方式为 Qt::AutoConnection,通常不需要手动设定。
disconnect()函数可以断开信号和槽的连接,参数与connect一致。
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPushButton *myButten = new QPushButton(this);
myButten->setText("关闭");
myButten->move(200,200);
connect(myButten,&QPushButton::clicked,this,&Widget::close);
}
分析这段代码,QPushButten为按钮类,随后为其命名为“关闭”,并设置其在界面中的位置。
在connect函数中,定义的按钮myButten为信号的发送者,clicked为信号函数,即“按钮被按下”这个信号,this为信号的执行者,即Widget对象,也就是界面本身,close为接收到信号后要执行的槽函数,即关闭界面。
运行程序,点击关闭即可关闭界面。
这里扩展一点知识:
实际上上述所给出的connect函数的参数,是老版本的Qt中的connect函数,能够看到参数2和参数4,是两个char* 类型的函数指针,但实际上所传入的函数指针,并不一定都是char*类型,在老版本中,需要通过宏来修改两个参数的类型,这样太过麻烦,所以在Qt5版本之后,将connect的参数改为了模版类型,并且带有了一定的参数检查功能,即参数2和参数4必须分别是参数1和参数3两个对象的成员函数,否则就会编译出错。
三.自定义信号和槽
在上述操作中,我们所使用的信号函数和槽函数,都是对应的控件中所自带的,但是自带的这些信号和槽,肯定不能满足我们所有的需求,所以我们还需要进行自定义。
1.自定义槽函数
槽函数的本质,还是类中的一个成员函数。
(1)代码方式
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPushButton *myButten = new QPushButton(this);
myButten->setText("按钮");
myButten->move(200,200);
connect(myButten,&QPushButton::clicked,this,&Widget::handleClick);
}
void Widget::handleClick()
{
this->setWindowTitle("按钮被按下!");
}
注意成员函数必须要在.h文件中先进行声明。
这里我们编写了一个名为handleClick的成员函数,并由connect进行连接,代码的含义为:点击按钮就会修改界面的标题为“按钮被按下!”,结果如下:
(2)界面操作
我们在界面设计中设计一个按钮,然后右键点击,能够看到一个“转为槽”的选项,点击,就能得到当前的QPushButten控件,及其父控件的所有的信号函数:
我们选择clicked信号,点击OK,随后就会跳转到.cpp文件,并创建一个对应的函数,同时该函数也会在.h文件中进行声明:
随后就可以在该函数中进行代码编写:
void Widget::on_pushButton_clicked()
{
this->setWindowTitle("按钮已经按下");
}
运行代码,结果如下:
能够看出,通过界面设计的槽函数,不需要使用connect函数进行连接,也可以直接实现槽函数对信号的响应。
这与槽函数的名字有关,这样一个规则的名字命名方式,Qt就能自动把信号和槽函数建立联系。
on_pushButton_clicked
2.自定义信号
自定义信号比较少见,在实际开发中很少会需要自定义信号,因为在GUI中,用户能够进行的操作,是可以穷举的,Qt内置的信号,基本上已经覆盖到了所有可能得用户操作。
信号是一类非常特殊的函数,定义自定义信号函数时,我们只需给出函数声明即可,信号函数的返回值必须是void,有没有参数都可以。
此时,我们需要告诉Qt,这是一个“信号”,通过在类中使用signals关键字,当代码扫描到该关键字时,就会自动把下面的函数认为是信号,随后给这些函数自动生成函数定义。
Qt内置的信号,都不需要我们手动通过代码来触发,那么自定义出的信号,该如何触发呢?
通过emit关键字 + 信号函数,可以触发自定义信号,当然直接调用信号函数,也可以触发信号。
signals:
void mySignal();
public:
void handleMySignal();
在类中声明信号。
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
connect(this,&Widget::mySignal,this,&Widget::handleMySignal);
emit mySignal();
//mySignal();
}
void Widget::handleMySignal()
{
this->setWindowTitle("触发自定义信号");
}
随后进行连接并触发信号,结果如下:
3.信号和槽传参
我们在使用信号时,也可以设置参数,当信号设置参数之后,对应连接的槽函数也必须拥有相同的类型的参数,参数的个数可以不一致,但信号的参数必须比槽函数的个数多。
信号传参,本质是将参数的内容传递给槽函数,从而槽函数就可以使用参数内容:
signals:
void mySignal(const QString& text);
public:
void handleMySignal(const QString& text);
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
connect(this,&Widget::mySignal,this,&Widget::handleMySignal);
emit mySignal("信号传参");
}
void Widget::handleMySignal(const QString& text)
{
this->setWindowTitle(text);
}
结果如下: