目录
一、Qt背景介绍
二、搭建Qt开发环境
三、新建工程
四、Qt中的命名规范
五、Qt Creator中的快捷键
六、QWidget基础项目文件详解
6.1 .pro文件解析
6.2 widget.h文件解析
6.3 widget.cpp文件解析
6.4 widget.ui文件解析
6.5 main.cpp文件解析
七、对象树
八、窗口坐标系
什么是Qt?
Qt是⼀个跨平台的C++图形用户界面应用程序框架。它为应用程序开发者提供了建立艺术级图形界面所需的所有功能。它是完全面向对象的,很容易扩展。Qt为开发者提供了一种基于组件的开发模式,开发者可以通过简单的拖拽和组合来实现复杂的应用程序,同时也可以使用C++语言进行高级开发
图形用户界面:指采用图形方式显示的计算机操作用户界面,是计算机与其使用者之间的对话接口,是计算机系统的重要组成部分
Qt支持的平台
Qt的版本
目前最新的版本是Qt6,但是相对来说Qt6和Qt5之间的核心功能区别不大,并且企业中也仍然有大量的项目在使用Qt5。因此后续文章上仍然使用Qt5版本来进行介绍
Qt在发布的时候还提供了两种许可证
Qt的优点
Qt的应用场景
Qt能够创建各种类型的桌面应用程序,包括文件管理器、媒体播放器、绘图程序等。Qt应用程序支持多种操作系统,可以运行在Windows、Linux、macOS等桌面操作系统上
Qt支持Android和IOS移动操作系统,为应用程序提供了强大的跨平台能力。可以使用Qt构建各种
移动应用程序。如社交应用、游戏、娱乐等
Qt在嵌入式领域应用非常广泛,可以构建面向各种设备的图形应用程序,在机顶盒、车载娱乐系统、安防监控设备等领域具有广泛的应用
环境的下载与安装
先去官网下载Qt SDK:https://download.qt.io/archive/qt/
本文中下载的版本是5.14.2,可以自行选择
注意:
若电脑磁盘空间允许,建议全选以便后续使用;若空间不允许,请看下图自行选择
Qt环境变量配置
环境变量是在操作系统中一个具有特定名字的对象,它包含了⼀个或者多个应用程序将要使用到的信息。如Windows和DOS操作系统中的path环境变量,当要求系统运行一个程序而没有告诉它程序所在的完整路径时,系统除了在当前目录下面寻找此程序外,还会到path中指定的路径中找。
在Windows上设置Qt的环境变量是为了能够在命令行或其他应用程序中直接访问Qt相关的命令和工具
打开Qt Creator;在菜单栏中选中"文件";"新建文件或项目" 或者 使用快捷键Ctrl+n 或者 直接点击"new"
选择项目模板
新建项目对话框中有五类项目模板:
项目模板 | 说明 |
Application | Qt应用程序,包括普通窗体和QtQuick程序 |
Library | 可创建动态库、静态库、以及Qt Quick扩展插件、Qt Creator自身插件 |
其他项目 | 可以创建单元测试项目,Qt4设计师自定义控件、子目录项目 |
Non-Qt-Project | 非Qt项目,可以创建纯C或纯C++项目 |
Import Project | 导入项目。从版本控制系统管理的软件项目中导入旧的项目 |
一般常用的是第一类Application,选择后,在右侧会看到Qt应用程序的多个子模板
子模板 | 说明 |
Qt Widgets Application | 普通窗体模板,传统基于部件的窗体界面程序 |
Qt Console Application | Qt控制台应用程序(使用较少) |
Qt for python | 在Python下用LGPL的许可来开发闭源Qt软件 |
Qt Quick Application | Qt提供的⼀种高级用户界面技术,使用它可以方便快速的为移动以及嵌入式设备开发流畅美观的用户界⾯。Qt Quick模块是开发QML应用的标准库,提供了使用QML创建用户界面所需的一切,包括可视化、交互、动画、模型、视图、粒子效果以及着色效果等 |
选择不同的项目模板,Qt Creator就会在后续项目创建好了之后生成不同的基础代码
选择构建系统
选择Qt项目的构建系统,使用默认的"qmake"即可
Qt官方声明,因市场原因,弃用Qbs。对于Qt用户来说,qmake是当前使用最广泛的构建工具,CMake其次
类信息设置界面
目前有三种基类可供选择:
基类 | 说明 |
QWidget | 最简单、最基本的窗体程序,里面可以放置多个控件实现程序功能 |
QMainWindow | 主窗口类,一般用于较为复杂的应用程序,除了中央客户区界面,还包括菜单栏、工具栏、状态栏以及多个可停靠的工具对话框 |
QDialog | 基于对话框的程序,对话框一般用户弹窗,也可以用于主界面显示。对话框是从QWidget继承而来的,并丰富了一些功能,如模态显示和返回值等 |
三个类之间的关系如下图:
选择Qt套件
默认只有第⼀个"Desktop Qt 5.14.2 MinGW 64-bit",若安装配置了多个Qt套件,就可以都选上。Qt套件是指Qt程序从编译链接到运行环境的全部工具和Qt类库的集合,对于MinGW版本Qt程序生成和调试,至少需要MinGW中的编译器g++(自动调用链接器)、g++配套的基础库、调器gdb还
有使用MinGW环境编译而成的Qt类库自身。默认情况下,在上面Kit Selection里选中全部套件
选择版本控制系统
在项目管理界面可以设置作为子项目,以及加入版本控制系统管理。这两个功能暂时用不到,都用默认的
若想把代码提交到码云或者github,可以在此处选择git作为版本控制系统
驼峰命名法:
Qt偏好驼峰命名法,这一点不像之前学习的STL偏好蛇形命名,如unordered_map等
工程新建好之后,在工程目录列表中有一个后缀为".pro"的文件,即工程文件,是qmake自动生成的用于生成makefile的配置文件
双击进入该文件,该文件的核心内容如下:
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
DEFINES += QT_DEPRECATED_WARNINGS
TARGET = QtTest
TEMPLATE = app
SOURCES += \
main.cpp \
widget.cpp
HEADERS += \
widget.h
FORMS += \
widget.ui
这条语句的含义是,若QT_MAJOR_VERSION大于4,即当前使用的Qt5及更高版本,需要增加widgets模块。若项目仅需支持Qt5,也可以直接添加"QT += widgets"⼀句。不过为了保持代码兼容,最好还是按照Qt Creator生成的语句编写
CONFIG用来告诉qmake关于应用程序的配置信息,上述语句说明使用c++11的特性
定义编译选项。QT_DEPRECATED_WARNINGS表示当Qt的某些功能被标记为过时的,那么编译器会发出警告
app | 建立一个应用程序的makefile,默认值,若模板没有被指定,这个将会被使用 |
lib | 建立一个库的makefile |
vcapp | 建立一个应用程序的VisualStudio项目文件 |
vclib | 建立一个库的VisualStudio项目文件 |
subdirs | 这是一个特殊的模板。它可以创建一个能够进入特定目录的makefile并且为它调用make的makefile |
说明:
widget.cpp文件是类Widget的实现代码,所有在窗体上要实现的功能添加在此文件中
widget.ui是窗体界面定义文件,是一个XML文件,定义了窗口上的所有组件的属性设置、布局,及其信号与槽函数的关联等。用UI设计器可视化设计的界面都由Qt自动解析,并以XML文件的形式保存下来。在设计界面时,只需在UI设计器里进行可视化设计即可,而不用管widget.ui文件是怎么生成的
#include "widget.h"
#include // Qt系统提供的标准类名声明头⽂件,没有.h后缀
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w; //实例化窗⼝对象
w.show(); //调⽤show函数显⽰窗⼝
return a.exec();
}
在Qt中创建很多对象的时候会提供一个parent对象指针,这个parent到底是干什么的呢?
QObject是以对象树的形式组织起来的
这种机制在GUI程序设计中相当有用。如,一个按钮有一个QShortcut(快捷键)对象作为其子对象。当删除按钮的时候,这个快捷键理应被删除。这是合理的
QWidget是能够在屏幕上显示的一切组件的父类
对象树机制一定程度上解决了内存问题
若QObject在栈上创建,Qt保持同样的行为。正常情况下也不会发生什么问题
作为父组件的window和作为子组件的quit都是QObject的子类。这段代码是正确的,quit的析构函数不会被调用两次,因为标准C++要求,局部对象的析构顺序应该按照其创建顺序的相反过程。因此,这段代码在超出作用域时,会先调用quit的析构函数,将其从父对象window的子对象列表中删除,然后才会再调用window的析构函数
上述代码的析构顺序就有了问题。在上面的代码中,作为父对象的window会先被析构,因为它是最后一个创建的对象。在析构过程中,它会调用子对象列表中每一个对象的析构函数,即quit此时就被析构了。然后代码继续执行,在window析构之后,quit也会被析构,因为quit也是⼀个局部变量,在超出作用域的时候当然也需要析构。但是,这时候已经是第⼆次调用quit的析构函数了,C++不允许调用两次析构函数,因此,程序崩溃了
自定义MyPushButton类观察释放过程
mypushbutton.h:
#ifndef MYPUSHBUTTON_H
#define MYPUSHBUTTON_H
#include
#include
class MyPushButton : public QPushButton
{
Q_OBJECT
public:
explicit MyPushButton(QWidget *parent = nullptr);
~MyPushButton();//添加MyPushButton类的析构函数
signals:
};
#endif // MYPUSHBUTTON_H
mypushbutton.cpp:
#include "mypushbutton.h"
MyPushButton::MyPushButton(QWidget *parent) : QPushButton(parent)
{
qDebug() << "我的按钮的构造函数被调用";
}
MyPushButton::~MyPushButton()
{
qDebug() << "我的按钮的析构函数被调用";
}
widget.h:
#include "widget.h"
#include "ui_widget.h"
#include "mypushbutton.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
MyPushButton* button = new MyPushButton(this);
//传入this,将其设置到对象树中,当窗口关闭时就会自动调用其析构函数
button->setText("我的按钮");
}
Widget::~Widget()
{
qDebug() << "Widget的析构函数被调用";
delete ui;
}
对象树确保的是先释放子节点的内存,后释放父节点的内存。而析构函数的调用顺序则不⼀定遵守上述要求,因此看到子节点的析构执行顺序反而在父节点析构顺序之后
注意:调用析构函数和释放内存并非是同一件事情
总结
Qt的对象树机制虽然在⼀定程度上解决了内存问题,但是也引入了⼀些值得注意的事情。这些细节在今后的开发过程中很可能时不时跳出来烦扰一下,所以最好从开始就养成良好习惯
注意:在Qt中,尽量在构造的时候就指定parent对象,并且大胆在堆上创建
坐标体系:以左上角度为原点(0,0),X向右增加,Y向下增加
对于嵌套窗口,其坐标是相对于父窗口而言的
可以使用代码设置控件在坐标系中的位置
#include "widget.h"
#include "ui_widget.h"
#include "mypushbutton.h"
#include
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPushButton* btn1 = new QPushButton("按钮1", this);
btn1->move(200, 300);
QPushButton* btn2 = new QPushButton("按钮2", this);
qDebug() << "按钮1的坐标为:[" << btn1->x() << "," << btn1->y() << "]";
qDebug() << "按钮2的坐标为:[" << btn2->x() << "," << btn2->y() << "]";
}
Widget::~Widget()
{
delete ui;
}
200与300都代表着像素点