当按钮检测到自己被点击,它就发出一个信号signal,当对象对这个信号感兴趣,就会使用连接connect函数,将要处理的信号与自己的函数(也称为)绑定到一起处理这个信号

信号

信号时由于用户对窗口或控件进行了某些操作,导致窗口或控件产生了特定时间,Qt会发出某个信号,以此对用户的挑选做出反映,信号的本质就是事件

  • 鼠标点击,双击
  • 窗口刷新(大小变化)
  • 鼠标移动,鼠标按下,鼠标释放
  • 键盘输入

槽函数是一类特殊的功能函数,在编码过程中也可以作为类的普通成员函数来使用。

connect

在Qt中信号和槽都是独立的个体,通过connect函数进行二进制关联。

connnect(信号sender发出者,函数指针信号signal函数地址,信号接受者receiver,method方法)
//当检测到sender发送signal信号时,receiver对象调用method方法,信号发出之后处理动作
//method槽函数本质是一个回调函数,调用的时机是信号产生之后,
//接受者和发出者的必须实例化

使用

功能实现,关闭窗口
功能分析:

  • 按钮: 信号发出者 -> QPushBUtton 类型
  • 窗口: 信号的接收者和处理者 -> QWidget类型
    需要使用的标准信号槽函数
//单击按钮发出的信号
[signal] void QAbstractButton::clicked(bool checked = false)
// 关闭的窗口的槽函数
[slot] bool QWidget::close();
//点击按钮关闭窗口
connect(ui->closewindow,&QPushButon::clicked,this,&MainWindow::close)

connect()操作一般写在窗口的构造函数中,相当于在事件发生之前qt框架先进行注册,在程序运行过程中,被点击,即会调用对应的槽函数

自定义槽函数

自定义需要的信号和槽

满足条件

  • 编写新的类让其继承qt的某些标准类
  • 新的子类必须从QObject类或者QObject子类进行派生
  • 在定义类的头文件中加入Q_OBJECT宏

自定义信号

  • 信号时类的成员函数,需要使用signals关键字进行声明
  • 返回值须是void类型
  • 参数可以随意指定,信号也支持重载
  • 信号函数只需要生命,不需要定义(没有函数体实现)
  • 发送信号的本质就是调用信号函数,习惯在信号函数前加关键字emit声明信号被发射了
//Qt类想要使用信号槽机制必须要从QObject类派生(直接派生或间接派生)
Class Test : public QObject
{
	Q_OBJECT
signals:
	void testsignal();
	//参数的作用是数据传递,调用信号函数需要指定实参
	// 实参最终会被传递给槽函数
	void testsignal(int a);
}

自定义槽

槽函数就是信号的处理动作,

注意事项

  • 返回值必须是void类型
  • 槽也是函数,支持重载
  • 连接的信号有多少参数函数也需要有多少个参数(信号多的函数将会被忽略)
  • Qt中槽函数的类型可以是类的成员函数,全局函数,静态函数,lambda表达式(匿名函数)
  • 槽函数可以使用关键字slots声明 slots,private slots(不能在类外调用),protected slots

女朋友饿了,于是带她去吃饭

// class GirlFriend
class GirlFriend : public QObject
{
    Q_OBJECT
public:
    explicit GirlFriend(QObject *parent = nullptr);

signals:
    void hungry();	            // 不能表达出想要吃什么
    void hungry(QString msg);	// 可以通过参数表达想要吃什么
};

// class Me
class Me : public QObject
{
    Q_OBJECT
public:
    explicit Me(QObject *parent = nullptr);
    //explicit 防止隐式类型转换,对应的函数不能使用隐式类型转换

public slots:
    // 槽函数
    void eatMeal();             // 不能知道信号发出者要吃什么
    void eatMeal(QString msg);  // 可以知道信号发出者要吃什么
};

隐式类型转换

Class Test 
{
public:
	Test(char * str);
	Test(int a);
private:
	char* m_ste;
	int size;
}
//在调用时可以使用
Test str = "abc"; //会传递成功
Test str1 = 10;  //也会传递成功,但是对用户端存在向str1不清楚的问题,当int前的构造函数加上explict就只能使用下面的方法传递参数,此方法将报错。
Test str1(10);

信号槽的扩展

一个信号可以连接多个槽函数,发送一个信号有多个处理动作,需要写多个connect连接,槽函数的执行顺序是随机的,

//建立连接
connnect
//断开连接
disconnect

Lambda表达式

匿名表达式

[capture](params) opt -> ret{body;};
- capture :捕获列表
- params:参数列表
- opt : 函数选项
- ret : `返回值`类型
- body : 函数体
  • 捕获列表
    • [] 不捕获任何变量
    • [&] 捕获外部作用域的所有变量,并作为引用在函数体内使用(按引用捕获)
    • [=] 捕获外部作用域的所有变量,并作为副本在函数体内使用(按值捕获),拷贝的副本在匿名函数体内是只读的
    • [=,&foo],并按照引用捕获外部变量foo
    • [bar] 按引用捕获bar不变量,同时不捕获其他变量
    • [this] 捕获当前类中的this指针,让lambda表达式拥有和当前类成员 函数同样的访问权限,如果已经使用了&或者= 默认添加此功能
  • 参数列表
  • opt可以省略
    • mutable按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)
    • exception指定函数抛出的异常,如抛出整数类型的异常,可以使用throw()
  • 返回值类型:
    • 标识函数返回值的类型,当返回值为void,或者函数体只有一处return的地方(编译器可以自动判断出返回值类型),这部分可以省略
  • 函数体: 函数的实现

定义与调用

//匿名函数定义,执行这个匿名函数不会被调用
[]()
{
	qDebug() <<"lambda表达式";
}
// 匿名函数定义 + 调用
int ret = [](int a) -> int
{
	return a+1;
}(100);  //100是传递给匿名函数的参数

捕获列表案例

// 在匿名函数外部定义变量
int a=100, b=200, c=300;
// 调用匿名函数
[](){
    // 打印外部变量的值
    qDebug() << "a:" << a << ", b: " << b << ", c:" << c;  // error, 不能使用任何外部变量
}

[&](){
    qDebug() << "hello, 我是一个lambda表达式...";
    qDebug() << "使用引用的方式传递数据: ";
    qDebug() << "a+1:" << a++ << ", b+c= " << b+c;
}();

// 值拷贝的方式使用外部数据
[=](int m, int n)mutable{
    qDebug() << "hello, 我是一个lambda表达式...";
    qDebug() << "使用拷贝的方式传递数据: ";
    // 拷贝的外部数据在函数体内部是只读的, 如果不添加 mutable 关键字是不能修改这些只读数据的值的
    // 添加 mutable 允许修改的数据是拷贝到函数内部的副本, 对外部数据没有影响
    qDebug() << "a+1:" << a++ << ", b+c= " << b+c;
    qDebug() << "m+1: " << ++m << ", n: " << n;
}(1, 2);

实例

  • UI界面建立一个QPushButton,命名为close
  • 定义按钮数字++ 命名为num
  • 定义取消按钮数字++命名为close_num
  • 新建myclass1类,属于QObject 建立信号
signals: //信号,
    void MySignal(void); //不需要实现,调用信号使用emit 信号函数即可
    void Num(int& num); //信号函数实现数值+
  • 新建myclass2类,属于QObject 建立函数
public slots:
    void MySlot(void); //槽函数,需要实现
    void NumSlot(int& num); //信号函数实现数值+
//函数实现 
void MyClass2::MySlot()
{
    //调用MySignal槽函数实现
    qDebug()<<"调用了MysSignal槽函数";
}

void MyClass2::NumSlot(int& num)
{
    num ++;
    qDebug()<<"num: "<<num;
}
  • 主函数实现
//头文件mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "myclass.h"
#include "myclass2.h"
namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_num_clicked();

    void on_close_num_clicked();

private:
    Ui::MainWindow *ui;
    MyClass * mc1;
    MyClass2 * mc2;
    int num =0;

};
#endif // MAINWINDOW_H

//函数实现 
// mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QLabel>
#include <QPushButton>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    //锁定全局大小不可放大
    //setFixedSize(400,300);
    //设置标题
    setWindowTitle("My first No UI");
    mc1 = new MyClass(this);
    mc2 = new MyClass2(this);
    //connect连接信号与槽
    //一个信号可以关联多个槽,多个槽可以被多个信号关联
    connect(ui->close,&QPushButton::clicked,mc2,&MyClass2::MySlot); //调用
    connect(mc1,&MyClass::MySignal,mc2,&MyClass2::MySlot);

    connect(mc1,&MyClass::Num,mc2,&MyClass2::NumSlot);

    connect(ui->num,&QPushButton::clicked,mc1,&MyClass::MySignal);  //当点击数字信号发出,同时调用另一个信号

    //label标签
    QLabel *label = new QLabel(this); //创建label是带this参数,即显示到当前主页面
    label->setText("你好呀!");
    label->move(200,100);  //移动位置,以左上角为起点
    emit mc1->MySignal();
    // 或者使用
    // label->setParent(this);
    //锁定this窗口的大小,不能放大

    //button标签
    QPushButton* button = new QPushButton(this);
    button->setText("点击");
    button->move(200,150);
}

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

void MainWindow::on_num_clicked()
{
    emit mc1->Num(num); //当点击num之后,调用mc1函数的Num信号,并传递num数值
}

void MainWindow::on_close_num_clicked()
{
    //当点击close_num取消数字关联,参数和connect内容相同
    disconnect(mc1,&MyClass::Num,mc2,&MyClass2::NumSlot);
}

参考资料

  • 爱编程的大丙
  • 嵌入式技术公开课 Qt5~8