目录
Hello Qt 程序实现
使用“按钮”实现
纯代码方式实现:
// Widget构造函数的实现
Widget::Widget(QWidget *parent)
: QWidget(parent) // 使用父类构造函数初始化QWidget,传入父窗口指针
, ui(new Ui::Widget) // 创建Ui::Widget类的实例,并用new动态分配内存
{
// 调用ui的setupUi函数来初始化界面,传入this指针,即将当前Widget实例与设计的UI绑定
ui->setupUi(this);
// 创建一个QPushButton对象指针btn
QPushButton *btn = new QPushButton;
// 设置按钮的文本为"Hello_Qt"
btn->setText("Hello_Qt");
// 设置按钮的父窗口为当前Widget,这样按钮会自动被管理(包括销毁)
btn->setParent(this);
}
// Widget析构函数的实现
Widget::~Widget()
{
// 释放ui指针所指向的内存,防止内存泄漏
delete ui;
}
代码结果:
可视化操作实现:
(1)双击:" widget.ui " ⽂件;
(2)拖拽控件⾄ ui 界⾯窗⼝并修改内容;
(3)构建并运⾏,效果如下所⽰:
使用 "标签" 实现
纯代码方式实现:
#include "widget.h" // 包含自定义Widget类的头文件
#include "ui_widget.h" // 包含由Qt Designer生成的用户界面类头文件
#include <QLabel> // 包含QLabel控件的头文件,用于显示文本
#include <QFont> // 包含QFont类,用于设置字体样式
// Widget类的构造函数
Widget::Widget(QWidget *parent)
: QWidget(parent) // 调用基类QWidget的构造函数,传入父窗口指针
, ui(new Ui::Widget) // 使用new动态创建Ui::Widget实例
{
// 初始化由Qt Designer设计的界面
ui->setupUi(this);
// 创建一个新的QLabel对象,并将其父窗口设为当前Widget
QLabel *lab = new QLabel(this);
// 设置QLabel的显示文本为"Hello Qt"
lab->setText("Hello Qt");
// 设置窗口固定大小为800x600像素
setFixedSize(800, 600);
// 创建QFont对象,设置字体为"华文行楷",字号为64
QFont font("华文行楷", 64);
// 将创建的字体应用到QLabel上
lab->setFont(font);
// 移动QLabel的位置到屏幕上的(0, 300)
lab->move(0, 300);
// 设置QLabel的文字颜色为蓝色
lab->setStyleSheet("color:blue");
}
// Widget类的析构函数
Widget::~Widget()
{
// 清理由new分配的ui对象,防止内存泄漏
delete ui;
}
代码结果:
可视化操作实现:
(1)双击:" widget.ui " ⽂件;
(2)拖拽 "标签" ⾄ UI 设计界⾯中,并双击修改标签内容;
(3)实现效果如下图所⽰:
项目文件解析
在我们创建完一个项目后,Qt Creator 会默认给我们生成以下文件:
解释 .pro
# Qt项目配置文件 (.pro) 示例
# 添加必需的Qt模块:core和gui。core模块包含基础运行时功能,而gui模块用于构建图形用户界面。
QT += core gui
# 如果当前使用的Qt版本主版本号大于4,则额外添加widgets模块,因为从Qt5开始,widgets被分离出来作为一个独立模块。
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
# 配置项目以使用C++11标准。这对于利用现代C++特性如auto、lambda表达式等是必要的。
CONFIG += c++11
# 定义宏QT_DEPRECATED_WARNINGS,使得编译器在遇到Qt中已标记为过时的功能时发出警告。
# 这有助于开发者及时迁移代码,避免使用即将不支持的API。
DEFINES += QT_DEPRECATED_WARNINGS
# 注释掉的行展示了如何禁止使用指定版本之前的所有过时API。
# 解除注释并设置期望的版本(如QT_DISABLE_DEPRECATED_BEFORE=0x060000禁用Qt6.0.0前的过时API),
# 可强制编译失败而不是警告,确保代码完全不含旧API。
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # 禁用所有在Qt 6.0.0之前废弃的API
# 指定源文件,项目将从这些文件编译生成可执行代码。
SOURCES += \
main.cpp \
widget.cpp
# 指定头文件,这些是源文件中可能引用的声明文件。
HEADERS += \
widget.h
# 指定UI表单文件,这些是通过Qt Designer设计的界面文件,将被编译为C++代码。
FORMS += \
widget.ui
# 部署规则设定:
# - 对于QNX系统,目标路径设置为/tmp/${TARGET}/bin
# - 对于其他Unix系统(非Android),目标路径设置为/opt/${TARGET}/bin
# 如果target.path被设置了(即不为空),那么会自动添加一个安装步骤(INSTALLS += target)来安装生成的可执行文件到指定路径。
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
解释widget.h
// widget.h 文件头部使用预处理器指令防止多次包含
#ifndef WIDGET_H
#define WIDGET_H
// 包含基类 QWidget 的头文件,以便继承
#include <QWidget>
// 使用QT_BEGIN_NAMESPACE和QT_END_NAMESPACE包裹Ui命名空间的声明,
// 以符合Qt的编码规范,避免命名冲突。
QT_BEGIN_NAMESPACE
namespace Ui {
// 前向声明Ui::Widget类,该类由uic自动生成并用于界面管理
class Widget;
}
QT_END_NAMESPACE
// 类Widget的声明,它公有继承自QWidget
class Widget : public QWidget
{
// Q_OBJECT 宏是必须的,用于Qt的信号与槽机制以及国际化支持
Q_OBJECT
public:
// 构造函数,接受一个指向父组件的指针,默认为nullptr
explicit Widget(QWidget *parent = nullptr);
// 析构函数,清理资源
~Widget();
private:
// 私有成员变量,指向由Qt设计师生成的界面类实例
Ui::Widget *ui; // 实例化Ui::Widget用于界面控制
};
// 结束条件编译,确保WIDGET_H只被定义一次
#endif // WIDGET_H
在Qt中,如果要使⽤信号与槽(signal 和 slot)的机制 就必须加⼊ Q_OBJECT 宏;
Ui::Widget *ui; 这个指针是⽤前⾯声明的 namespace Ui ⾥的 Widget 类定义的,所以指针 ui 是指向可视化设计的界⾯,后⾯要访问界⾯上的组件,都需要通过这个指针 ui 去访问。
解释 main.cpp
// 包含自定义窗口部件类(Widget)的实现文件
#include "widget.h"
// 引入QApplication类,用于管理整个GUI应用程序的控制流程
#include <QApplication>
// 应用程序的主函数
int main(int argc, char *argv[])
{
// 创建QApplication对象,argc和argv是从命令行传入的参数,
// 这一行是Qt GUI应用的起点,管理事件循环和提供系统级别的功能
QApplication a(argc, argv);
// 实例化自定义窗口部件Widget类
Widget w;
// 调用窗口部件的show()方法,使窗口在屏幕上显示出来
w.show();
// 启动Qt的事件循环,a.exec()会一直运行直到事件循环结束,
// 通常当所有窗口关闭时事件循环结束,然后返回 exitCode 给操作系统
return a.exec();
}
解释widget.cpp
widget.cpp ⽂件是类 Widget 的实现代码,所有在窗体上要实现的功能添加在此⽂件中;
// 包含自定义窗口部件类的头文件
#include "widget.h"
// 包含由Qt Designer生成的用户界面类头文件,用于界面布局和控件
#include "ui_widget.h"
// Widget类构造函数
Widget::Widget(QWidget *parent)
: QWidget(parent) // 使用基类QWidget的构造函数初始化,传入父窗口指针
, ui(new Ui::Widget) // 创建Ui::Widget类的实例,并用new动态分配内存
{
// 调用ui->setupUi(this)来初始化界面,this指针指向当前Widget实例,
// 这一步会根据.ui文件设置窗口的布局和控件
ui->setupUi(this);
}
// Widget类析构函数
Widget::~Widget()
{
// 当Widget实例被销毁时,释放之前动态分配的ui对象的内存
delete ui;
}
解释widget.ui
widget.ui 是窗体界⾯定义⽂件,是⼀个 XML ⽂件,定义了窗⼝上的所有组件的属性设置、布局,及其信号与槽函数的关联等。⽤ UI 设计器可视化设计的界⾯都由 Qt ⾃动解析,并以 XML ⽂件的形式保存下来。在设计界⾯时,只需在 UI 设计器⾥进⾏可视化设计即可,⽽不⽤管 widget.ui ⽂件是怎么⽣成的。
<!-- XML声明部分,定义了文档的版本为1.0,字符编码为UTF-8 -->
<?xml version="1.0" encoding="UTF-8"?>
<!-- 开始定义Qt用户界面文件,版本号为4.0 -->
<ui version="4.0">
<!-- 定义用户界面的主类名为Widget -->
<class>Widget</class>
<!-- 实际的窗口部件配置开始,这是一个QWidget类型的窗口部件 -->
<widget class="QWidget" name="Widget">
<!-- 设置窗口部件的初始几何属性 -->
<property name="geometry">
<!-- 使用矩形(rect)来具体定义位置和大小 -->
<rect>
<x>0</x> <!-- X轴坐标起始于0 -->
<y>0</y> <!-- Y轴坐标起始于0 -->
<width>800</width> <!-- 窗口宽度为800像素 -->
<height>600</height> <!-- 窗口高度为600像素 -->
</rect>
</property>
<!-- 设置窗口的标题 -->
<property name="windowTitle">
<string>Widget</string> <!-- 窗口标题为"Widget" -->
</property>
</widget> <!-- QWidget配置结束 -->
<!-- 资源部分,目前没有定义任何额外资源 -->
<resources/>
<!-- 连接部分,目前没有定义任何信号与槽的连接 -->
<connections/>
</ui> <!-- 用户界面定义结束 -->
Qt 编程注意事项
Qt 中的命名规范
Qt Creator 中的快捷键
使用帮助文档
认识对象模型(对象树)
在 Qt 中创建很多对象的时候会提供⼀个 Parent 对象指针,下⾯来解释这个 parent 到底是⼲什么的。
这种机制在 GUI 程序设计中相当有⽤。例如,⼀个按钮有⼀个 QShortcut(快捷键)对象作为其⼦对象。当删除按钮的时候,这个快捷键理应被删除。这是合理的。
如果 QObject 在栈上创建,Qt 保持同样的⾏为。正常情况下,这也不会发⽣什么问题。来看下⾯的代码⽚段:
作为⽗组件的 window 和作为⼦组件的 quit 都是 QObject 的⼦类(事实上,它们都是QWidget的⼦类,⽽QWidget 是 QObject 的⼦类)。这段代码是正确的,quit 的析构函数不会被调⽤两次,因为标准 C++ 要求,局部对象的析构顺序应该按照其创建顺序的相反过程。因此,这段代码在超出作⽤域时,会先调⽤ quit 的析构函数,将其从⽗对象 window 的⼦对象列表中删除,然后才会再调⽤window 的析构函数。
但是,如果我们使⽤下⾯的代码:
情况⼜有所不同,析构顺序就有了问题。我们看到,在上⾯的代码中,作为⽗对象的 window 会⾸先被析构,因为它是最后⼀个创建的对象。在析构过程中,它会调⽤⼦对象列表中每⼀个对象的析构函数,也就是说, quit 此时就被析构了。然后,代码继续执⾏,在 window 析构之后,quit 也会被析构,因为 quit 也是⼀个局部变量,在超出作⽤域的时候当然也需要析构。但是,这时候已经是第⼆次调⽤ quit 的析构函数了,C++ 不允许调⽤两次析构函数,因此,程序崩溃了。
由此我们看到,Qt 的对象树机制虽然在⼀定程度上解决了内存问题,但是也引⼊了⼀些值得注意的事情。这些细节在今后的开发过程中很可能时不时跳出来烦扰⼀下,所以,我们最好从开始就养成良好习惯。
Qt对象树如图:
Qt 窗口坐标体系
坐标体系:以左上⻆为原点(0,0),X向右增加,Y向下增加。
对于嵌套窗⼝,其坐标是相对于⽗窗⼝来说的。
⽰例:使⽤Qt中的坐标系设置控件的位置;
代码结果: