news 2026/9/30 9:18:38

Qt 5.9 入门实战:完整解析一个 QWidget 项目的文件结构与核心语法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Qt 5.9 入门实战:完整解析一个 QWidget 项目的文件结构与核心语法

一、项目概述

本文以 Qt Creator 创建的一个标准 Qt Widgets 工程为例,分析以下几个部分:

  • Qt 工程文件.pro的作用与语法
  • main.cpp、widget.h、widget.cpp之间的关系
  • .ui界面文件的 XML 结构
  • Q_OBJECT、setupUi()、MOC、UIC 等 Qt 核心机制
  • 当前项目可能存在的问题与优化方向

项目目录结构如下:

untitled/ ├── untitled.pro ├── untitled.pro.user ├── main.cpp ├── widget.h ├── widget.cpp └── widget.ui

编译之后,在独立的构建目录中还会生成:

build-untitled-Desktop_Qt_5_9_0_MinGW_32bit-Debug/ ├── Makefile ├── ui_widget.h ├── debug/ │ ├── moc_widget.cpp │ ├── main.o │ ├── widget.o │ └── untitled.exe

需要特别注意:

  • ui_widget.h是由 UIC 根据widget.ui自动生成的
  • moc_widget.cpp是由 MOC 根据Q_OBJECT自动生成的
  • 这两个文件不能手动修改,否则重新编译后内容会被覆盖

二、untitled.pro:Qt 工程配置文件

untitled.pro是 qmake 工程文件,相当于整个项目的“构建说明书”。

主要内容如下:

QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = untitled TEMPLATE = app DEFINES += QT_DEPRECATED_WARNINGS SOURCES += \ main.cpp \ widget.cpp HEADERS += \ widget.h FORMS += \ widget.ui

1.QT += core gui

表示项目需要链接 Qt Core 和 Qt GUI 模块。

  • QtCore:提供字符串、容器、文件、时间和对象机制
  • QtGui:提供字体、颜色、绘图等基础 GUI 能力
  • QtWidgets:提供按钮、标签、输入框等桌面控件

2.greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

这是 qmake 的条件语法:

条件: 满足条件时执行的语句

它的含义是:

如果 Qt 主版本号大于 4,就额外加入widgets模块。

因为在 Qt 5 中,QWidget、QPushButton、QLabel等控件已经从QtGui分离到了独立的QtWidgets模块。

如果是 Qt 4,只需要:

QT += core gui

如果是 Qt 5 或 Qt 6,则需要:

QT += core gui widgets

3.TARGET = untitled

指定最终生成的可执行程序名称:

untitled.exe

4.TEMPLATE = app

表示当前工程类型是应用程序。

常见类型包括:

  • app:桌面应用程序
  • lib:动态库或静态库
  • subdirs:包含多个子工程

5.DEFINES += QT_DEPRECATED_WARNINGS

定义一个编译宏,让编译器在使用 Qt 已废弃 API 时给出警告。

这有利于后续从 Qt 5 升级到 Qt 6。

6.SOURCES、HEADERS、FORMS

分别声明:

SOURCES += main.cpp widget.cpp HEADERS += widget.h FORMS += widget.ui
  • SOURCES:C++ 源文件
  • HEADERS:头文件
  • FORMS:Qt Designer 界面文件

其中反斜杠\表示下一行仍然属于当前语句。


三、untitled.pro.user:Qt Creator 的本地配置文件

untitled.pro.user不是项目源代码,而是 Qt Creator 自动生成的用户配置文件。

其中保存了:

  • 使用的 Qt 版本
  • 编译套件
  • Debug、Release、Profile 构建目录
  • 运行配置
  • 调试器配置
  • 环境变量配置

当前工程使用的构建套件是:

Desktop Qt 5.9.0 MinGW 32bit

对应的 Debug 构建目录是:

C:/Users/dell/Documents/build-untitled-Desktop_Qt_5_9_0_MinGW_32bit-Debug

建议不要手动编辑.pro.user。

如果使用 Git 管理项目,通常应该将其加入.gitignore:

*.pro.user *.pro.user.*

因为它只适用于当前电脑和当前 Qt Creator 配置,不适合提交到代码仓库。


四、main.cpp:程序入口

完整代码如下:

#include "widget.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); Widget w; w.show(); return a.exec(); }

1. 引入头文件

#include "widget.h" #include <QApplication>
  • 引号形式"widget.h":通常用于当前项目中的自定义头文件
  • 尖括号形式<QApplication>:通常用于标准库或 Qt 官方库头文件

2. 创建 Qt 应用程序对象

QApplication a(argc, argv);

QApplication是 Qt Widgets 程序的应用程序对象,负责:

  • 管理 GUI 程序生命周期
  • 处理窗口系统事件
  • 分发鼠标、键盘和窗口事件
  • 管理应用程序级设置

对于有界面的 Qt Widgets 程序,main()中必须创建一个QApplication对象。

3. 创建主窗口控件

Widget w;

这里的Widget是自定义类,继承自QWidget。

对象采用栈内存创建,因此程序结束时会自动析构,不需要手动delete。

4. 显示窗口

w.show();

调用show()后,窗口才会真正显示。

5. 进入事件循环

return a.exec();

exec()会进入 Qt 事件循环,程序会持续等待并处理用户操作,例如:

  • 点击按钮
  • 输入文字
  • 关闭窗口
  • 定时器触发

如果没有调用exec(),窗口可能会一闪而过,程序也不会持续响应用户操作。


五、widget.h:Widget 类声明

完整代码如下:

#ifndef WIDGET_H #define WIDGET_H #include <QWidget> namespace Ui { class Widget; } class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = 0); ~Widget(); private: Ui::Widget *ui; }; #endif // WIDGET_H

1. 头文件保护

#ifndef WIDGET_H #define WIDGET_H // 头文件内容 #endif

这是传统的 Include Guard,用于防止同一个头文件被重复包含,避免重复定义错误。

2. 前置声明

namespace Ui { class Widget; }

这里只是告诉编译器:

Ui命名空间中存在一个名为Widget的类。

具体定义不在这里展开,从而减少头文件之间的依赖。

真正的Ui::Widget定义由 UIC 自动生成在ui_widget.h中。

3. 继承 QWidget

class Widget : public QWidget

表示Widget是一个 Qt 窗口部件,具有窗口、布局、事件和控件管理能力。

类似的常见基类包括:

  • QWidget:通用控件或自定义窗口
  • QMainWindow:带菜单栏、工具栏、状态栏的主窗口
  • QDialog:对话框

4.Q_OBJECT宏

Q_OBJECT

这是 Qt 元对象系统的核心宏。

它让类可以使用:

  • 信号与槽
  • tr()国际化
  • 运行时类型信息
  • 动态属性

只要类中写了Q_OBJECT,qmake 就会调用 MOC,自动生成类似下面的文件:

moc_widget.cpp

如果删除这个宏,代码可能仍然能编译,但是信号、槽等 Qt 特性会失效。

5. 构造函数

explicit Widget(QWidget *parent = 0);
  • explicit防止构造函数被隐式类型转换调用
  • parent表示父控件
  • 传入父控件后,子控件会自动加入父子对象树,由父对象管理生命周期

当前代码使用0表示空指针。在现代 C++ 中也可以写成:

explicit Widget(QWidget *parent = nullptr);

6. UI 指针

private: Ui::Widget *ui;

ui是指向自动生成界面类的指针。

通过它,可以访问.ui文件中定义的控件,例如:

ui->cmdLineEdit ui->cancelButton ui->browseButton

六、widget.cpp:界面初始化

完整代码如下:

#include "widget.h" #include "ui_widget.h" Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); } Widget::~Widget() { delete ui; }

1. 引入自动生成的 UI 头文件

#include "ui_widget.h"

ui_widget.h是由 UIC 根据widget.ui生成的。

它里面会定义:

class Ui_Widget { public: QLabel *label; QLabel *label_2; QLineEdit *cmdLineEdit; QPushButton *pushButtocommitButtonn; QPushButton *cancelButton; QPushButton *browseButton; void setupUi(QWidget *Widget); void retranslateUi(QWidget *Widget); };

然后通过命名空间包装:

namespace Ui { class Widget : public Ui_Widget {}; }

因此项目代码中可以使用Ui::Widget。

2. 初始化列表

Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget)

冒号后面的部分称为初始化列表。

它完成了两件事:

  1. 调用父类QWidget的构造函数
  2. 使用new创建 UI 对象

这比在构造函数函数体内赋值更加规范,也符合 C++ 对象初始化顺序。

3. 构建界面

ui->setupUi(this);

setupUi()是 UIC 自动生成的核心函数,它的主要作用是:

  • 创建.ui中定义的控件
  • 设置控件位置和大小
  • 设置字体、标题和文字
  • 设置布局
  • 把控件加入父对象

例如,widget.ui中的:

<widget class="QLineEdit" name="cmdLineEdit">

最终会在setupUi()中生成类似代码:

cmdLineEdit = new QLineEdit(Widget); cmdLineEdit->setObjectName(QStringLiteral("cmdLineEdit")); cmdLineEdit->setGeometry(QRect(130, 100, 113, 21));

4. 析构函数

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

程序销毁Widget对象时,需要释放动态创建的ui对象。

不过,界面中的控件一般会由 Qt 父子对象机制自动释放,不需要逐个delete。


七、widget.ui:XML 界面文件

widget.ui是 Qt Designer 使用的 XML 文件。

它的核心结构可以简化为:

<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>Widget</class> <widget class="QWidget" name="Widget"> <!-- 属性 --> <!-- 子控件 --> </widget> </ui>

1. XML 声明

<?xml version="1.0" encoding="UTF-8"?>

表示当前文件使用 XML 1.0 格式,编码是 UTF-8。

因此中文可以正常保存,例如:

<string>确定</string>

2. 根节点

<ui version="4.0">

表示这是 Qt Designer UI 文件,格式版本为 4.0。

3. 界面类名

<class>Widget</class>

对应 C++ 中的 XML 类名。

4. 根控件

<widget class="QWidget" name="Widget">

表示根控件类型为QWidget,对象名为Widget。

5. 窗口尺寸与标题

<property name="geometry"> <rect> <x>0</x> <y>0</y> <width>400</width> <height>300</height> </rect> </property> <property name="windowTitle"> <string>Widget</string> </property>

表示窗口初始大小为 400 × 300,标题为Widget。

6. 当前项目中的控件

控件类型对象名作用
QLabellabel显示“请输入命令”
QLabellabel_2显示“打开:”
QLineEditcmdLineEdit输入命令或路径
QWidget未命名按钮区域容器
QPushButtonpushButtocommitButtonn确定按钮
QPushButtoncancelButton取消按钮
QPushButtonbrowseButton浏览按钮
QHBoxLayouthorizontalLayout水平排列三个按钮

7. QLabel 中的 HTML 文本

例如:

<string>&lt;html&gt;&lt;body&gt;请输入命令&lt;/body&gt;&lt;/html&gt;</string>

XML 中的:

&lt; 表示 < &gt; 表示 > &quot; 表示 "

Qt 的QLabel支持富文本,因此可以直接使用简单 HTML 设置居中、字号和颜色。

8. 按钮布局

<layout class="QHBoxLayout" name="horizontalLayout"> <item> <widget class="QPushButton" name="pushButtocommitButtonn"> <property name="text"> <string>确定</string> </property> </widget> </item> </layout>

QHBoxLayout表示水平布局,三个按钮会从左到右排列。


八、UIC 与 MOC 的工作过程

这个项目涉及两个非常重要的 Qt 自动生成工具。

1. UIC:处理.ui文件

widget.ui ↓ UIC ui_widget.h

UIC 是 Qt User Interface Compiler。

它把 XML 界面文件转换成 C++ 代码。

项目中的:

#include "ui_widget.h"

使用的就是生成后的文件。

2. MOC:处理Q_OBJECT

widget.h ↓ MOC moc_widget.cpp

MOC 是 Meta-Object Compiler。

由于Widget中包含:

Q_OBJECT

MOC 会生成与信号、槽和元对象相关的 C++ 代码。

3. 完整构建流程

untitled.pro ↓ qmake Makefile ↓ UIC + MOC + C++ Compiler ↓ 目标文件 ↓ 链接 Qt 库 ↓ untitled.exe

九、当前项目存在的问题

1. 按钮没有绑定任何功能

目前widget.ui中:

<connections/>

是空的,C++ 代码中也没有调用connect()。

因此点击“确定”“取消”“浏览”暂时不会产生任何效果。

可以通过信号与槽添加功能,例如:

connect(ui->browseButton, &QPushButton::clicked, this, [this]() { QString path = QFileDialog::getOpenFileName(this, "选择文件"); if (!path.isEmpty()) { ui->cmdLineEdit->setText(path); } });

需要额外引入:

#include <QFileDialog>

2. 按钮对象名存在疑似拼写错误

当前对象名是:

pushButtocommitButtonn

其中Butto和末尾的Buttonn都可能属于拼写问题。

更规范的命名建议是:

confirmButton

如果修改对象名,需要重新生成ui_widget.h,同时修改所有引用该控件的 C++ 代码。

3. 大量使用绝对坐标

当前界面主要通过geometry设置位置和大小:

<property name="geometry"> <rect> <x>110</x> <y>40</y> <width>161</width> <height>41</height> </rect> </property>

这种方式适合简单练习,但窗口缩放后布局不容易自适应。

更推荐使用:

  • QVBoxLayout
  • QHBoxLayout
  • QGridLayout
  • QFormLayout

例如,可以把“请输入命令”、输入框和按钮区域组合成垂直布局。

4..pro.user不应提交到 Git

建议在.gitignore中加入:

*.pro.user *.pro.user.*

源码目录保留.pro、.cpp、.h和.ui即可。


十、Qt 核心语法总结

语法含义
QT += core gui widgets引入 Qt 模块
TARGET = untitled指定生成程序名称
TEMPLATE = app指定工程类型
SOURCES +=添加源文件
HEADERS +=添加头文件
FORMS +=添加.ui界面文件
class Widget : public QWidget继承 Qt 控件类
Q_OBJECT启用 Qt 元对象系统
Ui::Widget *ui指向自动生成的界面类
ui->setupUi(this)根据.ui构建界面
new Ui::Widget创建界面对象
delete ui释放界面对象
QApplication管理整个 GUI 程序
a.exec()启动事件循环
show()显示窗口
connect()连接信号与槽

十一、总结

这个untitled工程虽然代码量不大,但它已经包含了 Qt Widgets 项目最基础的完整结构:

.pro 负责构建配置 main.cpp 负责程序启动 widget.h 负责类声明 widget.cpp 负责界面初始化 widget.ui 负责可视化界面描述 MOC 负责元对象代码 UIC 负责界面代码

理解它们之间的关系后,就可以继续扩展:

  1. 为按钮添加信号与槽
  2. 使用QFileDialog实现文件选择
  3. 使用布局替代绝对坐标
  4. 增加业务逻辑类
  5. 使用QProcess执行外部命令
  6. 使用.qrc管理图片和样式资源
  7. 使用QSettings保存配置
  8. 将功能拆分为多个模块和子工程

这个项目可以作为一个 Qt Widgets 入门模板,用来继续学习 Qt 的对象树、事件循环、信号与槽、布局系统和 qmake 构建机制。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/30 9:18:19

爆单不爆仓:丽迅物流如何帮鞋服品牌打赢秋冬旺季战

每年秋冬&#xff0c;鞋服行业就进入了一年中最紧张的战役期。中秋、国庆门店补货高峰先行启动&#xff0c;紧接着双 11 订单洪峰来袭&#xff0c;双 12 接踵而至&#xff1b;寒潮突袭&#xff0c;羽绒服和靴子需求瞬间暴涨&#xff1b;之后迎来元旦节前备货&#xff0c;再衔接…

作者头像 李华
网站建设 2026/9/30 9:17:51

RH134前半程核心解析:从命令行效率到SSH免密的自动化运维体系

我其实挺少看到有人认认真真坐下来把 RH134 前半程梳理成体系的。大部分备考的朋友要么抱着题海猛刷&#xff0c;要么把课程从头到尾点一遍&#xff0c;结果考完没几天就忘了。RH134 这门课&#xff0c;红帽给出的全称是 Red Hat System Administration II&#xff0c;对应的是…

作者头像 李华
网站建设 2026/9/30 9:16:46

爬虫入门实战:用requests抓取2345天气网城市天气数据

兄弟&#xff0c;你要是正准备入坑爬虫&#xff0c;千万别一上来就死磕那些复杂的电商反爬、登录验证。我跟你说&#xff0c;天气数据采集是练手性价比最高的项目&#xff1a;接口不用登录、数据结构规整、信息量适中&#xff0c;而且不管你是想做数据分析、搭个可视化大屏&…

作者头像 李华
网站建设 2026/9/30 9:16:17

Python实验一入门指南:从环境配置到基础语法与调试

很多年没摸过Python的人&#xff0c;第一次接触编程语言&#xff0c;往往不是被语法难倒&#xff0c;而是被一堆零碎的环境问题耗光了耐心。我每学期带实验一的时候&#xff0c;都有学生卡在Python装不上、编辑器打不开、跑出乱码之类的事情上。其实对一门语言的第一印象&#…

作者头像 李华
网站建设 2026/9/30 9:16:04

国内服务器备案绕不开:合规上线与时间同步运维基线

上周凌晨一点多&#xff0c;一个做独立开发的朋友给我发消息&#xff1a;他部署在国内服务器上的站点又打不开了&#xff0c;浏览器里挂着一个拦截提示页&#xff0c;问我有没有什么"不看备案"的路子。这已经不是第一次有人问我这个问题了&#xff0c;而且问的人里既…

作者头像 李华