Qt 自定义槽 + 自定义信号(9)
文章目录
- 自定义槽
- 通过纯代码的方式自定义
- 通过编辑图形化界面的方式自定义
- 自定义信号
- 自定义信号代码的实现(无参数版本)
- 边写代码 + 边理解
- 纯代码
简介:我们之前connect连接用的信号和槽函数都是Qt内置的类提供给我们的,拿去用就可以了。但是在实际开发中,绝大部分的槽函数都是需要咱们自己去定义实现的,内置的槽函数不可能满足我们所有需要,不过Qt内置的信号倒是已经满足咱们的需求(文章下面的:自定义信号解释了为啥)。
这篇文章介绍的就是如何去自定义槽函数和信号
,这里其实想把自定义信号(有参数的)一起写了,只不过昨天晚上发生了一件忒烦躁的事,同样写到自定义信号代码的实现,保存后直接关闭浏览器(它也没提升没保存成功)再关机,随后打开手机软件一看天塌了,校园网断了导致写的过程中没自动保存(从零开始)。又花了两三个小时找攻略去恢复,也是没有成功。大家写文章一定要有网,要特别注意保存,没网保存不下来
自定义槽
自定义槽函数是非常关键的,因为开发中大部分的情况都是需要去自定义槽函数的。槽函数
,就是用户触发某个操作之后所要进行的一项业务逻辑
通过纯代码的方式自定义
如果我们想实现一个这样的功能:用户点击按钮之后,将程序窗口的标题改为
自定义槽函数来喽
。之前我们实现了一个,当按钮按下之后(发出信号)该程序窗口就会关闭,此时调用的这个槽函数&widget::close
是widget或它父类
里已经实现了的槽函数,那能满足我们的需求就无需去自定义槽函数了。可是现在的这个槽函数想实现上述要求就唯有自定义,自定义槽,所谓的 槽(slot) 就是一个普通的成员函数,自定义一个槽函数,操作过程和自定义一个普通的成员函数,没啥区别。
先来实现这个自定义槽函数,这里基本上已经成功70%,剩下的就是之前学过的创建按钮对象,connect连接
// widget.h
private slots: // 可写可不写// 自定义一个槽函数void handleClicked();// widget.cpp
void Widget::handleClicked()
{// 当按钮按下去之后,触发信号// 自动将该标题名称该为--自定义槽函数来喽this->setWindowTitle("自定义槽函数来喽");
}
若在以前版本的Qt中,槽函数必须放到
public/private/protected slots:
,此处的slots是Qt自己扩展的关键字(不是C++标准中的语法),Qt 里广泛使用了 元编程技术(基于代码,生成代码),而qmake构建Qt项目的时候,就会调用专门的扫描器,去扫描代码中特定的关键字(slots这种),基于关键字自动生成一大堆相关的代码
创建按钮对象,将其移动到窗口(200,300)处,再connect连接信号和槽
// widget.cpp
// 创建一个按钮对象
QPushButton* myButton = new QPushButton(this);
myButton->setText("按钮");
myButton->move(200, 300);// connect去连接信号和槽
connect(myButton, &QPushButton::clicked, this, &Widget::handleClicked);
通过编辑图形化界面的方式自定义
此时Qt Creator会帮我们在
widget.cpp
中自动生成该函数,我们唯一需要做的就是在该函数中补充实现槽函数功能的代码
void Widget::on_pushButton_clicked()
{// 在点击按钮后,将窗口标题改为按钮已经按下this->setWindowTitle("按钮已经按下");
}
- 可是咱好像发现在各代码文件中,并没有用到connect这个函数啊。那只有信号和槽,但咱没有给它两进行连接啊,这里关键点就在于
on_pushButton_clicked
这个函数的名字。 - 在Qt中除了通过connect连接信号和槽之外,还可以通过函数名字的方式来自动连接。首先函数名开头带个
on
,其次函数名中的pushButton
它是按钮这个类的对象名(objectNmae
),而clicked
它则是这个pushButton
里信号的名字,当函数名符合上述规则后,Qt就能自动的把信号和槽给建立上联系。 - 此外当我们去打开Qt Creator自动生成
ui_widget.h
文件,会看到下面的一段代码。
QMetaObject::connectSlotsByName(Widget);
Qt在调用这个函数之前,它会去检查你这个自定义的槽函数名是不是符合上述的规则,如果符合就会自动触发上述自动连接信号槽的规则,不符合就会出现下面的情况
- 如果我们通过图形化界面去创建控件,还是推荐使用这种快速的方式来连接信号和槽
- 如果我们是通过纯代码的方式去创建控件,你得手动的去写connect(因为在你的代码中并没有去调用
connectSlotsByName
)
自定义信号
Qt中也允许自定义信号,不过自定义信号远远没有自定义槽函数用的多。因为在实际开发中很少会需要自定义信号。说到底这个信号就对应到用户的某个操作(比如鼠标点击按钮啊,键盘输入快捷键啊),在GUI中用户能够进行哪些操作,是可以穷举的(环境是你开发的,你想要用户有哪些操作信号取决于你啊)而Qt 内置的信号基本上已经覆盖到了上述所有可能的用户操作,因此使用Qt内置的信号就足以应付大部分的开发场景了,而且自定义信号,本身代码比较简单的
自定义信号代码的实现(无参数版本)
边写代码 + 边理解
之前我们是通过
connect
连接自定义槽函数
与QPushButton或它父类
提供的信号clicked
(上面的通过纯代码的方式自定义槽函数)去更改程序窗口的标题,那只需要我们点击这个按钮,它所提供的信号直接发出,从而执行槽函数更改标题。现在,我想自定义这个信号,不用它所提供的信号去达到同样的效果,也就是connect连接自定义信号和自定义槽函数,发出信号,直接更改标题
。而所谓Qt的信号它本质上也就是一个稍微特殊的函数,至于哪里特殊,等到代码实现后阐述
咱先去把自定义信号的声明与自定义槽函数的声明和定义给实现出来,至于为啥不写自定义信号函数的定义,也是由于它是一个特殊函数的缘故
// widget.h
signals:// 自定义信号的声明void mySingal();public slots: // 可加可不加// 自定义槽函数void handleMysingal();
signals
;这个也是Qt自己扩展出来的关键字,在qmake的时候会去调用一些代码分析/生成工具,扫描到类中包含signals
这个关键字的时候,此时就会自动地把下面地函数声明认为是信号,并且给这些信号函数自动地生成函数定义,这个是一定得加上地,跟slots不一样。这也是为啥我们不需要去自己实现信号函数的定义
// widget.cpp
void Widget::handleMysingal()
{// 自定义槽函数// 当发出信号后,需要将程序运行的窗口改名为:信号已经发出this->setWindowTitle("信号已经发出");
}
connect(this, &Widget::mySingal, this, &Widget::handleMysingal);
接下来连接我们自己实现的自定义信号和自定义函数,可是咱们连接是连接好了(建立连接,不代表信号发出来了),但这个信号是如何触发(发送)的呢?之前那个按钮还能用鼠标点击触发信号,这里压根就没机会触发信号。
这里我们需要用到
编辑图形化界面的方式再去自定义一个槽函数,在这个槽函数中把我们自定义的信号给发出
,当点击这个按钮后,触发点击信号,再发出信号。这两个信号得分清楚哈!一个是类提供的信号,一个是我们自己自定义的信号。Qt内置的信号都是不需要咱们手动通过代码来触发的,用户在GUI界面进行某些操作就会自动触发对应信号(发射信号的代码已经内置到Qt框架中了
void Widget::on_pushButton_clicked()
{// 发射信号// 这里是自定义槽函数--在点击按钮后,它会将我们自定义的信号mySingal直接发出emit mySingal();
}
emit这个单词它有发射的意思
。其实在Qt 5中emit它啥也没有做,相当于是个摆设,只是告诉你我要发射信号了,真正的操作都包含在mySingal内部生成的函数定义中,也就是即使你不写emit,该信号照样能发出去。不过在实际开发中,还是建议各位把emit都加上,这样代码可读性更高,也更明显的标识出这里是发射自定义的信号了
步骤 | 操作 |
---|---|
1 | 点击按钮,触发 QPushButton::clicked 信号 |
2 | 连接到槽函数 Widget::on_pushButton_clicked() |
3 | 在槽函数中发射自定义信号 emit mySingal() |
4 | 连接到槽函数 Widget::handleMySingal() |
- 咱们的 Widget 虽然还没有定义任何信号,由于继承自
QWidget
和QObject
,这俩类里面已经提供了一些信号了,可以直接使用。 - 之前不是说信号是一类非常特殊的函数
- 程序员只要写出函数声明并且告诉Qt,这是一个
"信号"
即可,而这个函数的定义是Qt在编译过程中自动生成的(自动生成的过程,程序员无法干预)。信号在 Qt 中是特殊的机制,Qt生成的信号函数的实现,要配合Qt 框架做很多既定的操作。
- 作为信号函数,这个函数的返回值,必须是void,有没有参数都可以,甚至也可以支持重载。
- 程序员只要写出函数声明并且告诉Qt,这是一个
纯代码
// widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();signals:// 自定义信号的声明void mySingal();public slots: // 可加可不加// 自定义槽函数void handleMysingal();private slots:void on_pushButton_clicked();private:Ui::Widget *ui;
};
#endif // WIDGET_H
// widget.cpp
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 连接自定义信号和自定义槽函数connect(this, &Widget::mySingal, this, &Widget::handleMysingal);
}Widget::~Widget()
{delete ui;
}void Widget::handleMysingal()
{// 自定义槽函数// 当发出信号后,需要将程序运行的窗口改名为:信号已经发出this->setWindowTitle("信号已经发出");
}void Widget::on_pushButton_clicked()
{// 发射信号// 这里是自定义槽函数--在点击按钮后,它会将我们自定义的信号mySingal直接发出emit mySingal();
}