Qt初识简单使用Qt

使用C++代码实现hello world

之前介绍过用图形化界面的方式创建hello world,这里我们使用C++代码的方式再来实现一次hello world。

如上,首先要先包含一个头文件。

在QT这里,每一个类都有一个对应的同名头文件。比如这里我就包含了 <QLabel>

不过,在调用 setText函数的时候

发现这里的参数是一个QString类型,这是什么呢?
这其实是一个历史问题,因为QT是在1991年推出的,那时的C++对字符串类型的定义比较混乱,C++自身的string类型还不是很好用,于是QT就自己造了一个轮子,也就是QString类型,后来C++标准定义的string类型发布了,但是QT也并没有把原来设计的类型删掉,而是保留了下来。

 

但是我们在这里调用的时候,直接传入了一个 "hello world",这是因为C风格的字符串会隐式转化为QString。

关于内存泄漏的问题 

 

还是这个代码,我们发现,我们new了一个QLabel对象后,并没有调用delete来进行释放,这是否会造成内存泄漏呢?
这里是不会造成内存泄漏的,因为在QT中有一个对象树的概念,QLabel继承自它的父类Widget,在我们构造QLabel对象的时候,就把this指针传入了过去,那么也就是将 QLabel对象挂到这个对象树中了。

使用对象树的目的,就是为了能够在合适的时机(比如窗口的关闭/销毁),把这些对象统一进行释放。统一释放这种事情还是挺不错的,如果某一个窗口内有对象提前释放了,这不就是bug了吗

我们这里是通过new的方式创建 QLabel对象的,也就是在堆上创建的,我们也可以在栈上创建,在栈上创建编译器不会报错,但是不建议,因为这样就会导致对象提前被释放了。

比如我们将刚刚实现hello world改为栈上生成:

发现这一次并没有hello world,说明对象被提前释放掉了。

我们知道内存泄漏是一件很可怕的事情,因为不容易被察觉,并且暴露问题的周期不确定,可能是几分钟,几小时,甚至几天。那么,我们将对象挂到对象树中,我们没有手动调用delete,这个对象真的被释放了吗?

为了进行一个验证,我们可以手动创建一个类,让它继承 QLabel,我们手动实现一下析构函数,来进行一个打印的方式进行验证。

先在头文件那里选择创建C++类

 这里我们要选择继承QLabel

点击创建以后,就会帮我们生成对应的.h和.cpp文件

头文件中:

注意,我们要想把对象挂到QT的对象树中,在构造函数中记得传入父类的指针 

Qt中通过继承内置自Qt内置的类,就可以达到对控件进行扩展的效果。 

在头文件中写完声明后,有一个技巧:

 用alt + enter(回车)的方式,自动在对应的.cpp文件中添加函数定义

 在cpp文件中

最后在widget.cpp中调用,记得包含头文件

运行程序

 发现hello world打印出来了,但是因为我们还没关闭窗口,所以MyLabel还没有销毁,当我们关闭窗口后

发现打印了这个,虽然有一些乱码,但是我们能认出这就是我们析构函数打印出来的内容。

关于乱码问题 

有这么一个问题:一个汉字占用多少字节?

如果我们直接回答,那么百分百就是错了,因为现在表示汉字的字符集主要有两种,一种是GBK:也就是Windows下对简体中文用的字符集,另一种是utf-8,在Linux下默认用的就是这种。

然后GBK下一个汉字是2字节,而utf-8下一个汉字一般是3字节。

所有出现乱码问题的原因只有一个:那就是编码方式不匹配。

如果要解决这个乱码问题,我们可以使用qDebug()函数来打印。

比如

 Qt中提供了一个qDebug工具,可以帮我们打印日志,并处理字符编码,还有不需要换行。

qDebug是一个宏,封装了QDebug对象。

并且如果我们使用qDebug,可以很方便的通过编译开关,来进行一键式关闭和打开。

因为我们打印日志主要是给程序员看的,当我们将程序发布上线给用户使用后,当然不应该给用户看到日志信息。

关于为什么不用VS 或者 gdb这样的调试工具来调试,这是因为调试器在很多情况下是有局限性的,比如一个bug出现的概率是1%,这时想要通过调试器来调试就会非常困难和麻烦。

用按钮实现一个hello world

第一种方法:我们同样可以使用图形化界面的方式来实现

 

这是一个按钮,但是没有进行任何处理的话,我们点击这个按钮是没有反应的。

这里就简单说下Qt信号槽机制

这里的本质就是给按钮的点击操作,关联一个处理函数。

当用户点击的时候,就会调用这个处理函数。

这里的设置处理函数的接口就是 connect

这里的connect跟网络套接字那里没有任何关系。

来看一下我这里的调用方式

这里我们发现,我们是用了一个 ui->pushButton。并且显然这个pushButton就是我们的按钮对象

为了做区分,我又用代码的方式创建了一个QLabel
 

运行结果:

可以看到是没问题的,现在的情况就是 这个按钮是我用图形化界面创建的,这个QLabel是我用代码的方式创建的。

接着在设计图形化界面这里:
 我们发现用代码生成的不会显示在这里,并且注意右边,在QPushButton下有一个对象,名字就是pushButton,这个对象其实就是由我们的ui自动帮我们创建的,并且这个对象的名字也是可以由我们进行修改的。我们怎么看到ui的代码呢?

我们先点击在Explorer中显示:
 在build中找到

打开后

 我们发现,这里就会自动帮我们创建一个QPushButton的对象,对象名正是pushButton,它就是ui_Widget的类成员。

所以到这里我们就搞清楚了,为什么通过图形化界面创建的,要通过ui->这种方式使用了。

这下我们也就能理解,在Widget中的这个ui是干啥的了。 

关于connect参数的解释:

 这个handleClick当然也是我们自己实现的:
那么步骤就是:先在Widget.h中进行声明,然后再对应的.cpp中实现:

 

这里我们实现的功能是:如果这个按钮的名字是 hello world hzj,那么就按钮设置为 hello world qt,否则就又设置成 hello world hzj。

这里就是用图形化界面的方式用按钮实现一个hello world,那么用代码的方式如下:
 这里记得也要将这个_myButton作为Widget的成员变量声明一下

对两种实现方式的总结:
 在实际开发中,这两种方式都会用到,都很重要。

使用图形化界面的场景:当界面内容固定,此时就会以图形化界面的方式来构造界面。

使用代码的场景:当程序界面需要经常动态变化时,此时就会用代码的方式构造界面。

给变量/函数/文件/类命名的规范

在C/C++中,一般使用蛇形命名法:
 而在其它的语言中,几乎都使用驼峰命名法,包括Qt这里也是:

项目文件的解析

关于.pro文件 

⼯程新建好之后,在⼯程⽬录列表中有⼀个后缀为 ".pro" 的⽂件, ".pro" ⽂件就是⼯程⽂件 (project) ,它是 qmake ⾃动⽣成的⽤于⽣产 makefile 的配置⽂件。如图所⽰:

 

双击进⼊该⽂件,该⽂件的核⼼内容如下:
QT += core gui // Qt 包含的模块
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets //⼤于 Qt4 版本才包含 widget 模块
TARGET = QtFirst //应⽤程序名⽣成的 .exe 程序名称
TEMPLATE = app //模板类型,应⽤程序模板
SOURCES += main.cpp\ //源⽂件
widget.cpp //源⽂件
HEADERS += widget.h //头⽂件

widget.h ⽂件解析 

在Qt中,如果要使⽤信号与槽(signal 和 slot)的机制 就必须加⼊ Q_OBJECT 宏;
Ui::Widget *ui; 这个指针是⽤前⾯声明的 namespace Ui ⾥的 Widget 类定义的,所以指针 ui 是指向可视化设计的界⾯,后⾯要访问界⾯上的组件,都需要通过这个指针 ui 去访问。

 main.cpp ⽂件解析 

QApplication 为应⽤程序类;QApplication a;(a为应⽤程序对象,有且仅有⼀个。)
⚫ QApplication 管理图形⽤⼾界⾯应⽤程序的控制流和主要设置。
⚫ QApplication 是 Qt 的整个后台管理的命脉。它包含主事件循环,在其中来⾃窗⼝系统和其它
资源的所有事件处理和调度。它也处理应⽤程序的初始化和结束,并且提供对话管理。

⚫ 对于任何⼀个使⽤ Qt 的图形⽤⼾界⾯应⽤程序,都正好存在⼀个 QApplication 对象,⽽不论
这个应⽤程序在同⼀时间内是不是有 0、1、2 或更多个窗⼝。

myWidget w; //实例化窗⼝对象
w.show(); //调⽤show函数显⽰窗⼝
  a.exec() :程序进⼊消息循环,等待对⽤⼾输⼊进⾏响应。这⾥ main()把控制权转交给Qt,Qt 完
成事件处理⼯作,当应⽤程序退出的时候 exec() 的值就会返回。在 exec() 中,Qt 接受并处理⽤⼾
和系统的事件并且把它们传递给适当的窗⼝部件。

Qt Creator中的快捷键 

注释:ctrl + /
运⾏:ctrl + R
编译:ctrl + B
字体缩放:ctrl + ⿏标滑轮
查找:ctrl + F
整⾏移动:ctrl + shift + ⬆/⬇
帮助⽂档:F1
⾃动对⻬:ctrl + i;
同名之间的 .h 和 .cpp 的切换:F4
⽣成函数声明的对应定义: alt + enter

使用帮助文档 

如果我们要查询某一个函数或者类,可以点击一下这个函数,然后按f1

右边就会给我们将文档展示出来

我们还可以点击左边的帮助按钮:

不过实际开发中,还是用到什么,用f1查比较多。 

认识对象模型(对象树) 

在 Qt 中创建很多对象的时候会提供⼀个 Parent 对象指针,下⾯来解释这个 parent 到底是⼲什么的。 

QObject 是以对象树的形式组织起来的。 

当创建⼀个 QObject 对象时,会看到 QObject 的构造函数接收⼀个 QObject 指针作为参数,这
个参数就是 parent,也就是⽗对象指针。
这相当于,在创建 QObject 对象时,可以提供⼀个其⽗对象,我们创建的这个 QObject 对象
会⾃动添加到其⽗对象的 children() 列表。
当⽗对象析构的时候,这个列表中的所有对象也会被析构。(注意,这⾥的⽗对象并不是继承
意义上的⽗类!)
这种机制在 GUI 程序设计中相当有⽤。例如,⼀个按钮有⼀个 QShortcut(快捷键)对象作为其
⼦对象。当删除按钮的时候,这个快捷键理应被删除。这是合理的。

QWidget 是能够在屏幕上显⽰的⼀切组件的⽗类。 

QWidget 继承⾃ QObject ,因此也继承了这种对象树关系。⼀个孩⼦⾃动地成为⽗组件的⼀
个⼦组件。因此,它会显⽰在⽗组件的坐标系统中,被⽗组件的边界剪裁。例如,当⽤⼾关闭
⼀个对话框的时候,应⽤程序将其删除,那么,我们希望属于这个对话框的按钮、图标等应该
⼀起被删除。事实就是如此,因为这些都是对话框的⼦组件。
当然,我们也可以⾃⼰删除⼦对象,它们会⾃动从其⽗对象列表中删除 ⽐如,当我们删除了
⼀个⼯具栏时,其所在的主窗⼝会⾃动将该⼯具栏从其⼦对象列表中删除,并且⾃动调整屏幕
显⽰。

Qt 引⼊对象树的概念,在⼀定程度上解决了内存问题。 

当⼀个 QObject 对象在堆上创建的时候,Qt 会同时为其创建⼀个对象树。不过,对象树中对象的
顺序是没有定义的。这意味着,销毁这些对象的顺序也是未定义的。
任何对象树中的 QObject 对象 delete 的时候,如果这个对象有 parent,则⾃动将其从 parent 的
children() 列表中删除;如果有孩⼦,则⾃动 delete 每⼀个孩⼦。Qt 保证没有 QObject 会被
delete 两次,这是由析构顺序决定的。

 不过也存在一些特殊情况:

如果 QObject 在栈上创建,Qt 保持同样的⾏为。正常情况下,这也不会发⽣什么问题。来看下⾯的代码⽚段:

作为⽗组件的 window 和作为⼦组件的 quit 都是 QObject 的⼦类(事实上,它们都是QWidget的⼦类,⽽QWidget 是 QObject 的⼦类)。这段代码是正确的,quit 的析构函数不会被调⽤两次,因为标准 C++ 要求,局部对象的析构顺序应该按照其创建顺序的相反过程。因此,这段代码在超出作⽤域时,会先调⽤ quit 的析构函数,将其从⽗对象 window 的⼦对象列表中删除,然后才会再调⽤window 的析构函数。

但是如果我们调换一下对象的构造顺序:

情况⼜有所不同,析构顺序就有了问题。我们看到,在上⾯的代码中,作为⽗对象的 window 会⾸先被析构,因为它是最后⼀个创建的对象。在析构过程中,它会调⽤⼦对象列表中每⼀个对象的析构函数,也就是说, quit 此时就被析构了。然后,代码继续执⾏,在 window 析构之后,quit 也会被析构,因为 quit 也是⼀个局部变量,在超出作⽤域的时候当然也需要析构。但是,这时候已经是第⼆次调⽤ quit 的析构函数了,C++ 不允许调⽤两次析构函数,因此,程序崩溃了。

由此我们看到,Qt 的对象树机制虽然在⼀定程度上解决了内存问题,但是也引⼊了⼀些值得注意的事情。这些细节在今后的开发过程中很可能时不时跳出来烦扰⼀下,所以,我们最好从开始就养成良好习惯

 因此:在 Qt 中,尽量在构造的时候就指定 parent 对象,并且⼤胆在堆上创建。

Qt的坐标体系 

坐标体系:以左上⻆为原点(0,0),X向右增加,Y向下增加。 

这里跟数学的坐标体系是有点不一样的。

 对于嵌套窗⼝,其坐标是相对于⽗窗⼝来说的。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

高效运维:构建全面监控与自动化管理体系

在当今数字化时代&#xff0c;企业IT系统的稳定运行直接关系到业务的连续性和竞争力。运维团队作为保障系统稳定运行的中坚力量&#xff0c;面临着前所未有的挑战。随着云计算、大数据、物联网等技术的快速发展&#xff0c;系统架构日益复杂&#xff0c;运维工作也从传统的被动…

Docker网络和overlay的基础讲解

本人发现了两篇写的不错的文章&#xff1a;Docker网络 - docker network详解-CSDN博客&#xff0c;Docker 容器跨主机通信 overlay_docker overlay 网络-CSDN博客 因为这两篇文章中含有大量的例子&#xff0c;新手看起来毫不费力。于是我偷了个小懒&#xff0c;在本篇文章中没有…

C++开发基础之使用librabbitmq库实现RabbitMQ消息队列通信

1. 前言 RabbitMQ是一个流行的开源消息队列系统&#xff0c;支持多种消息协议&#xff0c;广泛用于构建分布式系统和微服务架构。可以在不同应用程序之间实现异步消息传递。在本文中&#xff0c;我们将熟悉如何使用C与RabbitMQ进行消息通信。 2. 准备工作 在 Windows 平台上…

vue elementui el-dropdown-item设置@click无效的解决方案

如图&#xff0c;直接在el-dropdown-item上面设置click&#xff0c;相应的method并没有被触发&#xff0c;查找资料发现需要在它的上级 el-dropdown 处使用 command 方法触发。 【template】 <el-dropdown placement"bottom-end" command"handleCommand&quo…

使用docker-compose单点搭建社区版seafile+onlyoffice在线word编辑平台

文章目录 前言一、平台介绍1. seafile平台介绍2. onlyoffice平台介绍 二、版本信息三、基础信息准备1.docker配置文件2.准备目录 四、onlyoffice部署五、seafile平台部署1.准备seafile.yaml文件2.运行平台 六、双平台对接总结 前言 记录使用docker-compose单点搭建社区版seafi…

游戏引擎学习第七天

视频参考:https://www.bilibili.com/video/BV1QFmhYcE69 ERROR_DEVICE_NOT_CONNECTED 是一个错误代码&#xff0c;通常在调用 XInputGetState 或 XInputSetState 函数时返回&#xff0c;表示指定的设备未连接。通常会出现以下几种情况&#xff1a; 未连接控制器&#xff1a;如…

计算机的错误计算(一百五十二)

摘要 探讨 MATLAB 中双曲正弦函数 sinh(x)与双曲余弦函数 cosh(x)的计算精度问题。 例1. 已知 计算 以及 直接贴图吧&#xff1a; 另外&#xff0c;16或17位的正确值分别为 0.5168712762709208e1、0.52645599648397069e1、0.2140244217618104e247 与 0.2140244217…

【C++】详解RAII思想与智能指针

&#x1f308; 个人主页&#xff1a;谁在夜里看海. &#x1f525; 个人专栏&#xff1a;《C系列》《Linux系列》 ⛰️ 丢掉幻想&#xff0c;准备斗争 目录 引言 内存泄漏 内存泄漏的危害 内存泄漏的处理 一、RAII思想 二、智能指针 1.auto_ptr 实现原理 模拟实现 弊端…

JDBC-Dao层模式

分层思维 分层思维是软件架构设计的一种重要思想&#xff0c;它通过将应用程序划分为多个相互独立且依赖关系的层。 通常分为以下三层关系。 web层&#xff1a;主要负责与用户进行交互&#xff0c;处理请求。 service层&#xff1a;业务逻辑层&#xff0c;主要负责处理应用程序…

三种单例实现

1、不继承Mono的单例 实现 使用 注&#xff1a; 使用需要继承BaseManager 泛型填写自己本身 需要实现无参构造函数 2、挂载式的Mono单例 实现 使用 注&#xff1a; 使用需要继承SingletonMono 泛型填写自己本身 需要挂载在unity引擎面板 3、不用挂载式的单例 实现 使…

Flink API 的层次结构

Apache Flink 提供了多层 API&#xff0c;每层 API 针对不同的抽象层次和用途&#xff0c;使得开发者可以根据具体需求选择合适的 API 层次。以下是 Flink API 的层次结构及其简要说明&#xff1a;

DHCP与DNS安全管理

一、DHCPSnooping的攻击防范功能配置&#xff08;路由器&#xff09; 1.启动设备 2.将pc设为DHCP获取IP地址 3.配置DHCP [AR1]dhcp enable //启动DHCP服务 [AR1]ip pool aaa //设置地址池 [AR1-ip-pool-aaa]network 192.168.10.0 mask 24 //设置地址范围 [AR1-ip-poo…

51c视觉~合集6

我自己的原文哦~ https://blog.51cto.com/whaosoft/11603901 #CSWin-UNet 将自注意力机制集成到UNet中&#xff01;CSWin-UNet&#xff1a;U型分割方法&#xff0c;显著提高计算效率和感受野交互&#xff01;本文提出了CSWin-UNet&#xff0c;这是一种新颖的U型分割方法&…

深度学习服务器租赁AutoDL

省钱绝招 #AutoDL #GPU #租显卡

设备接入到NVR管理平台EasyNVR多品牌NVR管理工具/设备的音视频配置参考

NVR管理平台EasyNVR是一款功能强大的安防视频监控平台&#xff0c;能够轻松实现视频流的导入、录像、存储和回放等功能。在将设备接入到海康NVR管理平台EasyNVR时&#xff0c;视音频配置是确保视频监控效果的重要步骤。本文将详细介绍如何将设备接入到EasyNVR平台&#xff0c;并…

35.3K+ Star!PhotoPrism:一款基于AI的开源照片管理工具

PhotoPrism 简介 PhotoPrism[1] 是一个为去中心化网络设计的AI照片应用,它利用最新技术自动标记和查找图片,实现自动图像分类与本地化部署,你可以在家中、私有服务器或云端运行它。 项目特点 主要特点 浏览所有照片和视频,无需担心RAW转换、重复项或视频格式。 使用强大的…

HTML之列表

练习题&#xff1a; 图所示为一个问卷调查网页&#xff0c;请制作出来。要求&#xff1a;大标题用h1标签&#xff1b;小题目用h3标签&#xff1b;前两个问题使用有序列表&#xff1b;最后一个问题使用无序列表。 代码&#xff1a; <!DOCTYPE html> <html> <he…

redis实现消息队列的几种方式

一、了解 众所周知&#xff0c;redis是我们日常开发过程中使用最多的非关系型数据库&#xff0c;也是消息中间件。实际上除了常用的rabbitmq、rocketmq、kafka消息队列&#xff08;大家自己下去研究吧~模式都是通用的&#xff09;&#xff0c;我们也能使用redis实现消息队列。…

Linux下MySQL的简单使用

Linux下MySQL的简单使用 导语MySQL安装与配置MySQL安装密码设置 MySQL管理命令myisamchkmysql其他 常见操作 C语言访问MYSQL连接例程错误处理使用SQL 总结参考文献 导语 这一章是MySQL的使用&#xff0c;一些常用的MySQL语句属于本科阶段内容&#xff0c;然后是C语言和MySQl之…

即插即用篇 | YOLOv8 引入 代理注意力 AgentAttention

Transformer模型中的注意力模块是其核心组成部分。虽然全局注意力机制具有很强的表达能力,但其高昂的计算成本限制了在各种场景中的应用。本文提出了一种新的注意力范式,称为“代理注意力”(Agent Attention),以在计算效率和表示能力之间取得平衡。代理注意力使用四元组(Q…