当前位置: 首页 > article >正文

Qt第二课----信号和槽

作者前言

🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂
​🎂 作者介绍: 🎂🎂
🎂 🎉🎉🎉🎉🎉🎉🎉 🎂
🎂作者id:老秦包你会, 🎂
简单介绍:🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂
喜欢学习C语言、C++和python等编程语言,是一位爱分享的博主,有兴趣的小可爱可以来互讨 🎂🎂🎂🎂🎂🎂🎂🎂
🎂个人主页::小小页面🎂
🎂gitee页面:秦大大🎂
🎂🎂🎂🎂🎂🎂🎂🎂
🎂 一个爱分享的小博主 欢迎小可爱们前来借鉴🎂


Qt

  • **作者前言**
  • 打印hello world
  • QT的编码格式
  • 使用编辑框进行输出hello wrold
  • 点击按钮
  • 信号和槽
    • 信号
    • 使用
    • 自定义信号
    • 自定义槽
    • 带参数的信号和槽
  • 窗口坐标体系
  • Q_OBJECT的介绍
  • 信号和槽的连接方式
  • 信号和槽 的断开
  • 总结

打印hello world

第一种方法,
在ui文件里面进行编辑
在这里插入图片描述
输入内容,然后运行文件,可以打印出来

第二种方法可以写c++代码进行运行出来
这里需要包含QLabel头文件, 这个头文件是之前Qt用的头文件风格
就好比19989年的c++98规定统一使用#include代替#include<stdio.h>头文件
还需要注意的是: 在Qt中,有自己的一套轮子,
QString、 QVector、QList 这些是Qt代码中常见的类型,一一对应的就是C++的string 、vector、list等类型
在这里插入图片描述

 QLabel *labe = new QLabel(this);//推荐在堆上创建,给labe指定一个父节点
 labe->setText("Hello world");//

图中代码,调用了QLabel的方法,使其显示出来,setText的参数是一个Qstring
如图:
在这里插入图片描述
这个方法默认显示到左上角,
观察代码,可以发现没有释放空间,会造成内存泄漏,这是潜意识,
但是是不会内存泄漏的,因为是把这个对象挂到了对象树上了
在这里插入图片描述
使用对象树,把内容组织起来,在合适的时候统一释放(窗口关闭时),但是如果挂在树上的对象提前销毁了,就会导致对应的的控件在界面上不存在了
如图:
在这里插入图片描述
所以说,labe对象会随对象树释放
我们也可以写一个类来进行观察,继承这个QLabel这个内置类,
在这里插入图片描述
.h

#include<QLabel>

class mylabel : public QLabel
{
public:
    mylabel(QWidget* parent);
    ~mylabel();

};

.cpp

#include<QDebug>
mylabel::mylabel(QWidget* parent)
    :QLabel(parent)
{

}

mylabel::~mylabel()
{
   qDebug()<< "释放了";//字符的编码格式和这个文件的编码格式一样
   //终端的编码格式不一样
   //
}

在构造函数中,必须指定父类,这样才能挂在对象树上,否则的话,只能自己手动的进行delete了

回归到原来问题上去,

QT的编码格式

在qt中,我们使用std::cout打印输出就会出现编码格式错误,
例如:

在这里插入图片描述

所以qt有自己的处理工具,或是使用Qstring类型
我们需要包含头文件<QDebug>,使用qDebug()来打印输出,

在这里插入图片描述qDebug()是一个宏,里面封装了一个QDebug对象,所以使用qDebug()相当于std::cout
需要注意是windos不要使用cout, 但是Linux可以,因为Linux的编码格式是utf-8,cpp的文件的编码格式也是utf-8

使用编辑框进行输出hello wrold

上面在程序面板中简单 输出了hello wrold, 这次我们来试试编辑框 QLineEdit,在设计中也可以通过ui文件进行设计,

bfd897d17.png)

在这里插入图片描述
接下来我们使用c++的纯代码来进行实现
还是一样的道理,使用啥控件就导入对应控件的名字(头文件), 导入#include<QLineEdit>
如图:

在这里插入图片描述
代码的写法还是一样的, 控件对象初始化一定要指明父类(父节点),

点击按钮

除了上面的方法,我们也可以通过点击按钮在程序面板中输出
这里需要我们导入头文件#include<QPushButton>

QPushButton * qbutton = new QPushButton(this);
qbutton->setText("hell world");

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

这个按钮点击是没有效果的,需要建立一个点击操作,这就涉及到信号和槽了

信号和槽

在Qt中,⽤户和控件的每次交互过程称为⼀个事件。⽐如"⽤⼾点击按钮"是⼀个事件,"⽤户关闭窗⼝"也是⼀个事件。每个事件都会发出⼀个信号,例如⽤⼾点击按钮会发出"按钮被点击"的信号,⽤⼾关闭窗⼝会发出"窗口被关闭"的信号,而在Qt中,对信号做出的响应动作就称之为槽

注意:信号的本质就是事件

槽的本质
槽(Slot)就是对信号响应的函数。槽就是⼀个函数,与⼀般的C++函数是⼀样的,可以定义在类的任何位置(public、protected或private),可以具有任何参数,可以被重载,也可以被直接调⽤(但是不能有默认参数)。槽函数与⼀般的函数不同的是:槽函数可以与⼀个信号关联,当信号被发射时,关联的槽函数被⾃动执⾏

说明
(1)信号和槽机制底层是通过函数间的相互调⽤实现的。每个信号都可以⽤函数来表⽰,称为信号函数;每个槽也可以⽤函数表⽰,称为槽函数。例如:"按钮被按下"这个信号可以⽤clicked()函数表⽰,"窗⼝关闭"这个槽可以⽤close()函数表⽰,假如使⽤信号和槽机制实现:"点击按钮会关闭窗⼝"的功能,其实就是clicked()函数调⽤close()函数的效果。
(2)信号函数和槽函数通常位于某个类中,和普通的成员函数相⽐,它们的特别之处在于:
• 信号函数⽤ signals关键字修饰,槽函数⽤public slots、protected slots或者private slots修饰。signals和slots是Qt在C++的基础上扩展的关键字,专⻔⽤来指明信号函数和槽函数;
• 信号函数只需要声明,不需要定义(实现),⽽槽函数需要定义(实现)

信号

在Linux中,进程的通讯方式是信号, signal,
谁发出来的,啥类型的信号, 怎么处理,
而在Qt中也有自己的信号,
涉及到三个要素
简单的理解的就是, 信号源、 信号类型, 处理方式(槽, connect的使用)

使用

在Qt中Qobject类中提供一个静态函数connet(),该函数专⻔⽤来关联指定的信号函数和槽函数
connect() 函数原型:

bool QObject::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 )

type:⽤于指定关联⽅式,默认的关联⽅式为Qt::AutoConnection,通常不需要⼿动设定。
在这里插入图片描述
这写了两个,从图中可以看出,qbutton是new出来的, pushButton是ui文件通过qmake生成的,
这个变量名我们是可以控制的,如果要更改的话,
如图:

在这里插入图片描述
qmake会根据ui文件的objectName对应的值,生成的变量名(唯一),我们也可以更改这个值,
我们这个值修改为puBu
如图:
在这里插入图片描述

需要注意的是:&QPushButton::clicked 是函数指针,
代码:

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
    ,qbutton(new QPushButton(this))
{
    ui->setupUi(this);
    /*QLineEdit* qlineedit = new QLineEdit(this);
    qlineedit->setText("hello world");*/ // 单编辑框
    qbutton->setText("hell world");
    connect(qbutton,&QPushButton::clicked,this, &Widget::funtion);
    connect(ui->puBu,&QPushButton::clicked,this, &Widget::funtion);
}
void Widget:: funtion()
{

    qDebug() << "点击成功";
    if(ui->puBu->text()==QString("点击"))
    {
        ui->puBu->setText("成功点击");
    }
    else{
        ui->puBu->setText("点击");
    }

}

.h

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
   void funtion();
private:
    Ui::Widget *ui;
    QPushButton * qbutton;
};

看到这里, 很多人会迷惑,为啥在qt中,会使用不同的类型的指针进行传参,在c++中不能这样做的,
这是因为在以前的Qt版本中, connect传参需要传入宏的, 分别是SIGNAL和SLOT,
写法如下:

connect(btn, SIGNAL(&QPushButton::clicked), this, SLOT(&Widget::close));

这两个宏的作用就是把函数指针类型转换成char*
而在QT5之后,就增加了泛型编程,也就是函数模板,
在这里插入图片描述

自定义信号

信号本质就是事件, 也就是一个“函数”,但是信号是一个特殊的函数, 程序员只要声明这个函数就行,至于函数的定义,是qt在编译的过程自动生成的(生成过程,程序员无法干预)

需要注意的是: 信号函数的返回值必须是void, 也没有形参都可以,重载函数也可以
在qt中有关键字signals:这个是c++没有的
至于怎么定义的函数, 是qmake在调用一些工具的时候,扫描到signal的时候,就会根据函数声明写出一个函数定义,
下面我们先声明一个信号函数,然后使用connect把信号和槽建立连接
在这里插入图片描述
然后我们运行起来, 就会发现。我们该怎么触发信号, 前面我们使用内置的点击可以触发信号,但是现在我们自定义信号, 没有这样的功能 这里Qt就引入了emit关键字, 使用这个关键字触发我们声明的信号函数

emit this->myclicked();

如图:
在这里插入图片描述
在Qt5中不写这个emit也可以

自定义槽

顾名思义,简单的理解就是一个函数的声明加定义,所以说当我们要定义一个槽函数需要按照Qt的写法进行,
槽函数写法1
在Qt中有c++中没有的关键字,为了区分普通函数和槽函数,qt增加了如下关键字

public slots:
protected slots;
private slots

槽函数定义和声明在其中,这样可以一目了样,就算不写在其中,也是可以的,这个要看程序员怎么写
槽函数写法2
除了上面的纯代码方式,还可以在ui文件进行
如图:

点击进行就会看到如下:
在这里插入图片描述
然后选择好信号就会生成对应的槽函数
在这里插入图片描述
然后我们只需要函数里面写我们的内容就行,
当我们运行的时候就会发现, qt文件中没有使用connect,而生成的ui_widget.h的头文件也没有,
这是因为,在QT中,信号和槽的链接不局限于使用connect, 还可以通过函数名的方式来链接
而生成的函数名字也是有讲究的,如图:
在这里插入图片描述
我们知道QPushButton有clicked这个函数,如果我们当名字更改了就会报错,
当我们更改后,
在这里插入图片描述

就会发现,输出的那里有一个函数名字叫做connectSlotsByName,这个对应着头文件ui_widget.h
在这里插入图片描述
当调用这个函数的时候,就会调用对应的函数名链接的规则,进行信号和槽的链接, 如果这个函数(connectSlotsByName)没有调用, 哪怕在Qt文件中没有更改函数名,也会报错,

总结:
如果我们时使用图形化工具创建控件的话,就使用函数名链接规则进行信号和槽的连接,
如果是纯代码的方式来创建控件的话,要使用connect来,因为我们没有调用connectSlotsByName这个函数

带参数的信号和槽

前面我们自定义或者内置的类的成员函数,都是没有参数的,如果要想写一个带有参数的信号函数,对应的槽函数也要带参数, 并且参数类型要一致,个数不一致也可以,信号函数参数个数 >= 槽函数参数个数
下面我写了一段代码

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

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    btn = new QPushButton("点击",this);
    connect(btn, &QPushButton::clicked, this,&Widget::HandBtn);
    connect(this, &Widget::myclicked, this, &Widget::handmyclicked);

}

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

void Widget::handmyclicked(const QString& ch)
{
    this->setWindowTitle(ch);
}

void Widget::HandBtn()
{
    btn->move(200,300);
    emit this->myclicked("无敌大厂");
}

大致思路:
在这里插入图片描述

窗口坐标体系

前面我们使用代码写出来的,都是默认在程序面板的左上角显示的,而在qt中的
坐标体系:以左上⻆为原点(0,0),X向右增加,Y向下增加。
在这里插入图片描述
在qt中,给某个控件设置位置的时候,相对于的坐标原点,就是父窗口的左上角
在这里插入图片描述
前面我们简单的理解了对象树, QPushButton作为Qwidget的子节点,对应的坐标原点就是QWidget框的左上角
当我们运行起来就会默认在qwidget的左上角,
在这里插入图片描述
我们可以使用move函数

void move(int x, int y);

就会在(x,y)处开始显示,这个是相对于Qwidget这个父框的
如图:
在这里插入图片描述

我们也可以给Qwidget这个窗口进行设置,

Q_OBJECT的介绍

前面我们知道,如果一个类中要定义和使用信号和槽(或者定义和声明信号和槽),就需要类开始的地方写入宏 Q_OBJECT,否则就会报错

信号和槽的连接方式

在大部分的GUI网页开发中,主要形式不是使用connect这样的形式,(一对一形式)

funtion handle()
{
.....
}
button.onclick = handle;

只要点击就会触发函数, 这种方式有很多, 但是在Qt中, 搞出connect这样的方式,是为了解决信号和槽的耦合, 也就是实现处理 用户的操作控件和处理用户的操作的逻辑,(一个控件需要产生不同的效果),也叫解耦合
还有就是实现一个信号连接多个槽函数,或者一个槽函数连接多个信号,
总结: 在qt中信号和槽的连接有一对一. 一对多以及多对多的方式

信号和槽 的断开

关键字: disconnect
代码如下:


Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    connect(ui->pushButton1, &QPushButton::clicked, this, &Widget::Myslot1);
     connect(ui->pushButton2, &QPushButton::clicked, this, &Widget::Myslot2);
}

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

void Widget::Myslot1()
{
    qDebug()<< "Myslot1";
    this->setWindowTitle("东东");
}
void Widget::Myslot2()
{
    qDebug()<< "Myslot2";
    //断开
    disconnect(ui->pushButton1, &QPushButton::clicked, this, &Widget::Myslot1);
    //重新连接
    connect(ui->pushButton1, &QPushButton::clicked, this, &Widget::Myslot3);
}
void Widget::Myslot3()
{
    qDebug()<< "Myslot3";
    this->setWindowTitle("喜喜");
}

如图:
在这里插入图片描述
disconnect一般不怎么使用,主动断开连接,一般情况下是连接其他的槽函数,
除了上面这样声明和定义槽函数, 还可以使用lambda表达式
在这里插入图片描述
但是需要注意到是Qt中的lambda表达式支持是在c++11的基础上的,Qt5之后的版本,如果是Qt5之前的版本,就需要在.pro文件中更改一下,如图:
在这里插入图片描述

总结

Linux中的信号和Qt中的信号有很多相似点,

  1. 信号源
    2)信号类型
    3)信号的处理方式

信号和槽的连接和使用
connect的使用和disconnect的使用

自定义信号和槽函数
Q_OBJECT宏
lambda表达式


http://www.kler.cn/a/282573.html

相关文章:

  • 从零开始学习 sg200x 多核开发之 uboot 网络功能使能
  • 机器学习 决策树
  • 浅谈React的虚拟DOM
  • WPF中如何使用区域导航
  • AdaBoost 二分类问题
  • Android Osmdroid + 天地图 (二)
  • python把dbc转换成excel
  • 在Ubuntu 18.04上安装Linux、Nginx、MySQL、PHP(LEMP堆栈)的方法
  • 使用LinkedHashMap实现固定大小的LRU缓存
  • 【分享】7-Zip解压缩软件的4个功能模块
  • HarmonyOS NEXT未成年人模式无缝联动所有应用,过滤非适龄内容
  • centos中yum安装时提示Cannot find a valid baseurl for repo: base/7/x86_64 出现仓库源问题
  • 大模型知识检索RAG业务实践实践(初级篇)
  • 基于SpringBoot+Vue+MySQL的图书管理系统
  • Spring框架:从依赖注入到微服务
  • HTML5有格调的个人介绍网站源码
  • 产品经理如何提升系统思考能力
  • 商业律师事务所借助 DocuSign 解决方案加快了 QES 和身份识别流程 | 电子签约律师事务解决方案
  • Kotlin内联函数
  • BeautifulSoup:Python网页解析库详解
  • 数据结构(邓俊辉)学习笔记】串 14——BM_GS算法:构造gs表
  • Linux文件和目录常用命令
  • 探索OpenCV:图像处理基础与实践
  • 基于STM32开发的智能灌溉系统
  • day31-测试之性能测试工具JMeter的功能概要、元件作用域和执行顺序
  • python基础(13魔法方法介绍)