Qt_信号与槽_connect_自定义槽函数_自定义信号_2


一、信号和槽的概述

Linux信号Signal,系统内部的通知机制,进程间通信的方式。
信号源:谁发的信号
信号的类型:哪种类别的信号
信号的处理方式:注册信号处理函数,在信号被触发的时候自动调用执行

Qt中的信号和Linux中的信号,虽然不是一样的概念,但是确实有相似之处。

Qt中,谈到信号,也是涉及到了三个要素。

  • 信号源:由哪个控件发出的信号。
  • 信号的类型:用户进行不同的操作,就可能触发不同的信号。
  • 点击按钮,触发点击信号。
    在输入框移动光标,触发移动光标的信号。
    咱们写的GUI程序,就是要让用户进行操作,就是要和用户进行交互,这个过程中就需要关注,用户当前的操作具体是个什么样的操作。

信号的处理方式:槽(slot)=>函数
Qt中可以使用connect这样的函数,把一个信号和一个槽关联起来。
后续只要信号触发了,Qt就会自动的执行槽函数。
所谓的“槽函数”本质上也是一种“回调函数”(callback)

Qt中,一定是先关联信号和槽,然后再触发这个信号,顺序不能颠倒,否则信号就不知道如何处理了。

二、connect

这个函数和LinuxTCP socket中建立连接的函数,没有任何关系,只是名字恰巧一样了
是QObject提供的静态的成员函数。
Qt中提供的的这些类,本身是存在一定继承关系的:

Widget的子类:
QPushButton
QLineEdit
QTextEdit
QRadioButton

Widget是QObject的子类,而connect是QObject的。

1.函数体

connect()函数原型:
最后一个参数暂时不考虑,也很少用到这个。

QObject::connect(const QObject *sender,
 const char *signal,
  const QObject *receiver,
   const char *method,
    Qt::ConnectionType type = Qt::AutoConnection)
  • sender:信号的发送者
  • signal:信号的类型
  • receiver:哪个对象负责处理
  • method:这个对象该怎么处理

2.使用样例

界面上包含一个按钮,用户点击按钮,则关闭窗口。
所谓“信号”也是Qt中的对象,内部提供的一些成员函数。
在这里插入图片描述

click是一个slot函数,作用就是调用的时候相当于点击了一下按钮
clicked(过去分词形式,完事了,点完了),才是要触发的点击信号。

Widget::close():是QWidget内置的槽函数,Widget继承自QWidget,也就继承了父亲的槽函数。

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QPushButton* button = new QPushButton(this);
    button->setText("关闭");
    button->move(200,200);

    connect(button,&QPushButton::clicked,this,&Widget::close);
}

Widget::~Widget()
{
    delete ui;
}


在这里插入图片描述
点击后关闭。

3.问题

1.问题1

1.咋知道的QPushButton有个clicked信号?咋知道的Qwigget有一个close槽?Qt里面到底提供了哪些内置的信号和槽可以让我们直接使用?

多看文档!!!
如果在翻阅文档的时候,如果在当前类中没有找到对应的线索,不妨看看这个类的父类。
在这里插入图片描述
abstract抽象的
Qt中会提供好几种按钮
这些按钮之间存在一些“共性”内容
就把这些共性的东西,提取出来,放到了QAbstractButton类里面。

在这里插入图片描述
在这里插入图片描述
当这个按钮被激活,就会发送这个信号。
emitted:发送
activated:激活

2.问题2

const char *signal 和const char method
在实际上用的 :
connect(button,&QPushButton::clicked,this,&Widget::close);
,&QPushButton::clicked ->函数指针void(*)()
&Widget::close->函数指针bool(*)()
char
和函数指针是同一个东西吗?
不是一个东西。

C++中,不允许你使用两个不同的指针类型,互相赋值
这个函数声明,是以前的旧版本的Qt的connect函数的声明。以前版本中,传参的写法和现在其实也是有区别的。
此时,给信号参数传参,要搭配一个SIGNAL宏,给槽参数传参,搭配一个SLOT宏。
connect(button,SIGNAL(&QPushButton::clicked),this,SLOT(&Widget::close));

Qt 5 开始,对上诉写法做出了简化,不在需要写宏了
给connect提供了重载版本,重载版本中,第二个参数和第四个参数达成了泛型参数,允许我们传入任意类型的函数指针了。

在这里插入图片描述

三、自定义槽函数和信号

1.代码自定义槽函数

所谓的自定义一个槽函数,操作过程和自定义一个普通的成员函数,没啥区别。
在以前Qt中,槽函数必须放到
public/private/protected slots;

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    void  handleClicked();
private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QPushButton* button = new QPushButton(this);
    button->setText("按钮");
    button->move(100,100);

    connect(button,&QPushButton::clicked,this,&Widget::handleClicked);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::handleClicked()
{
    //按下按钮,修改一下窗口标题。
    this->setWindowTitle("按钮已经按下");
}


在这里插入图片描述

2.图形化一键创建槽函数

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include "widget.h"
#include "ui_widget.h"

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

}

Widget::~Widget()
{
    delete ui;
}


void Widget::on_pushButton_clicked()
{
    this->setWindowTitle("按钮已经按下");
}

在这里插入图片描述
问题没有connect是如何连接?
在Qt中除了通过connect来连接信号槽之外,还可以通过函数名的方式来自动连接。
如示例:void Widget::on_pushButton_clicked()
on_按钮的objectName_信号的名字
当函数名符合上述规则之后,Qt就能自动的把信号和槽给建立上联系。

3.自定义信号 - signals - emit

Qt中也允许自定义信号。
自定义槽函数,非常关键,开发中大部分情况都需要自定义槽函数的。
槽函数,就是用户触发某个操作之后,要进行的业务逻辑。
自定义信号,比较少见,实际开发中很少需要自定义信号。
信号就对应到用户的某个操作,在GUI,用户能够进行哪些操作,是可以穷举的,Qt内置的信号,基本上已经覆盖到了上述所有可能的用户操作。
因此使用Qt内置的信号,就足以应对大部分的开发场景。
自定义信号,本身代码比较简单的。

所谓的Qt的信号,本质上也就是一个“函数”
Qt5以及更高版本中,槽函数和普通的成员函数之间,没啥差别了
但是信号则是一类非常特殊的函数。
程序员只要写出函数声明,并且告诉Qt,这是一个“信号”即可。
这个函数的定义,是Qt在编译过程中,自动生成的。(自动生成的过程,程序员无法干预)
信号在Qt中是特殊的机制,Qt生成的信号函数的实现,要配合Qt框架做很多既定的操作。

作为信号函数,这个函数的返回值,必须是void
有没有参数都可以,甚至也可以支持重载。

signals:
这个也是Qt自己扩展出来的关键字,qmake的时候,调用一些代码的分析/生成工具,扫描到类中包含signals这个关键字的时候,此时,就会自动的把下面的函数声明认为是信号,并且给这些信号函数自动的生成函数定义。

如何才能触发出自定义的信号?
Qt内置的信号,都不需要我们手动通过代码来触发,用户在GUI,进行某些操作,就会自动触发对应的信号(发射信号的代码已经内置到Qt框架中了)

关键字 emit - 发射
emit 信号函数()
发送自定义的信号。
widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

signals:
    void  mySignal();

public:
    void handleMySignal();

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::mySignal,this,&Widget::handleMySignal);
    emit mySignal();
}

Widget::~Widget()
{
    delete ui;
}

void Widget::handleMySignal()
{
    this->setWindowTitle("处理自定义信号");
}


在这里插入图片描述

信号和槽也可以带参数:
widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

signals:
    void  mySignal(const QString& text,const QString& text2);

public:
    void handleMySignal(const QString& text );

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::mySignal,this,&Widget::handleMySignal);
    emit mySignal("把标题设置为标题2");
}

Widget::~Widget()
{
    delete ui;
}

void Widget::handleMySignal(const QString& text)
{
    this->setWindowTitle(text);
}


在这里插入图片描述

注意:
信号函数参数可以比槽函数参数多,但反过来不行。
在这里插入图片描述
Qt中如果要让某个类能够使用信号槽,(可以在类中定义信号和槽函数),则必须要在类最开始的地方,写下Q_OBJECt宏。

4.信号和槽存在的意义

Qt中谈到的信号和槽“多对多”就和数据库的多对多非常类似
学生(学号 姓名…)
1 张三
2 李四
3 王五
课程(课程编号,课程名字…)
100 语文
101 数学
102 英语

一个学生,可以选择多门课程来学习,一门课程,也可以被多个学生来选择。
connect的作用,就相当于关联表,将学生和课程关联起来。

综上Qt引入信号槽机制,最本质的目的(初心)就是为了能够让信号和槽之间按照“多对多”的方式来进行关联,其他的GUI开发的过程中,“多对多”这件事,其实是一个“伪需求”,实际开发很少会用到,绝大部分情况,一对一就够用了。

5.关于信号槽两补充知识点

1.使用disconnect来断开信号槽的连接。

disconnect使用的方式和connect是非常类似的。
在这里插入图片描述
widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    void handleClick();
    void handleClick2();
private slots:
    void on_pushButton_2_clicked();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include<QDebug>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    connect(ui->pushButton,&QPushButton::clicked,this,&Widget::handleClick);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::handleClick()
{
    this->setWindowTitle("修改窗口的标题");
    qDebug()<<"handleClick";
}

void Widget::handleClick2()
{
    this->setWindowTitle("修改窗口的标题2");
    qDebug()<<"handleClick2";
}


void Widget::on_pushButton_2_clicked()
{
    //1.先断开pushButton原理的信号槽
    disconnect(ui->pushButton,&QPushButton::clicked,this,&Widget::handleClick);
    connect(ui->pushButton,&QPushButton::clicked,this,&Widget::handleClick2);
}

在这里插入图片描述

2.定义槽函数的时候,也是可以使用lambda表达式

#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>
#include<QDebug>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QPushButton* button = new QPushButton(this);
    button->setText("按钮");
    button->move(200,200);
    connect(button,&QPushButton::clicked,this,[button,this](){
       qDebug()<<"lambda被执行了!";
       button->move(300,300);
       this->setWindowTitle("我被按了");
    });

}

Widget::~Widget()
{
    delete ui;
}

在这里插入图片描述
注:
如果当前lambda里面想使用更多的外层变量咋办?
写作[=]
这个写法的含义就是把上层作用域中所有的变量名都给捕获进来。

另外也要确认捕获的lambda内部的变量是有意义的,回调函数执行时机是不确定的(用户啥时候点击按钮不知道的),无论何时用户点击了按钮,捕获到的变量都能正常使用。

lambda语法是C++中引入的,对于Qt5以及跟高版本,默认就是按照C++11来编译的。如果使用Qt4或者更老的版本,就需要手动在.pro文件中加上C++11的编译选项: CONFIG+=C++11

四、小结

1.信号槽是啥,尤其是和Linux中信号进行对比。
a.信号源
b.信号的类型
c.信号的处理方式
2.信号槽使用
connect
3.如何查阅文档
一个控件,内置了哪些信号,信号都是何时触发。
一个控件,内置了哪些槽,槽都是什么作用
很有可能需要的信号槽,还得到这个类的父类/爷爷类/祖宗类去进行查询
4.自定义槽函数
本质上就是自定义一个普通的成员函数
还可以让Qt Creator自动生成,(虽然没有显示connect,但是可以通过函数名字特定规则来完成自动连接)
5.自定义信号
信号本质就是成员函数(函数的定义是Qt自己生成的,咱们只需要写函数声明)
signals:定义关键字中
emit来完成信号的发射(emit也可以省略)
6.信号和槽还可以带参数
发送信号的时候,把参数传递给对应的槽
信号参数和槽的参数要一致
a.类型匹配
b.个数,信号的参数要多于槽的参数
7.信号槽存在的意义
解耦合
多对多效果(非常类似于mysql中的多对多)
8.disconnect使用方式
9.lambda表达式,简化槽函数的定义

  • 27
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值