Qt——信号槽

信号和槽(slot)

类似于OS中的信号回调机制,一旦发射了信号就会触发(执行)槽函数**。在Qt中,信号看起来是函数的样子(之所以说看起来像,是因为实际上Qt暴露给我们的信号或者我们在Qt上自己创建的信号,都会被Qt的MOC处理成另外一种样子),而槽本质上就是一个函数。**

大部分信号都是Qt提供给我们的,但是我们也可以自定义信号。Qt也提供一些槽,但我们经常自己定义槽。对于Qt给我我们的,我们要多去查阅文档,从子类到父类向上寻找

在Qt中,信号和槽的关系是多对多关系,即一个槽可以被多个信号关联,一个信号可以关联多个槽。作为Qt的使用者,我们主要做的就是将信号源,信号,槽,接受对象关联起来,目的是使得用户操作后得到反馈。当信号源发射后信号后就会查找信号和槽的关联关系,执行与之关联的所有槽。


关联信号和槽

关联信号和槽有多种方式,我们下面逐一介绍

Connect函数方式

connect是QObject类(也就是Qt中所有类的祖宗)中的一个静态方法,因此所有类中都可以直接调用它。它常使用(connect有多个重载)的重载形式如下:

cpp 复制代码
static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
                                     const typename QtPrivate::FunctionPointer<Func2>::Object *receiver, Func2 slot,
                                     Qt::ConnectionType type = Qt::AutoConnection)
  • sender:发送信号的控件对象的地址,即设置信号源。
  • signal:要关联的信号,以函数地址的方式传入。
  • receiver:要对信号做出反应的控件对象,以对象指针的方式传入。
  • slot:槽函数,以函数指针的方式传入。
  • type:基本不会用到,暂时不做解释

接下来我们来使用connect:

cpp 复制代码
/*只要点击按钮,按钮上就会出现"哈哈"*/

void Widget::handler()
{
    button->setText("哈哈");
}

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);


    button = new QPushButton(this);//创建按钮对象,button是widget的成员属性
    button->move(100,100);//将button安置到某位置

    //建立关联关系,&QPushButton::clicked是QAbstractButton的给我们提供的信号,
    //继承这个类的其他类型的按钮也有这个信号
    connect(button,&QPushButton::clicked,this,&Widget::handler);//建立关联关系
}
cpp 复制代码
/*只要点击按钮,窗口就会关闭*/

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);


    button = new QPushButton(this);//创建按钮对象,button是widget的成员属性
    button->move(100,100);//将button安置到某位置

    //建立关联关系,&QPushButton::clicked是QAbstractButton的给我们提供的信号,
    //继承这个类的其他类型的按钮也有这个信号
    //&Widget::close是widget提供给我们的槽
    connect(button,&QPushButton::clicked,this,&Widget::close);//建立关联关系
}

顺便提一下,以前connect函数的第二和第四个参数都是char*类型的,因此每次传参都要把实参用宏强转成char*,但是现在重载了更好用的版本。

自动关联方式

右击选中想要设置信号和槽的控件,然后选择'转到槽':

然后我们选择一个按钮发射的信号:

然后我们发现widget的成员函数多了一个(头文件和源文件都写好了):

接下来我么只要在该函数编写信号的处理过程即可,该函数就是pushbutton按钮发出发射信号后所执行的槽函数。并且我们不需要手动关联信号源,信号,接收对象,槽,而是Qt通过命名方式自动关联的:

接下来我们使用一下这种方式:

cpp 复制代码
/*点击按钮,按钮上出现"哈哈"*/

void Widget::handler()
{
    button->setText("哈哈");
}

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

}

Widget::~Widget()
{
    delete ui;
}
void Widget::on_pushButton_clicked()
{
    ui->pushButton->setText("哈哈");
}

当我们查看ui文件生成的类,可以发现:

这个函数就是开启名字匹配的方式。


disconnect解除关联

diconnect函数和connect用法一样,只不过他是把关联关系解除


自定义槽

上面已经使用过自定义槽和Qt提供的槽(如close)。自定义槽其实就是编写一个成员函数然后传参给connect即可,但是在早期,Qt中自定义槽需要在声明时加**"slot"关键字****来帮助Qt进行元编程(现在不需要了,但是加上也可以),比如下面这样:**

槽还可以是一个lambda表达式,调用connect的时候传入即可**:**

cpp 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
     ui->setupUi(this);
     button = new QPushButton(this);
     connect(button,&QPushButton::clicked,this,[this]()->void{this->setWindowTitle("haha");});
}
Widget::~Widget()
{
    delete ui;
}

自定义信号

自定义信号只需要写出函数声明,并告诉Qt这是一个信号(使用signals关键字)即可,但是要保证信号的返回值必须是void。例如这样:

定义信号过后,就可以像正常信号一样关联使用了:

上面程序的意思就是打开窗口后Widget发送信号,然后将Widget内的按钮文本替换成"哈哈"。emit也是Qt添加的关键字,表示发射一个信号,但实际上现在不加emit也可以。


信号和槽的参数

如果要使用参数,信号和槽的对应参数的类型必须一致,但是在个数上信号的参数可以比槽更多(不可以更少),这样可以让槽被更多信号复用,关联。直接举一个使用参数的例子:

在这个例子中,我们实现了按不同按钮就设置不同标题的功能,但是设置标题的槽函数只有一个。


注意

一个类想要定义信号和槽,必须在类声明最开始的地方,写下Q_OBJECT宏:

如果不写这个宏,那么不能定义信号槽,但是使用Qt库提供给我们的信号槽是可以的

相关推荐
海边的Kurisu13 分钟前
Mybatis-Plus | 只做增强不做改变——为简化开发而生
java·开发语言·mybatis
浅念-20 分钟前
C++ 模板进阶
开发语言·数据结构·c++·经验分享·笔记·学习·模版
Omigeq1 小时前
1.2.2 - 采样搜索算法(以RRT和RRT*为例) - Python运动规划库教程(Python Motion Planning)
开发语言·人工智能·python·机器人
m0_531237172 小时前
C语言-操作符进阶
c语言·开发语言
q1234567890982 小时前
FNN sin predict
开发语言·python
沐知全栈开发2 小时前
C++ 多态
开发语言
zihan03212 小时前
若依(RuoYi)框架核心升级:全面适配 SpringData JPA,替换 MyBatis 持久层方案
java·开发语言·前端框架·mybatis·若依升级springboot
先做个垃圾出来………2 小时前
Python字节串“b“前缀
开发语言·python
无限进步_2 小时前
21. 合并两个有序链表 - 题解与详细分析
c语言·开发语言·数据结构·git·链表·github·visual studio
神奇大叔3 小时前
Java 配置文件记录
java·开发语言