【QT】初始QT

news2024/11/24 9:43:24

目录

  • 一.背景
    • 1.GUI开发的各种技术方案
    • 2.什么是框架
    • 3.QT支持的系统
    • 4.QT的版本
    • 5.QT的优点
    • 6.QT的应用常见
  • 二.环境搭建
    • 1.认识QTSDK中的重要工具
    • 2.使用QT Creator创建项目
    • 3.项目解释
      • (1)main.cpp
      • (2)widget.h
      • (3)widget.cpp
      • (4)widget.ui
      • (5)Empty.pro
      • (6)临时文件
  • 三.初始QT
    • 1.Hello World
      • (1)图形化方式
      • (2)代码方式
        • 2.1 内存泄漏问题
        • 2.2 对象树
        • 2.3 乱码问题
          • 背景
          • 解决问题
        • 2.4 小节
      • (3)编辑框实现
      • (4)按钮方式
        • 4.1 图形化方式
        • 4.2 代码方式
    • 2.QT中的命名规则
    • 3.QT Creator中的常见快捷键
    • 4.帮助使用文档
    • 5.窗口坐标体系

一.背景

互联网核心岗位:

  • 开发(程序员)、测试、运维(管理机器)、产品经理(非技术岗,提出需求)
  • 开发岗位:后端开发、前端开发、算法工程师、游戏开发
  • 前端开发:网页前端开发、桌面应用开发、移动端应用开发

qt属于客户端开发,也就是前端开发的一个分支,属于桌面应用开发

  • 客户端:直接和用户打交道的这一端的程序(edge浏览器、qq音乐、steam…)
  • 服务器:站在客户端背后的男人

虽然大部分的客户端程序,是需要有背后的服务器做支持的,但是确实也有些程序,是不需要服务器的,这样的程序(画图板、计算器…)同样也是直接和客户打交道,也可以称之为 “客户端程序”。

准确来说,qt特指用来进行桌面应用开发(电脑上的程序)设计到的一套技术。

qt无法进行网页开发,也不能进行移动端开发(虽然目前qt官方也支持移动应用的开发了,但是目前还没有听说有啥知名的商用移动应用程序是qt开发的)

  • 客户端开发的重要任务:编写和用户交互的界面

和用户交互的界面,两种分格:

  1. 命令行界面/终端界面(黑框框) —— TUI (专业软件,给程序员用的)
  2. 图形化界面 —— GUI (普通用户,会用鼠标即可)

qt是用来编写桌面GUI程序的一套框架~~

在Windows上编写GUI程序,也有很多的解决方案 ~~ qt只是其中的一种(学会了一个,其他的都比较容易上手)

1.GUI开发的各种技术方案

Windows下还有那些方案,可以开发GUI?

  1. Windows API:windows系统提供的原始API
    开发起来非常原始和繁琐,但在windows刚诞生的阶段也没有什么好选的

  2. MFC:利用面向对象的思想,将windows API分装起来变为一个个控件,并且提供图形化界面的方式可以拖拽控件。
    上世纪90年代到今天,影响力非常深远

  3. QT:采取面向对象的方式将系统API进行分装,提供可视化界面
    上世纪1991年诞生,和MFC不同,MFC早以不在更新,而qt仍然在不停的退出新的版本,至今仍然非常有生命力。

以上都是基于C/C++搭建出来的一些 GUI 开发的技术体系~~
后来微软自己做了一个编程语言C#,在进行windows界面开发时,微软逐渐偏向于C#

  1. Windows Forms 给C#量身定做的一套开发GUI技术体系

  2. WPF、UWP:Windows Forms的升级版

  3. Electron:本质上是基于HTML网页,打包成一个基于windows上运行的客户端程序~~
    这个技术体系,最初是用来开发“atom”文本编辑器的,而“atom”是GitHub官方开发的
    后来“atom”不在流行,被微软的VSCode取代
    比较大的缺点是开发出来的程序,运行效率低于上面介绍的一些原生开发的技术体系,所以很少有公司去使用它

QT虽然是上述解决方案中的一种,仍然属于是其中非常能打的(商业公司的产品,使用QT非常多,对应的就业岗位相比于其他的技术方案也是很多的)

并且,qt可以跨平台,支持windows、linux、Mac,对于公司来说,更容易降低开发成本。

2.什么是框架

QT是跨平台的C++ 图形用户界面应用程序框架 。它为应用程序开发者提供了建立艺术级图形界面所需要的所有功能。它是完全面向对象的,很容易扩展。QT为开发者提供了一种基于组件的开发模式,开发者可以基于简单的拖拽和组合来实现复杂的应用程序,同时也可以使用C++语言进行高级开发。

一般而言自由、灵活就意味着容易出错!毕竟不能给它自由过了火。

而框架,本质是一群大佬发明出来,方便让咱们能力低的程序员,写出来的东西比较靠谱的东西,也就是说看框架是用来限制程序员的自由,让代码的下限得到保证。

平时我们了解较多的是库,库和框架都属于大佬把一些程序写好,让你去使用。

  • 库:被程序员调用(程序员是主体)
  • 框架:程序员配合框架,完善填充框架中留出的一些细节(框架是主体)

相对于C++,Java对于框架更加的依赖。

编写C++代码,框架当然也很重要,C++的生态是割裂的,散列的,不像隔壁Java,存在非常大的社区,一统天下。C++不同的开源社区/大厂,各自有各自的框架,各自为政。

相比之下,像QT这种,能够被大家共同认可的框架,在整个C++生态中是不常见的

3.QT支持的系统

  1. Windows系统
  2. Linux系统,尤其是Linux中的KDE桌面基于QT构建的
    Linux主要是给服务器使用的,服务器不需要图形化界面,是TUI界面,基于命令操作,门槛更高, 效率也更高。Linux需要将资源放在更重要的地方,而不是图形化界面。
    Linux桌面环境中存在好几套桌面环境,也有少数的用户,使用Linux做为桌面,但是这些桌面环境都不是很成熟,一起来会很不舒服
    • GNOME:基于GTK创建的
    • KDE:基于QT创建的
  3. Mac系统
  4. 嵌入式系统
    嵌入式系统也是QT实际开发中的主要系统之一,日常使用的冰箱、洗衣机、路由器…这些设备内部也有计算机,这种设备,里面的计算机,配置就不会那么高。
    有先嵌入式系统,也是需要运行图形化界面的程序,这个时候,QT就可以起到作用,尤其是一些工业设备上。
    当然在这个领域,QT也受到了来自安卓的挑战

4.QT的版本

目前最新的版本是Qt6,但是相对来说,qt6和qt5之间的核心功能区别不大,并且企业中也仍然有大量的项目在使用qt5.

另外,qt在发布的时候还提供了两种许可证:

  • 商业许可:你想开发一个QT程序,就可以向所属公司购买商业许可证
  • 开源许可:也就是想要开发一个程序,不需要花钱,直接拿来用

而相比开源序可,商业序可提供的更是一种服务,一种技术支持,当开发程序时若出现问题,可以直接找所属公司,让他们来解决。

5.QT的优点

  • 跨平台,几乎支持所有的平台

  • 接口简单,容易上手,学习qt框架对学习其他框架有参考意义

  • 一定程度上简化了内存回收机制;
    C++是一个讲究效率的语言,对于内存回收重视度不够,而qt采取了一定的策略,实现了半自动的垃圾回收,简化内存释放,尽可能小的影响程序的运行效率。

  • 开发效率高,能够快速的构建应用程序

  • 有很好的社区氛围,市场份额在缓慢上升

  • 可以进行嵌入式开发

6.QT的应用常见

  • 桌面的应用程序
    qt能够创建各种类型的桌面应用程序,包括文件管理器、媒体播放器、绘图程序等。Qt应用程序支持多种操作系统,可以运行在多个桌面操作系统上

  • 移动应用程序
    支持Android和IOS移动操作系统,为应用程序提供强大的跨平台能力。
    Android和IOS系统已经有许多类似的框架,很少有人使用QT去开发

  • 嵌入式系统
    QT在嵌入式领域应用非常广泛,它可以构建面向各种设备的图像应用程序,在机顶盒、车载娱乐系统、安防监控领域具有广泛的应用。

二.环境搭建

注意:具体如何下载安装QT,已经有大量的资料存在,这里不在赘述
QT的开发环境,需要安装3个部分:

  1. C++ 编译器(gcc、cl.exe…)
    注意编译器不是ide,不是visual studio,编译器只是IDE调用的一个程序

  2. QT SDK
    SDK是软件开发工具包,其中包含开发qt程序用到的各种工具,一般SDK中已经内置了一个C++编译器,比如Windows版本的QT SDK里已经内置了C++编译器。(内置的编译器是mingw,widows版本的gcc/g++)
    要是想用VS内置的cl.exe做为编译器,也不是不行,需要配置很多东西,容易出错。
    具体安装的过程中,需要将对应的C++编译器给勾选上。

  3. 需要有一个QT的集成开发环境(IDE)

    1. QT官方提供的QT Creator
      使用这个写qt,是最容易上手的方式,不需要任何额外的配置,开箱即用,在初学阶段建议使用,虽然在使用过程中存在不少bug(有先bug非常影响使用体验),但用起来挺方便的,比较适合初学者。
    2. Visual Studio
      功能更强,但需要额外的配置更多,更容易出错,有先公司开发商业QT程序的时候,可能会使用VS。需要给VS安装QT插件,并且需要QT SDK使用VS的编译器重新编译(可以使用编译好的版本)
    3. Eclipse
      Eclipse并不只是java IDE,本身是一个IDE本身,可以搭配不同的插件构成不同的IDE,但目前Eclipse市场份额受到很大的冲击。

说是安装三个东西,其实只需要安装一个QT SDK,另外两个也就有了。

1.认识QTSDK中的重要工具

  • Assistant 5.15.2:Qt自带的离线版本的官方文档,虽然Qt也有中文文档(非官方的),但主流仍然是去看官网的英文文档。
  • QT Designer:图形化的设计界面的工具,通过拖拽控件的方式来快速的生产界面,后面经常使用但是搭配QT Creator来使用
  • Linguist:QT语言家,作用是对国际化进行支持,有的时候,写的程序,是要和国际接轨,允许你单独创建一个语言配置文件,把界面上需要用到的各种文字,都配置到文件中,并且在文件中提前的把各种语言的翻译都配置进去。
  • QT Creator:QT的集成开发工具,学习QT过程中主要使用的工具

2.使用QT Creator创建项目

在这里插入图片描述
Application:应用程序,如果使用QT写一个GUI程序,就应该选这个

  • QT Widget:传统的开发GUI的方式
  • QT Quick:QT搞出的一套新的用来开发GUI的方式
  • QT for Python:QT不只支持C++,也支持python和Java

在这里插入图片描述
构建系统,通过QT写的程序,涉及到一系列的 “元编程” 技术。通过代码来生成代码。

QT框架会在编译的时候,自动先调用一系列的生成工具,基于你自己写的代码,生成一系列的其他C++代码,最终编译的代码,也就是最后生成的这些代码

  • qmake:老牌的构建工具
  • CMake:并非是QT专属,很多开源项目都会使用CMake
  • Qbs:新一代的QT构建工具,实际上很少人去使用它

在这里插入图片描述

  • Base class:使用QT Creator创建项目时,会自动生成一些代码出来,生成的代码就是包含一个类。此处就是要选择这选择这个自动生成的类的父类是谁。
    在这里插入图片描述

    • QMainWindow:完整的应用程序窗口,可以包含菜单栏、工具栏、状态栏…
    • QWidget:表示一个控件(窗口上的一个具体的元素,输入框、按钮、下拉框、单选按钮、复选按钮…)
    • QDialog:表示一个对话框

    QT中内置的类都是以Q开头的
    QWidget最简单,初学阶段选QWidget。

  • Class name:自动生成的类的类名,它的父类是Base class选择的类

  • Header File、Source File:类对应的文件
    次数生成的文件名和类名关联,关联并不是强制的,推荐大家使用一样的

  • form file:Qt中创建图形化界面的方式有两种

    1. 直接通过C++代码的方式创建界面
    2. 通过from file(ui文件),以图形化的方式来生成界面
      此时就可使用QT Designed或者QT Creator来编辑这个ui文件,从而以图形化的方式快速的生成图形界面

在这里插入图片描述
选择翻译文件(对应语言),此处暂时不关注,和国际化相关

在这里插入图片描述
选择一下基于那个编译器的QT SDK来构建后续的代码。

3.项目解释

(1)main.cpp

#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[]) // 命令行参数
{
    QApplication a(argc, argv); // 将命令行参数作为参数创建对象
    Widget w; // Widget 刚刚创建项目时,填写了生成的类名,也就是创建一个控件对象
    w.show(); // 显示该控件对象
    return a.exec();
}
  • 编写一个QT的图形化界面程序,一定需要有QApplication对象
  • Widget的父类是QWidget,都是由QWidget提供(QT是很好的面向对象示例,在学习QT的过程中可以很好的理解面向对象)
  • exec在Linux中曾经学过,表示进程中的程序替换,把可执行文件中的代码和数据替换到当前进程中。当然,当前QT中的exec和Linux中的没有如何关系。

(2)widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; } // 命名空间,声明类
QT_END_NAMESPACE

class Widget : public QWidget //QT SDK内置类,要想继承QWidget必须包含头文件,#include <QWidget>
{
    Q_OBJECT  // QT内置的一个宏,本质是文本替换

public:
	// QT中引入了“对象树”机制,创建的QT对象,就可以把这个对象挂在树上,往树上挂的时候需要指定“父节点”(后面讲)
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui; // 与 from file 密切相关
};
#endif // WIDGET_H
  • QT设定,使用QT中内置的类,包含的头文件的名字就是和类名一致的
    也不是用到的所有的QT的类都需要显示包含头文件。参考C++中,头文件可能是间接包含

  • 写代码的原则:一个QT的类,先拿过来,如果直接能用,说明对应的头文件已经被包含过了,无需显示包含。如果这个类提示找不到定义,在手动显示对应的头文件。

  • Q_OBJECT展开之后会生成一大堆代码,当我们使用QT中的 信号 的时候需要引入这个宏(具体后面介绍)

(3)widget.cpp

#include "widget.h"    // 创建项目生成的头文件
#include "ui_widget.h" // form file 被 qmake 生成的头文件

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this); // 6、8行 把 form file 生成的界面和当前 widget 关联起来
}

Widget::~Widget()
{
    delete ui;
}
  • 关键要点:form file

(4)widget.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Widget</class>
 <widget class="QWidget" name="Widget">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Widget</string>
  </property>
 </widget>
 <resources/>
 <connections/>
</ui>
  • 单击Widget.ui文件,进入上述代码编辑部分,该代码格式为 xml
    xmlhtml 非常相似,都是使用成对的标签来表示数据
    xml 这里的标签,有那些标签,都有什么含义,这个是程序员自定义的。此处看到的这些标签,就是QT开发大佬们自己约定的(这里的标签具体是啥含义不需要关注,只需要直到ui文件本质是一个xml即可)
    html虽然也是通过标签来表示,但是它的标签是固定的,每个标签是啥含义,有一个专门的标签委员会约定,所有的浏览器也是按照同样的规则来解释的。
    这里的xml标签的含义,就类似与Linux网络原理中学过的一个话题,自定义应用层协议。

  • QT中使用xml文件就是去描述程序的界面是啥样的,进一步的qmake会调用相关的工具,依据这个文件生成一些C++代码,从而把完整的界面构造出来。

  • 双击Widget.ui文件,QT Creator就会调用QT Designer,打开ui文件,打开图形化界面编辑器

在这里插入图片描述

(5)Empty.pro

QT       += core gui  // 要引入的QT模块,后面学习到一些内容的时候可能会修改这里

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

// 下面三项描述了当前项目中,参与构建的文件有啥(编译器要编译那些文件)
// 这个地方不需要手动修改,QT Creator 帮咱们自动维护好
SOURCES += \
    main.cpp \
    widget.cpp

HEADERS += \
    widget.h

FORMS += \
    widget.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
  • QT项目的工程文件,也是qmake工具构建时候的重要依据

  • .pro类似于在Linux上的makefile
    makefile其实是一个非常古老的技术,是常见的方式,但不是唯一的方式
    qmake搭配 .pro 起到的作用就和 Makefile 是类似的

  • QT Creator将运行过程都分装好了,只需要点击运行即可

(6)临时文件

上面的看到的各个文件都是源代码,如果编译运行QT项目,构建过程中还会生成一些中间文件
在这里插入图片描述
打开文件资源管理器,看看项目对应的目录是什么样子
在这里插入图片描述
运行一次文件,就会在项目目录并列的地方多出一个 build-xxxx 目录,这个目录里面就是该项目运行过程中,生成的一些临时文件。
在这里插入图片描述

  • 编译QT程序,还是会用到makefile,只不过这个makefile不需要手动写,而是 qmake 自动生成的
  • ui_widget: widget.ui xml生成的 .h文件
    在这里插入图片描述
    在这里插入图片描述

三.初始QT

1.Hello World

有两种方式实现Hello World

  1. 通过图形化方式,在界面上创建出一个控件,显示 Hello World
  2. 通过重代码的方式,通过编写代码,在界面上创建一个控件,显示Hello World

接下来,我们先分别了解代码和图形化方式,在来看一下不同的控件对应这两种方式的实现过程。

(1)图形化方式

双击Widget.ui文件,进入图形化界面,在控件区的最下方有一个Display Widgets分类,这个分类中的所有控件都是用来显示,使用Label控件(在QT中完成Hello World 可以通过很多种控件实现),拖拽该控件,放入程序窗口,修改内容,得到结果:
在这里插入图片描述
点击左下角运行:

在这里插入图片描述
刚才往界面上拖拽了一个QLabel控件,此时,ui文件的xml中就多出一段代码(如下:16~28行)

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Widget</class>
 <widget class="QWidget" name="Widget">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Widget</string>
  </property>
  <widget class="QLabel" name="label">
   <property name="geometry">
    <rect>
     <x>310</x>
     <y>230</y>
     <width>221</width>
     <height>51</height>
    </rect>
   </property>
   <property name="text">
    <string>Hello World</string>
   </property>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

进一步的qmake就会在编译项目的时候,基于这个内容生成一段C++代码,通过这个C++代码构建出界面内容。(自动完成)

而在该项目临时文件ui_widget.h中,就会根据Widget.ui配置文件,创建出对应的方法:

    void setupUi(QWidget *Widget)
    {
        if (Widget->objectName().isEmpty())
            Widget->setObjectName(QString::fromUtf8("Widget"));
        Widget->resize(800, 600);
        // 下面三行创建标签
        label = new QLabel(Widget);
        label->setObjectName(QString::fromUtf8("label"));
        label->setGeometry(QRect(310, 230, 221, 51));

        retranslateUi(Widget);

        QMetaObject::connectSlotsByName(Widget);
    } // setupUi

(2)代码方式

一般我们通过代码来构建界面时,一般会将构造界面的代码放在 Widget/MainWindow 的构造函数中。(以Widget为类)而构造函数在main.cpp中的Widget w;创建对象时调用,调用的是widget.cpp中对应的构造函数。

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

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget) // ui成员变量是qmake自动生成的
{
    ui->setupUi(this);
}

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

使用代码方式要使用Lable标签,那必须要添加头文件,否则报错:
在这里插入图片描述
添加QLable头文件时出现了两个文件:

  • qlabel.h:早期QT使用的头文件分格
    当时C++的标准还没有确立,直到C++98之后,规定包含头文件,比如统一使用#include 代替原有的#include <stdio.h>

  • QLabel:在原来头文件的基础上,重新使用更加规则的头文件名编写该头文件
    目前我们用更加规范的也就是QLabel

#include "widget.h"
#include "ui_widget.h"
#include <QLabel>  // 在QT当中,每个类都有对应的头文件

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    QLabel* lable = new QLabel(this); // 创建Lable对象,参数this表示给当前lable对象指定父对象(父对象:Widget w对象)(对象树,使各个节点连在一起)
    // 创建对象时,可以在堆上创建,也可以在栈上创建,两种写法都行,但是更推荐在堆上创建(原因在对象树中讲)
    
    lable->setText("Hello World!"); // 设置控件中要显示的文件,参数为const QString,QString内部提供构造函数转换C分格字符串,
    // QT诞生在没有标准及标准库的年代,在当时如何表示字符串并不统一,而且当时使用的字符串方式都不好用,于是自己建造轮子,
    // 做了一系列的基础类,来支持QT的开发,如:字符串:QString、动态数组:Qvector、链表:QList等
    // 经过很多年的打磨,C++标准库和QT使用的虽然本质是一个东西,但是两种又是分开的,想要删掉某一方这是不可能的,只能共存。
    // 所以,在开发代码时可以使用标准库或QT提供的,但是在QT原生的api中,涉及到的东西,用的都是自己的这一套东西。
    // 后续代码中会经常遇到QString,很少遇到std::string,但两种可以相互转换
    // QString用起来要比std::string好,因为QString内部已经对于字符编码做了处理(如转码操作),而std::string啥也没干
}

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

在这里插入图片描述

  • 通过代码创建QLable默认是在左上角,想要换到其他位置也是可以的(后续在下面窗口坐标系中讲)
2.1 内存泄漏问题

在上述代码中我们发现,new出来的对象,并没有释放,难道不会造成内存泄漏吗?

#include "widget.h"
#include "ui_widget.h"
#include <QLabel>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QLabel* lable = new QLabel(this); // new出来的对象没有释放
    lable->setText("Hello World!"); 
}

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

上述代码中,在QT中不会产生内存显露问题,label对象会在合适的时候被析构释放,虽然没有手动delete,但确实释放了。

之所以能够将该对象释放,主要是因为把这个对象挂在了对象树上。(详细看下面)

2.2 对象树

对象树不只是在QT中用到,前端开发(网页开发)也涉及到类似的对象树(DOM),本质上也是一个树形结构(N叉树),通过树形结构把界面上的各种元素组织起来。

QT中也是类似的,也搞了一个对象树,也是N叉树,把界面上的各种元素组织起来。

在这里插入图片描述
使用这些对象树,将这些内容组织起来,最主要的目的就是在合适的时机(窗口关闭),把这些对象统一进行释放。

这里的树上的对象,统一销毁是最好的,如果提前销毁,就会导致对应的控件在界面上不存在了。

所以创建对象时使用new,就是为了把这个对象的生命周期交给QT对象统一管理。若是在栈上创建对象,在运行时就会出现问题(提前释放),生命周期结束后,对应的栈就会销毁,对象也不会长久存在。

为了更好的看到这个现象,我们做一个小demo

  1. 创建mylabel类
    在这里插入图片描述

  2. 打开mylabel.h头文件

    #ifndef MYLABEL_H
    #define MYLABEL_H
    #include <QLabel>
    
    // 使用F4可以切换到对应的.cpp文件
    
    class mylabel : public QLabel
    {
    public:
       // 构造函数使用带QWidget* 版本的,这样才能保证添加到对象树上
        mylabel(QWidget* parent);
       ~mylabel();
    };
    
    #endif // MYLABEL_H
    

    注意这里需要手动添加头文件

  3. 点击F4,切换到mylabel.cpp文件,修改该文件如下:

    #include "mylabel.h"
    #include <iostream>
    
    mylabel::mylabel(QWidget* parent)
        :QLabel(parent)
    {
    
    }
    
    mylabel::~mylabel()
    {
        std::cout << "mylable 被销毁!" << std::endl;
    }
    
  4. 打开widget.cpp文件,修改文件如下:

    #include "widget.h"
    #include "ui_widget.h"
    #include "mylabel.h"
    
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::Widget)
    {
        ui->setupUi(this);
    
        // 使用自己定义的 mylabel 代替原来的 QLabel,所谓的“继承”本质上是扩展,保持原有功能不变的基础上,
        // 给对象扩展一个析构函数,通过这个析构函数,打印一个自定义的日志,方便咱们观察程序的运行效果
        mylabel* label = new mylabel(this);
        label->setText("hello World!");
    }
    
    Widget::~Widget()
    {
        delete ui;
    }
    
  5. 运行该项目,显示hello world!
    在这里插入图片描述

  6. 关闭显示窗口,在version Control模块,查看日志,看到mylabel类执行析构函数时输出内容:
    在这里插入图片描述
    由此,我们可以确定,在QT中我们new创建一个对象,该对象被放入对象树中,当窗口被销毁时,就会自动销毁对象树上所有对象,mylabel的析构函数被执行。

2.3 乱码问题

观察上面的demo,我们看到析构函数所输出的内容与我们想要它输出的存在差异
在这里插入图片描述
预期打印的时“被销毁”三个中文,但是实际显示效果出现了乱码。

对于乱码,我想大多数人会经常涉及到,而乱码问题,出现的原因有且只有一个(不限于C++):编码方式不匹配

背景

首先,我们要知道一个汉字,在计算机中,占几个字节?大多数人回答都是两个字节,其实这是不对的。

针对这个问题,只要你回答出一个具体的数字,就一定是错的,这需要结合前提条件,即当前中文编码使用的是那种方式(字符集)。

中文中的常用字大概是4千多个,算上不常见的,总数差不多是6万左右,参考ASCII码表,将这6万格字整理为一个表格,不同的数字对应不同的字,这对于计算机来说不是难事,对于这些表格称之为字符集,但具体这个表格长什么样子,不同的组织给出的结果不同。

表示汉字的字符集,其实有很多种,不同的字符集,表示同一个汉字,使用的数字不同。

随着时间的推移,很多字符集淹没在了历史中,目前,表示汉字的字符集,主要有两种方式:

  1. GBK(中国大陆):使用2个字节表示一个汉字(Windows简体中文,默认的字符集就是GBK)
  2. UTF-8/UTF8:边长编码,表示一个符号使用的字节数存在变化(2~4不定),但对于汉字,一般是3个字节(Linux默认就是UTF8)

一个汉字,具体的UTF8/GBK的一个汉字具体对于的编码是多少,可以通过在网上查找。

解决问题

了解了背景,我们在回到问题,编码不匹配就是,你的字符串本身是utf8编码的,但是终端(控制台)是按照gbk的方式来解析显示的,此时就会出现乱码问题。

想要解决这个问题,我们就要了解到,那些环境涉及到编码的问题,这些了解后将其统一自然就解决了。

在这里插入图片描述
上述的字符串使用的编码方式,与当前mylabel.cpp文件的编码方式是一致的。

对于如何查看一个文件的编码方式这里教大家一个小技巧:

  1. 在QT Creator中右击文件,选择在Explorer中显示
    在这里插入图片描述
  2. 打开后右击文件,选择打开方式,选择记事本打开
    在这里插入图片描述
  3. 在记事本的右下角给出编码方式
    在这里插入图片描述
    显示是UTF-8说明这个文件是UTF-8编码,显示是ANSI说明这个文件是GBK编码

在QT Creator内置的终端中显示出乱码,说明它不是UTF8编码,而且这个终端好像不能设置字符编码。

想要解决这个乱码问题,只能统一编码格式,要么改终端,要么改文件,终端不好改,而对于改文件,在当前表示中文的编码中,主流的方式就是使用UTF-8,这种编码方式适用范围极广,改它缺点也很大。

这里,我们就要使用到QT中提供的一个 qDebug() 这样一个工具,借助这个,就可以完成打印日志这个工作,很好的处理字符编码(不需要程序员关注,QT内部解决)。

修改mylabel.cpp如下:

#include "mylabel.h"
#include <iostream>
#include <QDebug>

mylabel::mylabel(QWidget* parent)
    :QLabel(parent)
{

}

mylabel::~mylabel()
{
//    std::cout << "mylable 被销毁!" << std::endl;
    qDebug() << "mylable 被销毁!";
}
  • QDebug是QT中的一个类,我们不会直接使用这个类,而是通过qDebug()这个东西,当做cout来使用。

后续在QT中,如果需要通过打印日志的方式,输出一些调试信息,都优先使用qDebug,虽然cout也行,但是凑团对于编码的处理不好,在Windows上容易出现乱码(如果在Linux上使用QT Creator,一般没事,Linux默认的编码一般都是utf8)。

qDebug还有一个好处,可以统一进行关闭,当我们想要它正常显示时它正常显示,若是不想,可以通过编译开关,来实现一键式的关闭。

2.4 小节
  1. 认识QLabel类,能够在界面显示字符串。

    通过 setText 来设置,参数 QString(QT中把C++里的很多容器类,进行了重新分装,历史原因)

  2. 内存泄漏

  3. 对象树,QT中通过对象树来统一释放界面的控件对象。

    QT还式推荐使用new的方式在堆上创建对象,通过对象树,统一释放对象

    创建对象树的时候,在构造函数中,指定父对象(此时才会挂在对象树上)

    如果你的对象没有挂在对象树上,记得手动释放

  4. 通过继承自 QT 内置的类,可以达到堆现有控件进行功能扩展的效果。

    QT内置的 QLabel,没法看到销毁的过程,为了看清,就创建类 mylabel,继承自QLabel,重写了析构函数。

    在析构函数中,加上日志,直观的观察到对象释放的过程。

    (面向对象继承的本质是对现有代码的扩展,也可以重写控件中任何的功能,达到对功能扩展的目的)

  5. 乱码问题 和 字符集问题

  6. 如果 QT 中打印日志,作为调试信息。

    使用cout固然可以,但不是上策(字符编码处理的不好,也不方便关闭)

    QT推荐使用qDebug()完成日志的打印。

  7. 我们之前调试程序的时候,都是使用调试器,VS/gdb,这里为什么打印日志调试?

    其实调试器有很多的局限性,是无法使用的,比如概率性问题,一个bug调试100次,只出现一次甚至更少,要想调试,使用调试器就不可能了。

    而无论使用那种方式,本质上就是观察程序执行的中间过程中间结果,只是在不同的场景下使用哪一个更有效而已。

(3)编辑框实现

编辑框分为如下两种:

  1. 单行编辑框:QLineEdit
  2. 多行编辑框:QTextEdit

这里我们使用单行编辑框即可,和上面的图形化方式相同,编辑框也可以使用代码的方式,这里先介绍控件的方式。

如下图,拖拽 Line Edit 控件到面板,输入字符串,运行后即为所需内容:

在这里插入图片描述
而对于代码的方式,我们需要修改 widget.cpp 内容如下:

#include "widget.h"
#include "ui_widget.h"
#include <QLineEdit>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QLineEdit* q = new QLineEdit(this);
    q->setText("hello World!");
}

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

(4)按钮方式

4.1 图形化方式

在设计中找到push button按钮(这里主要使用该按钮),拖拽其到面板修改内容即可:
在这里插入图片描述
作为一个按钮,可以进行点击,但当我们运行该程序,点击按钮会发现没有什么反映。

这里就涉及到了QT当中的信号槽机制,本质就是给按钮的点击操作,关联上一个处理函数,当用户点击的时候,就会执行该函数。(这里的函数本质就是一个回调函数)

想要管理这样一个函数需要使用到 connect() 函数(在Linux网络编程中,也学到过这样一个函数connect函数,用来给TCP socket建立连接用的,这里的connect与网络中的没有任何关系),该函数是QObject这个类提供的静态函数,这个函数的作用就是 “连接信号” 和 “槽”。

修改Widget.cpp中代码如下:

#include "widget.h"
#include "ui_widget.h"
#include <QObject>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    connect(ui->pushButton, &QPushButton::clicked, this, &Widget::handleClick);
}

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

void Widget::handleClick() // 注意:需要在Widget.h文件中添加该函数声明
{
    ui->pushButton->setText("hello QT!");
}

运行结果如下:

在这里插入图片描述
接下来我们来简单了解一下connect这个函数:

static QMetaObject::Connection connect(
    const QObject *sender, 
    const QMetaMethod &signal, 
    const QObject *receiver, 
    const QMetaMethod &method, 
    Qt::ConnectionType type = Qt::AutoConnection);
  • 第一个参数表示谁发出的信号
    这需要访问form file(ui 文件)中创建的控件。
    在QT Designer(设计)中创建一个控件,此时会给这个控件分配一个objectName属性,这个属性的值,要求是在界面中是唯一的(不能和别人重复),这个值可以自己生成,也可以手动修改。
    如下图,为上面按钮控件对于的值:
    在这里插入图片描述

    qmake在预处理.ui文件的时候,就会根据这里的 objectName 生成对于的C++代码。
    若我们打开该项目对应的临时文件,找到 ui_widget.h 文件打开后,会发现如下成员变量:
    在这里插入图片描述

    C++代码中该 QPushButton 类型所创建对象的名字就是这里的 objectName对应的值,这个变量就是ui属性中的成员变量。
    若是我们手动修改QT Designer中该控件的objectName值,ui_widget.h中对应的名称也会改变。(注意:变的是变量名,QPushButton是类型名,没办法变)

  • 第二个参数在点击按钮时就会触发这个信号,表示发出了啥信号
    有了这个信号就要将它关联到具体的槽函数上

  • 第三个参数表示谁来处理这个给信号
    指定关联到那个对象的槽函数

  • 第四个参数表示具体怎么处理这个信号
    指定具体的槽函数

4.2 代码方式

我们新建一个项目,修改widget.cpp文件如下(widget.h文件也需要修改,在代码中有体现):

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

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    myButton = new QPushButton(this); // 因为需要在处理信号函数中用到myButton变量,将其在Widget类中设置为成员变量
    myButton->setText("Hello World!");

    connect(myButton, &QPushButton::clicked, this, &Widget::handleClick);
}

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

void Widget::handleClick()
{
    if(myButton->text() == QString("Hello World!"))
    {
        myButton->setText("Hello QT!");
    }
    else
    {
        myButton->setText("Hello World!");
    }
}

对于纯代码版本,按钮对象是咱们自己new的,为了保证其他函数中也可以访问到这个变量,就需要把按钮对象,设定为Widget类的成员变量。

而对于图形界面,不需要自己new对象操作,这已经被QT自动生成了,而这个按钮对象,已经作为 ui 对象里的一个成员变量。无需作为 Widget 成员

对于这两种方法,在实际开发中使用谁没有主次之分。

如果当前的界面内容比较固定,此时就用图形化的方式来构建界面。

如果当前界面经常要动态变化,此时就以代码的方式来构建界面。

2.QT中的命名规则

在给变量/函数/文件/类 起名字是非常有讲究的:

  1. 起名要有描述性
  2. 如果名字有点长,有多个单词构成的,就需要使用适当的方式来区分不同单词

如下是QT中类名、函数名和变量名的命名规则:

  • 类名:首字母大写,单词和单词之间首字母大写;
  • 函数名及变量名:首字母小写,单词和单词之间首字母小写

3.QT Creator中的常见快捷键

  • F4:头文件和源文件之间切换
  • Alt + Enter:将光标移动到h文件中的方法声明,按Alt+Enter,再按回车键将在cpp中添加对应的方法实体
  • ctrl + / :注释
  • ctrl + R :运行
  • ctrl + B :编译
  • ctrl + 鼠标滚动 : 字体缩放
  • ctrl + F : 查找
  • ctrl + i :自动对齐
  • F1:帮助文件

4.帮助使用文档

打开帮助文件有三种方式(在QT Creator中使用):

  1. 光标选中想要查找的类或函数,点击F1
  2. QT Creator右侧栏边框中直接使用鼠标单机 “帮助” 按钮
  3. 直接在Windows搜素框搜素QT Assistant或是在QT安装目录下的mingw**/bin/assistant下直接点击该应用程序

本质上上述三种方式查找的都是同一份文档。

在实际开发中,一定会用到很多的第三方库和框架,很可能这些库会比较小众,网上很难找到一些相关资料,这就需要我们来查找官方文档,而且这个官方文档的作者很可能来自于其他国家,但只要我们所用到的东西走向世界,那一定会有英文的文档。

5.窗口坐标体系

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

在这里插入图片描述
计算机中采用左手坐标系,它的原点(0,0)在屏幕/窗口的左上角。

给QT中某个控件设置位置时,就需要指定坐标,相对于控件来说,坐标系的原点就是相对于它的父窗口/控件。

在这里插入图片描述
在前面讲对象树时已经提到,QPushButton的父元素/父控件/父窗口 就是QWidget。

QWidget没有父元素(NULL),所以它的父元素就是整个显示器桌面了。

重新创建一个项目,修改widget.cpp文件并运行如下:

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

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    myButton = new QPushButton(this);
    myButton->setText("Hello World!");
}

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

关于像素:
显示器本质上是由一大堆可以发光的小亮点/小灯泡来构成的
如果将自己的手机摄像头对准显示器,焦距放到最大,距离合适是可以看到的。
也可以在自己windows设置中查看自己显示器上的分辨率,这样我们就可以知道自己电脑横纵坐标最大取值范围。
在这里插入图片描述
move函数中参数为1,表示移动1像素。

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

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    myButton = new QPushButton(this);
    myButton->setText("Hello World!");
    myButton->move(100, 300);
}

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

在这里插入图片描述
同样的,不只是控件可以设置位置,运行出来的窗口也可以设置相对于桌面的位置,这需要使用在qmake创建的widget类对象来调用,也就是在widget构造函数中使用this指针调用,如下(这里就不掩饰结果了):

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

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    myButton = new QPushButton(this);
    myButton->setText("Hello World!");
    myButton->move(100, 300);


    this->move(100, 0);
}

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

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

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

相关文章

STM32单片机通过串口控制DDSM210 直驱伺服电机

1 电机介绍 官方资料&#xff1a;https://www.waveshare.net/wiki/DDSM210 DDSM210 直驱伺服电机是基于一体化开发理念&#xff0c;集外转子无刷电机、编码器、伺服驱动于一体的高可靠性永磁同步电动机&#xff0c;其结构紧凑&#xff0c;安装方便&#xff0c;运行稳定&#x…

飞致云开源社区月度动态报告(2024年4月)

自2023年6月起&#xff0c;中国领先的开源软件公司FIT2CLOUD飞致云以月度为单位发布《飞致云开源社区月度动态报告》&#xff0c;旨在向广大社区用户同步飞致云旗下系列开源软件的发展情况&#xff0c;以及当月主要的产品新版本发布、社区运营成果等相关信息。值得注意的是&…

服务器数据恢复—多块磁盘离线导致阵列瘫痪,上层lun不可用的数据恢复案例

服务器存储数据恢复环境&#xff1a; 某品牌MSA2000存储&#xff0c;该存储中有一组由8块SAS硬盘&#xff08;其中有一块热备盘&#xff09;组建的RAID5阵列&#xff0c;raid5阵列上层划分了6个lun&#xff0c;均分配给HP-Unix小型机使用&#xff0c;主要数据为oracle数据库和O…

链表面试题及其解析

1.返回倒数第k个节点 实现一种算法&#xff0c;找出单向链表中倒数第 k 个节点。返回该节点的值。 示例&#xff1a; 输入&#xff1a; 1->2->3->4->5 和 k 2 输出&#xff1a; 4 说明&#xff1a; 给定的 k 保证是有效的。 1.1快慢指针 即慢指针一次走一步…

[C++][数据结构]二叉搜索树:介绍和实现

二叉搜索树 概念 二叉搜索树又称二叉排序树&#xff0c;它是一棵空树&#xff0c;或者是具有以下性质的二叉树: 若它的左子树不为空&#xff0c;则左子树上所有节点的值都小于根节点的值若它的右子树不为空&#xff0c;则右子树上所有节点的值都大于根节点的值它的左右子树也…

JavaScript中的Math对象方法、Date对象方法

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;JavaScript 精粹 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; 文章目录 &#x1f31f;Math对象方法&#x1f344;1 Math静态属性&#x1f344;2 Math…

Vue阶段练习:组件拆分

页面开发思路 分析页面&#xff0c;按模块拆分组件&#xff0c;搭架子&#xff08;局部或全局注册&#xff09;根据设计图&#xff0c;编写html结构css样式拆分封装通用小组件&#xff08;局部或全局注册&#xff09;将来通过js动态渲染实现功能 BaseBrandItem.vue <templ…

A5资源网有哪些类型的资源可以下载?

A5资源网提供了广泛的资源下载&#xff0c;包括但不限于以下类型&#xff1a; 设计素材&#xff1a;包括各类图标、矢量图、背景素材、UI界面元素等&#xff0c;适用于网页设计、平面设计等领域。 图片素材&#xff1a;提供高质量的照片、插图、摄影作品等&#xff0c;可用于…

使用STM32F103C8T6与蓝牙模块HC-05连接实现手机蓝牙控制LED灯

导言: 在现代智能家居系统中,远程控制设备变得越来越普遍和重要。本文将介绍如何利用STM32F103C8T6单片机和蓝牙模块HC-05实现远程控制LED灯的功能。通过这个简单的项目,可以学会如何将嵌入式系统与蓝牙通信技术相结合,实现远程控制的应用。 目录 导言: 准备工作: 硬…

ue引擎游戏开发笔记(27)——解决角色移动及转动存在卡顿掉帧小技巧

1.需求分析&#xff1a; 随之游戏越来越大&#xff0c;难免出现部分时候移动出现卡顿&#xff0c;能否进行一定优化。 2.操作实现&#xff1a; 1.思路&#xff1a;采取捕获最后deltaseconds来逐帧进行旋转或移动&#xff0c;使动作显得不那么卡顿。 .2.首先在引擎中建立映射&a…

【深度学习】第一门课 神经网络和深度学习 Week 4 深层神经网络

&#x1f680;Write In Front&#x1f680; &#x1f4dd;个人主页&#xff1a;令夏二十三 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd; &#x1f4e3;系列专栏&#xff1a;深度学习 &#x1f4ac;总结&#xff1a;希望你看完之后&#xff0c;能对…

DRF解析器源码分析

DRF解析器源码分析 1 解析器 解析请求者发来的数据&#xff08;JSON&#xff09; 使用 request.data 获取请求体中的数据。 这个 reqeust.data 的数据怎么来的呢&#xff1f;其实在drf内部是由解析器&#xff0c;根据请求者传入的数据格式 请求头来进行处理。 drf默认的解…

连接一个 IP 不存在的主机时,会发生什么?(面试)

一、IP 不存在时 如果 IP 在局域网内&#xff0c;会发送 N 次 ARP 请求获得目的主机的 MAC 地址&#xff0c;同时不能发出 TCP 握手消息。 如果 IP 在局域网外&#xff0c;会将消息通过路由器发出&#xff0c;但因为最终找不到目的地&#xff0c;触发 TCP 重试流程。 二、IP…

✔ ★Java项目——设计一个消息队列(五)【虚拟主机设计】

虚拟主机设计 创建 VirtualHost实现构造⽅法和 getter创建交换机删除交换机创建队列删除队列创建绑定删除绑定发布消息 ★路由规则1) 实现 route ⽅法2) 实现 checkRoutingKeyValid3) 实现 checkBindingKeyValid4) 实现 routeTopic5) 匹配规则测试⽤例6) 测试 Router 订阅消息1…

酒水门店私域流量运营搭建执行规划方案

【干货资料持续更新&#xff0c;以防走丢】 酒水门店私域流量运营搭建执行规划方案 部分资料预览 资料部分是网络整理&#xff0c;仅供学习参考。 PPT可编辑&#xff08;完整资料包含以下内容&#xff09; 目录 精酿啤酒品牌私域执行运营的内容策划&#xff0c;涉及以下几个…

快讯! MySQL 8.4.0 LTS 发布(MySQL 第一个长期支持版本)

MySQL 第一个长期支持版本 8.4.0 LTS 发布&#xff0c;社区版下载地址&#xff1a; https://dev.mysql.com/downloads/mysql/ 功能变更 添加或更改的功能 组复制&#xff1a;与组复制相关的两个服务器系统变量的默认值已更改&#xff1a; 系统变量的默认值为 group_replication…

HashMap源码分析(jdk1.8,保证你能看懂)

现在的面试当中凡是那些大厂&#xff0c;基本上都会问到一些关于HashMap的问题了&#xff0c;而且这个集合在开发中也经常会使用到。于是花费了大量的时间去研究分析写了这篇文章。本文是基于jdk1.8来分析的。篇幅较长&#xff0c;但是都是循序渐进的。耐心读完相信你会有所收获…

【QT学习】12.UDP协议,广播,组播

一。Udp详细解释 UDP&#xff08;User Datagram Protocol&#xff09;是一种无连接的传输层协议&#xff0c;它提供了一种简单的、不可靠的数据传输服务。与TCP相比&#xff0c;UDP不提供可靠性、流量控制、拥塞控制和错误恢复等功能&#xff0c;但由于其简单性和低开销&#x…

for循环赋值

在for循环内将i赋值给j的问题 for(int i0,ji1;i<5;i){//此时j只会等于1cout<<"i-"<<i<<" j-"<<j<<endl; }如图&#xff1a; 将j放入循环体后没问题 for(int i0;i<5;i){int j i1; cout<<"i-"<<…

leetcode84柱状图中最大的矩形

题解&#xff1a; - 力扣&#xff08;LeetCode&#xff09; class Solution {public int largestRectangleArea(int[] heights) {Stack<Integer> stack new Stack<>();int maxArea Integer.MIN_VALUE;for(int i 0;i < heights.length;i){int curHeight hei…