通过一个案例来理解到信号和槽

使用"按钮"切换hello world

Push Button控件实现

在这里插入图片描述

注意

  • 在QT Creator创建一个控件的时候,就会为该控件分配一个objectName属性,并要求这个属性里面的指在这个界面中是唯一的(可以自动生成也可以自己修改)。qmake在预处理.ui文件时,会根据这里的objectName生成对应的C++代码,在C++代码中,QPushButton对象的变量名就是objectName属性中所对应的内容,这个变量就是ui属性中的成员变量。
    在这里插入图片描述
  • QT中的connect是QObject这个类所提供的静态成员函数,这个函数的作用就是连接信号和槽
connect(ui->pushButton,&QPushButton::clicked,this,&Widget::handleclick);
第一个参数ui->pushButto:谁发出了信号,信号的发出者是谁?
第二个参数&QPushButton::clicked:发出了什么信号,只要出发了这个信号,就会执行handleclick函数
第三个参数this:谁来处理这个信号
第四个参数&Widget::handleclick:怎么处理这个信号(槽slot)

纯代码方式实现

直接在堆上创建对象,但是如果指向该对象的指针不是成员函数,那么handleclick成员函数对信号进行处理的时候就无法访问该对象,因此需要把指向该对象的指针修改成Widget类的成员函数。
在这里插入图片描述

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    button = new QPushButton(this);
    connect(button,&QPushButton::clicked,this,&Widget::handleclick);
}
void Widget::handleclick()
{
    if(button->text() == QString("hello world"))
    {
        button->setText("qwer");
    }else
        {
         button->setText("hello world");
    }
}

总结

  • 通过纯代码版本,按钮对象是我们new出来的,为了保证其他函数能够访问到这个变量,就需要把按钮对象设定为WidGet类的成员变量。
  • 通过创建按钮控件,QT会自动帮我们new按钮对象,而且这个按钮对象已经是ui中的一个成员变量了,也无需作为Widget的成员。

QT中窗口坐标体系

坐标体系:以左上角为原点(0,0),X向右增加,Y向下增加。
在这里插入图片描述
对于嵌套窗口,其坐标是相对于父窗口/父元素/父控件来说的。Widget类没有父元素(NULL),就相当于整个显示器界面就是父元素。
坐标的单位是像素。
示例:使用Qt中的坐标系设置控件的位置;
在这里插入图片描述
在这里插入图片描述

信号和槽概述

在Qt中,信号和槽是一种用于对象间通信的机制,它是Qt框架的核心特性之一。

信号(Signals

  • 信号是由对象在特定事件发生时发射(emit)的特殊成员函数
  • 信号只有声明,没有实现
  • 信号通常表示"发生了某事"的事件通知

槽(Slots

  • 槽是普通的成员函数,可以被信号调用作为响应
  • 槽可以有实现,可以执行具体的操作
  • 槽的访问权限可以是public、protected或private

信号和槽的使用

在 Qt 中,QObject 类提供了⼀个静态成员函数 connect() ,该函数专门来关联指定的信号函数和槽函数。
注意:QObject 是 Qt 内置的父类. Qt 中提供的很多类都是直接或者间接继承自 QObject.

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,通常不需要手动设定。
一个简单的例子:界面上有一个按钮,用户(信号发出者)点击(信号)按钮,就关闭了窗口(信号处理)。
所谓“信号”其实也是QT对象中的一些内部成员函数。
在这里插入图片描述
注意:connect()中的第一二个参数是要匹配的,选择了button对象,就要使用QPushButton中的信号,同理,第二三参数也是一样的。

查看内置信号和槽

作为新手,我们是如何知道一个类有哪些信号?哪些槽函数呢?
答案就是查文档!
例如我们想查看QPushButton类有哪些对象,就可以Qt 帮助文档来查询。

在这里插入图片描述
在查询的过程中并没有发现clicked()信号,这个时候可以查看一下QPushButton的父类,QT总存在很多抽象类,将一些控件中的相同的功能给抽象出来。
查看父类QAbstractButton
在这里插入图片描述
这里的 clicked() 就是要找的信号。槽函数的寻找方式和信号⼀样,只不过它的关键字是 slot

自定义信号和槽

基本语法

在 Qt 中,允许自定义信号的发送方以及接收方,即可以自定义信号函数和槽函数。但是对于自定义的信号函数和槽函数有⼀定的书写规范。

自定义信号函数书写规范
(1)自定义信号函数必须写到 “signals” 下;
(2)返回值为 void只需要声明,不需要实现
(3)可以有参数,也可以发生重载

自定义槽函数书写规范
(1)早期的 Qt 版本要求槽函数必须写到 “public slots” 下,但是现在⾼级版本的 Qt 允许写到类的"public" 作用域中或者全局下;
(2)返回值为 void,需要声明,也需要实现;
(3)可以有参数,也可以发生重载

发送信号:使用 “emit” 关键字发送信号 。“emit” 是⼀个空的宏。“emit” 其实是可选的,没有什么含义,只是为了提醒开发人员。

自定义信号

信号是一类非常特殊的函数,我们只需要给出函数声明,定义是在Qt的编译过程中自动生成的,无法干预。这是因为Qt生成信号的操作,要配合Qt框架做很多既定的操作。

//widget.h
signals:
void mySingnal();
//widget.cpp
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QPushButton* button = new QPushButton(this);
    button->setText("按钮");
    connect(this,&Widget::mySingnal,this,&Widget::handleMySignal);//连接信号和槽
}
void Widget::handleMySignal()
{
    qDebug()<<"signal";
}

但是程序运行起来以后,如何触发信号呢?
使用emit关键字!

//widget.h
signals:
void mySingnal();
private slots:
void handleMySignal();

//widget.cpp
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QPushButton* button = new QPushButton(this);
    button->setText("按钮");
    connect(button,&QPushButton::clicked,this,&Widget::handle);
    connect(this,&Widget::mySingnal,this,&Widget::handleMySignal);
}

void Widget::handle()
{
    emit this->mySingnal();//发射信号,可以不写emit,mySingnal()中有emit,可以直接调用
}

void Widget::handleMySignal()
{
    qDebug()<<"signal";
}

在这里插入图片描述

自定义槽函数

所谓自定义槽函数,其实和定义一个不同的成员函数没有什么区别。
自定义槽有两种方式,一种是自己手动写代码,还有一种是让QT Creator帮我们自动生成。
第一种,自己手动写槽函数。

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QPushButton* button = new QPushButton(this);
    connect(button,&QPushButton::clicked,this,&Widget::handle); //handle是自己写的Widget类的成员函数
}

void Widget::handle()
{
    //......
}

第二种自定义槽函数的方法
在这里插入图片描述

在这里插入图片描述
Qt Creator会帮我们自动生成槽函数的定义和声明

//widget.h
private slots:  
void on_pushButton_clicked();
注意:slots是QT的一个关键字,qmake构建项目的时候,就会调用专门的扫面器,扫面代码中特定的关键字(slots),基于关键字自动生成一大堆相关的代码。
//widget.cpp
void Widget::on_pushButton_clicked()
{
    //自己填充内容
}

总结:除了connect来连接信号和槽,Qt中还可以通过函数名称来连接信号和槽

自动生成槽函数的名称有⼀定的规则。槽函数的命名规则为:on_XXX_SSS,其中:
1、以 " on " 开头,中间使⽤下划线连接起来;
2、" XXX " 表示的是对象名(控件的 objectName 属性)。
3、" SSS " 表示的是对应的信号。
如:" on_pushButton_clicked() " ,pushButton 代表的是对象名,clicked 是对应的信号。

带参数的信号和槽

自定义的信号和槽函数可以带参数,但是参数的类型必须匹配,并且信号的参数可以大于等于槽函数的参数。因为一个槽函数可以绑定多个信号,同时一个信号也可以绑定多个槽函数,形成“多对多”的关系。此时发射信号的时候,就会把信号中的参数传递到绑定的槽函数中。
在这里插入图片描述
在这里插入图片描述

注意:如果一个类中需要自定义信号和槽函数,需要在类中最开始的地方写下Q_OBJECT宏,这个是硬性规定,编译程序时,这个宏会展开很多的代码。
在这里插入图片描述

信号和槽断开连接

可以使用disconnect来断开连接,使用和connect类似。
在这里插入图片描述
在这里插入图片描述
在第一次打印标题设置为标题一之后,mySingnal1handleMySignal1的信号就断开了。

lambda定义槽函数

Qt5 在 Qt4 的基础上提⾼了信号与槽的灵活性,允许使用任意函数作为槽函数。
但如果想⽅便的编写槽函数,比如在编写函数时连函数名都不想定义,则可以通过 Lambda表达式 来达到这个目的。
Lambda表达式 是 C++11 增加的特性。C++11 中的 Lambda表达式 用于定义并创建匿名的函数对象,以简化编程工作。
Lambda表达式 的语法格式如下:

[ capture ] ( params ) opt -> ret { 
 Function body; 
};

capture:捕获列表
params :参数表
opt :函数选项
ret :返回值类型
Function body :函数体

[ ] : 标识⼀个 Lambda表达式 的开始。不可省略。

[ ] :局部变量捕获列表。Lambda表达式不能访问外部函数体的任何局部变量
[a] :在函数体内部使用值传递的方式访问a变量
[&b]: 在函数体内部使用引⽤传递的方式访问b变量
[=] :函数外的所有局部变量都通过值传递的方式使用, 函数体内使⽤的是副本
[&] :以引用的方式使用Lambda表达式外部的所有变量
[=, &foo] :foo使用引用方式, 其余是值传递的方式
[&, foo] :foo使用值传递方式,其余引用传递
[this] :在函数内部可以使用类的成员函数和成员变量,= 和 & 形式也都会默认引入

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QPushButton* button = new QPushButton(this);
    button->setText("按钮");
    connect(button,&QPushButton::clicked,this,[button,this](){
        button->move(300,300);//移动鼠标的位置
        this->move(10,10);
    });
}

说明:
• 由于使用引用方式捕获对象会有局部变量释放了而Lambda函数还没有被调用的情况。如果执行Lambda函数,那么引用传递方式捕获进来的局部变量的值不可预知。所以绝大多数场合使用的形式为: [=] () { }
• 早期版本的 Qt,若要使用Lambda表达式,要在 “.pro” 文件中添加: CONFIG += C++11
因为 Lambda表达式 是 C++11 标准提出的。Qt5 以上的版本无需手动添加,在新建项目时会自动添加。
在这里插入图片描述

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐