【QT入门1】

news2024/11/25 2:05:44

目录

1.创建工程时基类的选择

2.第一个QT程序

3.创建一个按钮

4.对象树简单理解

5.信号和槽

5.1自定义信号槽

5.2信号连接信号

5.3信号函数和槽函数的注意事项

5.4配合lambda表达式

1.创建工程时基类的选择

在创建工程时会被要求选择一个基类:

这里有三个基类可供选择,分别是"QMainWindow"、"QWidget"、"QDialog"。它们的关系是:

 这里简要介绍一下这三个基类分别是什么:

  • QWidget:一个空白窗口
  • QMainWindow:继承自QWidget类,它是一个包含菜单栏、工具栏、状态栏等等的窗口
  • QDialog:继承自QWidget类,它是一个对话框窗口

2.第一个QT程序

选择"QWidget"作为基类,并且不勾选"Generate form"选项,得到的工程文件如下:

.pro文件是以qmake构建的工程文件,它描述了当前工程的一些信息。该文件内的内容和解释如下图:

这些都是默认生成的。如果要使用网络通信模块,那么还应该加载"network"模块。

main.cpp是该项目的入口,它的内容和解释如下图:

如果开发过服务器,那么这几句代码很容易理解。"a.exec()"就是一个事件监听循环

widget.h是项目自动生成的派生类,它继承自"QWidget"。它的内容和解释如下图:

widget.cpp是上述类的成员函数实现,它的内容如下图:

运行该程序,得到如下结果:

可见,输出结果就是一个空白窗口,并且不是一闪而过,这是因为main.cpp中做了事件监听循环处理。

3.创建一个按钮

在QT当中,按钮也封装成了单独的类,它的相关说明如下图:

 可以看到,QPushButton类继承自QAbstractButton类,而QAbstractButton又继承自QWidget类,如下图所示:

这说明按钮可以在单独的窗口当中打开,代码如下:

#include "widget.h"

#include <QApplication>
#include <QPushButton>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    QPushButton *btn = new QPushButton;// 创建一个按钮对象
    btn->show();// 单独打开一个窗口显示
    w.show();
    return a.exec();
}

最终的运行结果是这样的:

那么想要让按钮依附于已经存在的窗口,只需要指定"父亲"即可,代码如下:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    QPushButton *btn = new QPushButton("我的按钮",&w);// 构造函数当中可以指定按钮的文字和"父亲"
    //btn->setParent(&w);// 另一种方法指定"父亲"
    //btn->setText("我的按钮");// 另一种方法指定按钮的文字
    w.show();
    return a.exec();
}

最终效果如下:

 还可以更改按钮的大小和位置,代码如下:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    QPushButton *btn = new QPushButton("我的按钮",&w);
    btn->resize(200,300);// 设置按钮的大小
    btn->move(200,300);// 移动到(200,300)位置
    w.show();
    return a.exec();
}

运行效果如下:

 关于QPushButton类还有很多用法,篇幅有限就不一一列举了,可以在平时的练习和项目当中发现更多有意思的东西。

4.对象树简单理解

观察上面的代码可以发现,它们不是一份合格的C++代码,因为它们看起来有内存泄漏:

那么给它加上delete会发生意想不到的事:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget *wid = new Widget;
    QPushButton *btn = new QPushButton("我的按钮",wid);
    wid->show();
    delete wid;
    delete btn;
    return 0;
}

这份代码看似完美,实则会发生崩溃:

这是因为对象树的存在。 

在上面的代码当中,出现了指定"父亲"的情况,这种操作实际上就是加入对象树。

那么对象树和内存泄漏有什么关系?

在QT当中的对象树有一个特性,对象树当中的任意一个对象要析构的时候,清理自身资源之前要先清理所有的"儿子"

这就是为什么大多数QT类当中需要传入一个parent指针的原因,就是要让该类对象加入对象树,然后根据对象树当中的任意对象析构时的特性,可以做到避免内存泄漏。

举一个例子来说明以上结论是正确的,这里自定一个MyPushButton类:

#include "widget.h"

#include <QApplication>
#include <QPushButton>
#include <QString>
#include <QDebug>
class MyPushButton : QPushButton// 继承自QPushButton类
{
public:
    MyPushButton(const QString &text, QWidget *parent = nullptr)
        :QPushButton(text,parent)
    {}

    ~MyPushButton()
    {
        qDebug() << "~MyPushButton()";
    }
private:
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    MyPushButton *btn = new MyPushButton("自定义按钮",&w);
    w.show();
    return a.exec();
}
Widget::~Widget()
{
    delete ui;
    qDebug() << "Widget::~Widget()";
}

其运行结果如下:

那么在这个例子当中就有了一颗简单的对象树:

5.信号和槽

"信号槽"实际上是两个东西,一个是信号,一个是槽。那么我自己更愿意称它们为"信号和信号处理"

既然谈到信号和信号处理,那么就必定涉及四个部分:

  • 信号的发送者
  • 发送了什么信号
  • 信号的接收者
  • 收到信号后要做什么动作

其中"收到信号后要做什么动作"在QT当中称为槽。

流程如下图所示:

 有一函数一一对应上面的过程:

connect函数是QObject当中的成员函数,而QObject是最顶层的基类,意思是说例如QWidget或者QMainWindow这样的类它们的祖宗类都是QObject,所以它们都可以直接使用类内的connect函数。

 下面以代码演示一下"按钮点击之后窗口关闭"的效果:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    QPushButton *btn = new QPushButton("我的按钮",&w);
    Widget::connect(btn,&QPushButton::clicked,&w,&Widget::close);// 注册信号和槽
    w.show();
    return a.exec();
}

connect方法实际上类似于Linux的系统调用signal,它们都是在注册一堆信号和信号处理方法,信号产生并且接收到时就会执行预设的动作。那么在QT当中有一个取消注册信号槽的方法:disconnect

5.1自定义信号槽

在QT当中允许自定义信号和槽。

有一需求:设计出员工类和老板类,员工负责发送"月底到了"的信号,老板负责响应员工发送的信号,老板的动作就是发工资。 

下面给出实现的代码:

// boss.hpp
#ifndef BOSS_H
#define BOSS_H

#include <QObject>
#include <QDebug>
class Boss : public QObject
{
    Q_OBJECT
public:
    explicit Boss(QObject *parent = nullptr)
        :QObject(parent)
    {}

public slots:
    void GetPaid()//
    {
        qDebug() << "老板发工资了!";
    }

};

#endif // BOSS_H
// staff.hpp
#ifndef STAFF_H
#define STAFF_H

#include <QObject>

class Staff : public QObject// 员工类
{
    Q_OBJECT
public:
    explicit Staff(QObject *parent = nullptr)
        :QObject(parent)
    {}

signals:
    void IsEndOfMonth();// 到月底了
};

#endif // STAFF_H
// main.cpp
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Staff *st = new Staff;
    Boss *bs = new Boss;
    QObject::connect(st,&Staff::IsEndOfMonth,bs,&Boss::GetPaid);// 注册一个信号和槽
    emit st->IsEndOfMonth();// 员工发送"月底到了"的信号
    return a.exec();
}

程序运行后的结果为:

上面代码设计到4个新鲜的东西,这里一一介绍一下:

  • Q_OBJECT:这是有关于信号和槽的关键字,必加
  • signals:这是声明信号的关键字,在定义信号函数时,只声明不实现
  • public slots:声明槽函数的关键字,在QT的较新版本当中,槽函数也可以直接写在public下
  • emit:发送信号的关键字

5.2信号连接信号

connect方法不仅可以指定信号连接槽,还可以指定信号连接信号

接下来演示一个实例代码:点击按钮触发一个信号,该信号连接到Staff类的"月底到了"的信号,然后Staff类的信号连接到Boss类的"发工资"信号:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    Staff *st = new Staff(&w);
    Boss *bs = new Boss(&w);
    QPushButton *btn = new QPushButton("我的按钮",&w);
    QObject::connect(btn,&QPushButton::clicked,st,&Staff::IsEndOfMonth);// 信号连接信号
    QObject::connect(st,&Staff::IsEndOfMonth,bs,&Boss::GetPaid);
    w.show();
    return a.exec();
}

5.3信号函数和槽函数的注意事项

 上面的案例当中信号函数和槽函数都是没有参数的,事实上在QT当中,信号函数和槽函数允许重载。需要注意的是,槽函数的参数必须与信号函数一一对应,但是可以少于信号函数的参数。例如下面这样:

// 信号函数
void Signal1();
void Signal2(int x);
void Signal3(int x,int y);

// 槽函数
void Slot1();
void Slot2(int x);

在如上代码代码当红,Signal1触发会调用Slot1槽函数,Signal2、Signal3触发会调用Slot2槽函数。

还需要注意,在上面的所有与信号和槽相关的代码时都是"不准确"的,因为在实际的项目当中一个类的信号或槽都有多个重载,那么在使用connect()函数时,如何指定信号和槽?答案是使用函数指针。例如下面的用法:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    Staff *st = new Staff(&w);
    Boss *bs = new Boss(&w);
    QPushButton *btn = new QPushButton("我的按钮",&w);
    /*指向无参的信号和槽*/
//    void (Staff:: *staffSignal)() = &Staff::IsEndOfMonth;
//    void (Boss:: *bossSlot)() = &Boss::GetPaid;

    /*指向int类型参数的信号和槽*/
    void (Staff:: *staffSignal)(int) = &Staff::IsEndOfMonth;
    void (Boss:: *bossSlot)(int) = &Boss::GetPaid;

    QObject::connect(btn,&QPushButton::clicked,st,staffSignal);// 信号连接信号
    QObject::connect(st,staffSignal,bs,bossSlot);
    w.show();
    return a.exec();
}

那么输出结果或许猜想到了,是不正确的,因为我们并没有做传参的处理:

5.4配合lambda表达式

为了解决上面的问题,可以使用lambda表达式来解决它。

注意,槽函数的本质只是一个函数,而lambda表达式本质是一个匿名函数对象,所以可以直接搭配使用。

最终代码如下:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    Staff *st = new Staff(&w);
    Boss *bs = new Boss(&w);
    QPushButton *btn = new QPushButton("我的按钮",&w);
    /*指向int类型参数的信号和槽*/
    void (Staff:: *staffSignal)(int) = &Staff::IsEndOfMonth;
    void (Boss:: *bossSlot)(int) = &Boss::GetPaid;
    QObject::connect(st,staffSignal,bs,bossSlot);
    auto func = [=]()
    {
        emit st->IsEndOfMonth(100);
    };
    QObject::connect(btn,&QPushButton::clicked,func);// 使用lambda表达式时可以不指定接收信号对象

    w.show();
    return a.exec();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1071235.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Java】语法特性篇

语法特性篇 Java对象的比较 1. 对象比较的问题 Java中引用类型的变量不能直接按照 > 或者 < 方式进行比较。那为什么可以比较&#xff1f; 因为&#xff1a;对于用户实现自定义类型&#xff0c;都默认继承自Object类&#xff0c;而Object类中提供了equal方法&#xf…

Kafka实战案例

kafka系统的生成&#xff0c;自顶向下 1. kafaka发送消息 1.1 是最初始外部调用kafaka的地方1.6 是最初调用kafaka的函数。中间是对kafaka的构建 1.1 向Kafka发送一条发布视频的message 在videoHandler的发布视频逻辑中&#xff0c;向Kafka发送一条发布视频的mq&#xff0c…

Ubuntu 22.04 安装系统 手动分区 针对只有一块硬盘 lvm 单独分出/home

自动安装的信息 参考自动安装时产生的分区信息 rootyeqiang-MS-7B23:~# fdisk /dev/sdb -l Disk /dev/sdb&#xff1a;894.25 GiB&#xff0c;960197124096 字节&#xff0c;1875385008 个扇区 Disk model: INTEL SSDSC2KB96 单元&#xff1a;扇区 / 1 * 512 512 字节 扇区大…

基于Springboot实现论坛管理系统项目演示【项目源码+论文说明】分享

基于Springboot实现论坛管理系统演示 摘要 在社会快速发展的影响下&#xff0c;论坛管理系统继续发展&#xff0c;使论坛管理系统的管理和运营比过去十年更加信息化。依照这一现实为基础&#xff0c;设计一个快捷而又方便的网上论坛管理系统是一项十分重要并且有价值的事情。对…

排序(order by)

MySQL从小白到总裁完整教程目录:https://blog.csdn.net/weixin_67859959/article/details/129334507?spm1001.2014.3001.5502 语法格式: select */列名 from 表名 order by 列名1 asc/desc, 列名2 asc/desc; 说明&#xff1a; 排序的目的&#xff1a;改变查询结果的返回顺序…

学习笔记(css穿透、vue-cookie、拦截器、vuex、导航守卫、token/Cookie、正则校验)

目录 一、记录 1、CSS穿透 2、输入框是否提示输入 3、插槽 #slot 4、v-deep深入改掉属性值 二、vue-cookie 1、官方文档 2、使用 三、拦截器 1、请求拦截器 2、响应拦截器 四、vuex对信息存取改 五、路由导航守卫 1、登录思路 2、设置白名单 六、Token与Cookie…

vue3 集成 tailwindcss

tailwindcss 介绍 Tailwind CSS 是一个流行的前端框架&#xff0c;用于构建现代、响应式的网页和 Web 应用程序。它的设计理念是提供一组可复用的简单、低级别的 CSS 类&#xff0c;这些类可以直接应用到 HTML 元素上&#xff0c;从而加速开发过程并提高样式一致性。 主要特点…

【数据结构与算法】二叉树的实现以及二叉排序数的实现

目录 通过数组实现二叉树 通过链表实现二叉树 排序二叉树的实现 通过数组实现二叉树 该实现方式只能用于完全二叉树&#xff0c;因为如果是普通二叉数的话&#xff0c;数组中会出现空隙&#xff0c;会导致空间的利用率会降低。 实现思路&#xff1a; 因为假设一个父节点的…

原码反码补码移码的介绍和计算

1.原码 原码的定义&#xff1a;十进制数据的二进制表示形式就是原码。 &#xff08;1&#xff09;原码的最左边那位是符号位&#xff0c;其他位为数据位&#xff0c;符号位是0则为正数&#xff0c;符号位是1则为负数。 &#xff08;2&#xff09;一个byte有8bit&#xff0c;最…

Node-RED系列教程-25node-red获取天气

安装节点:node-red-contrib-weather 节点图标如下: 使用说明:node-red-contrib-weather (node) - Node-RED 流程图中填写经度和纬度即可。 演示: json内容: {

jmeter 请求发送加密参数

最近在做http加密接口&#xff0c;请求头的uid参数及body的请求json参数都经过加密再发送请求&#xff0c;加密方式为&#xff1a;ase256。所以&#xff0c;jmeter发送请求前也需要对uid及json参数进行加密。我这里是让开发写了个加密、解密的jar&#xff0c;jmeter直接调用这个…

CRM系统如何自动分配线索

分配线索是销售部门很重要的一项工作&#xff0c;大量的线索中潜藏着许多企业未来的忠实客户。如果将大把的线索通过手工的方式分配给多个销售人员是一件棘手的事&#xff0c;就要借助CRM系统自动分配线索。 你的企业是否也面临这些难题&#xff1a; 1.渠道多线索多&#xff…

点击、拖拉拽开发可视化大屏,网友直呼不可思议

可视化大屏既足够炫酷&#xff0c;又能快速整合多业务系统数据&#xff0c;可视化分析数据&#xff0c;是一种可运用于博览中心、会议中心、监控中心、企业大屏看板等场景的常用数据可视化分析形式。但可视化大屏虽然好用&#xff0c;在开发制作上却难倒了不少人&#xff0c;直…

汇编实现点灯实验

.text .global _start _start: 设置GPIOF寄存器的时钟使能LDR R0,0X50000A28LDR R1,[R0]ORR R1,R1,#(0x1<<5)STR R1,[R0]设置GPIOE寄存器的时钟使能LDR R0,0X50000A28LDR R1,[R0] 从r0为起始地址的4字节数据取出放在R1ORR R1,R1,#(0x1<<4) 第4位设置为1STR R1,[…

vue3 + typescript + vite + naive ui + tailwindcss + jsx 仿苹果桌面系统

基于 vue3.x typescript vite naive ui tailwindcss jsx vue-router pinia&#xff0c;项目使用 tsx 作为模版输出&#xff0c;全程没有使用vue提供的SFC&#xff0c; 仿macos桌面前端项目&#xff0c;开源免费模版&#xff0c;希望减少工作量和学习新技术&#xff0c;希…

javascript制作简单的富文本,基本功能都实现,除了上传图片只能用URL

//所有的图标用的字符&#xff0c;以后可以换成网上的css-icon图标库的图标&#xff0c;再设置一下css样式即可简单的使用 //这里所有的标签元素都是直接获取&#xff0c;没有使用委托&#xff0c;如果使用委托性能会更好&#xff0c;这里只做了简单的清理&#xff0c;让内存回…

操作系统对内存的管理:分配与回收,虚拟内存,内存容量的扩充,内存保护,补充(链接方式、装入方式)

内存&#xff1a;即内存条&#xff0c;也称主存储器&#xff08;简称主存&#xff09;&#xff0c;用于存放数据。 为了缓和CPU和外存&#xff08;磁盘&#xff09;的速度矛盾&#xff0c;外存的程序先放入内存才能被CPU处理。 内存地址从0开始&#xff0c;每个内存地址对应一…

以太网基础学习(三)——UDP协议

一、UDP协议概述 UDP&#xff08;User Datagram Protocol&#xff0c;用户数据报协议&#xff09;是一种无连接协议&#xff0c;它不像TCP协议那样需要在发送和接收数据前进行握手和释放&#xff0c;而是直接把数据发送出去&#xff0c;也不会对数据进行可靠传输和流量…

ARM_汇编流水灯

ARM_汇编流水灯 .text .global _start _start: 设置GPIOE寄存器的时钟使能ldr r0,0x50000A28ldr r1,[r0] 从r0为起始地址的4字节数据取出存入r1orr r1,r1,#(0x01<<4) 第4位设置为1 表示开启时钟使能orr r1,r1,#(0x01<<5) 第5位设置为1 表示开启时钟使能str r1…

求推荐好用的可视化大屏软件?强推奥威BI

在博览中心、会议中心、监控中心等场合下&#xff0c;经常看到很多炫酷的企业可视化大屏&#xff0c;将复杂的企业数据可视化展现&#xff0c;高大上、实用性一个不缺。那&#xff0c;可视化大屏做得好的软件有哪些&#xff1f;首推奥威BI软件。 奥威BI软件&#xff1a;零编程…