Qt Widgets 架构与开发入门
前言
面向 C++ 初学者的 Qt Widgets 学习指南。示例基于 Qt 6,大部分 API 在 Qt 5.15 也可用,差异之处会额外标注。
第 1 章 认识 Qt
1.1 Qt 是什么
Qt 是一个成熟的 跨平台 C++ 应用框架,一次编写、多平台编译运行(Windows、macOS、Linux、Android、iOS、嵌入式)。它不仅提供 GUI 组件,还包含网络、数据库、多媒体、并发、序列化等一整套基础设施,可以理解为「C++ 版的标准 SDK」。
Qt 的两大 UI 技术栈:
- Qt Widgets:传统的桌面控件体系,基于 C++ 类继承,像
QPushButton、QTableView。适合复杂的、以数据展示为主的桌面应用。 - Qt Quick / QML:声明式 UI + JavaScript,适合动画丰富、触控友好的现代界面。
本文档只讲 Qt Widgets。
1.2 Qt Widgets vs Qt Quick
| 维度 | Qt Widgets | Qt Quick |
|---|---|---|
| 定位 | 传统桌面 | 现代 UI、移动、嵌入式 |
| 语言 | C++ | QML(声明式) + JS + C++ |
| 渲染 | CPU 光栅化 | GPU 加速 |
| 动画 | 一般 | 出色 |
| 数据密集型表格 | 强 | 弱 |
| 学习曲线 | 平缓(接近传统 GUI) | 需要额外学 QML |
经验法则:数据密集、以表单/表格为主的工具型软件选 Widgets;强调视觉、动画、跨触屏的产品选 Quick。
1.3 Qt 模块划分
Qt 是模块化设计,常用模块:
- Qt Core:非 GUI 基础设施,
QObject、容器、字符串、文件、时间、事件循环。 - Qt Gui:窗口系统抽象、事件、绘图底层(
QPainter、QImage)。 - Qt Widgets:传统控件、布局、Model/View。
- Qt Network:TCP/UDP、HTTP、SSL。
- Qt Sql:数据库访问。
- Qt Concurrent:高层并发工具。
- Qt Test:单元测试。
在 CMake 中通过 find_package(Qt6 REQUIRED COMPONENTS Widgets) 按需引入。
1.4 版本与许可
- Qt 目前主流版本是 Qt 6(推荐新项目)与 Qt 5.15 LTS(存量项目)。
- 许可有两种:LGPLv3(开源,可动态链接商用,但需遵守 LGPL 义务)与 商业许可(适合静态链接或需规避 LGPL 的产品)。
- 本文示例默认 Qt 6.5+。
第 2 章 开发环境搭建
2.1 安装 Qt
推荐 Qt Online Installer(需注册 Qt 账号):
- 从 qt.io/download 下载安装器。
- 选择组件时至少勾选:
- 一个 Qt 版本(如 6.7.x)
- 对应平台的编译器套件(Windows 建议 MSVC 2019/2022 64-bit,或 MinGW)
- Qt Creator(IDE)
- CMake、Ninja(通常自动带上)
- 磁盘至少预留 20 GB。
2.2 Qt Creator 界面速览
- 欢迎(Welcome):项目模板、示例、教程入口。
- 编辑(Edit):代码编辑器。
- 设计(Design):打开
.ui文件时的可视化设计器。 - 调试(Debug):断点、变量查看、GDB/LLDB/CDB 集成。
- 项目(Projects):配置构建套件(Kit)、编译选项、运行环境。
第一次打开时进入 Preferences → Kits,确认至少有一个绿色勾的 Kit(Qt 版本 + 编译器 + 调试器齐全)。
2.3 编译工具链
- Windows:MSVC(与 Visual Studio 一致的 ABI,推荐用于发布产品)或 MinGW-w64(免装 VS,适合个人学习)。
- macOS:自带 Clang。
- Linux:发行版自带 GCC/Clang。
⚠️ Qt 库和你的应用必须使用 相同编译器/ABI 构建,否则会出现符号找不到、STL 不兼容等错误。
2.4 第一个项目:Hello, Qt Widgets
在 Qt Creator 中:File → New Project → Application (Qt) → Qt Widgets Application,选中 CMake 构建。
生成的 main.cpp 类似:
#include <QApplication>
#include <QLabel>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QLabel label("Hello, Qt Widgets");
label.resize(300, 100);
label.show();
return app.exec();
}
关键三步:
- 创建
QApplication—— 每个 GUI 程序有且只有一个。 - 创建并
show()一个顶层 widget。 - 调用
app.exec()进入事件循环。
2.5 qmake 与 CMake
Qt 历史上用 qmake(.pro 文件),Qt 6 起 官方推荐 CMake。最小 CMake 示例:
cmake_minimum_required(VERSION 3.16)
project(HelloQt LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_AUTOMOC ON) # 自动运行 MOC
set(CMAKE_AUTOUIC ON) # 自动处理 .ui
set(CMAKE_AUTORCC ON) # 自动处理 .qrc
find_package(Qt6 REQUIRED COMPONENTS Widgets)
add_executable(HelloQt main.cpp)
target_link_libraries(HelloQt PRIVATE Qt6::Widgets)
三个 AUTO* 开关帮你省去手动调用 moc/uic/rcc 的麻烦,新项目建议一律打开。
第 3 章 核心机制
Qt Widgets 的所有 API 都建立在几个核心机制之上。理解它们比记 API 更重要。
3.1 对象树与内存管理
Qt 中所有继承自 QObject 的对象可以构成 父子树。规则:
- 构造时传入父对象:
new QPushButton(&parentWidget); - 父对象析构时,自动
delete所有子对象。 - 因此 有父对象的 QObject 无需手动 delete,也不应放进
std::unique_ptr。
auto *window = new QWidget; // 顶层,无父对象
auto *btn = new QPushButton("OK", window); // 挂到 window 下
window->show();
// 程序退出、window 被 delete 时,btn 自动 delete
常见陷阱:栈上创建的 QObject 又设置了父对象,会 double free。
3.2 元对象系统(MOC)
Qt 用 MOC(Meta-Object Compiler) 给 C++ 加了三样东西:
- 运行时反射(
className()、property()) - 信号槽机制
- 动态
qobject_cast
要让一个类支持这些能力:
class MyWidget : public QWidget
{
Q_OBJECT // 必写!否则信号槽用不了
public:
explicit MyWidget(QWidget *parent = nullptr);
};
MOC 会在编译期扫描带 Q_OBJECT 的头文件,生成 moc_*.cpp 文件。用 CMake AUTOMOC ON 时全自动。
3.3 信号与槽
信号槽是 Qt 的招牌。它是一种类型安全的观察者模式:某个对象发出信号(signal),连接到它的槽函数(slot)会被调用。
class Counter : public QObject
{
Q_OBJECT
public:
int value() const { return m_value; }
public slots:
void increment() {
++m_value;
emit valueChanged(m_value);
}
signals:
void valueChanged(int newValue);
private:
int m_value = 0;
};
连接方式(推荐现代语法):
Counter a, b;
QObject::connect(&a, &Counter::valueChanged,
&b, &Counter::increment);
// 也可以连到 lambda
QObject::connect(&a, &Counter::valueChanged, [](int v) {
qDebug() << "value =" << v;
});
旧语法(字符串宏,不推荐):
connect(&a, SIGNAL(valueChanged(int)), &b, SLOT(increment()));
旧语法只在运行时检查,现代语法编译期检查,IDE 还能跳转和重构。
连接类型(Qt::ConnectionType):
Qt::AutoConnection(默认):同线程 = 直连;跨线程 = 队列连接。Qt::DirectConnection:立即调用槽(在发送者线程)。Qt::QueuedConnection:投递到接收者线程的事件队列,异步执行。Qt::BlockingQueuedConnection:跨线程且阻塞等待返回(小心死锁)。
3.4 事件循环
QApplication::exec() 启动的就是主事件循环。它做三件事:
- 从操作系统收事件(鼠标、键盘、绘制、定时器)。
- 分发给对应的
QObject(通过event()方法)。 - 处理跨线程投递的槽调用(队列连接)。
主线程绝不能阻塞事件循环,否则界面会假死。耗时任务应放到线程或 QtConcurrent(第 9 章)。
QCoreApplication::processEvents() 可以在长任务中手动泵一次事件,让 UI 响应,但通常是设计不佳的信号,慎用。
3.5 属性系统
Q_PROPERTY 声明一个可反射、可绑定到 QSS/动画的属性:
class Star : public QWidget
{
Q_OBJECT
Q_PROPERTY(int rating READ rating WRITE setRating NOTIFY ratingChanged)
public:
int rating() const { return m_rating; }
void setRating(int r) {
if (r == m_rating) return;
m_rating = r;
emit ratingChanged(r);
}
signals:
void ratingChanged(int);
private:
int m_rating = 0;
};
好处:
- 可以被
QPropertyAnimation直接动画化。 - 可以在 QSS 里通过属性选择器匹配。
- 支持
setProperty("rating", 5)动态设置。

1323

被折叠的 条评论
为什么被折叠?



