C++ QT开发
C++ Qt 开发完全指南
文档版本:v1.0 | 更新日期:2026-06-05
Qt 版本:Qt 6.x(LTS 长期支持版)| C++ 标准:C++17 / C++20
本文档涵盖 Qt 开发全流程:环境搭建、核心概念、常用技术栈、代码范式、优质项目推荐,以及详细的注解说明。
目录
1. Qt 开发概述
1.1 Qt 是什么
Qt(读作 “cute”,官方发音)是由 Qt Company(原 Nokia 公司,后被 Digia、Qt Group 收购)开发的跨平台 C++ 应用程序开发框架。官方文档地址:https://doc.qt.io/qt6/
Qt 的核心优势:
| 特性 | 说明 |
|---|---|
| 跨平台 | 一套代码,编译运行于 Windows、macOS、Linux、Android、iOS 等 |
| 生态丰富 | GUI、网络、数据库、XML/JSON、音视频、3D 等模块开箱即用 |
| 信号与槽 | 独特的对象间通信机制,解耦组件 |
| 官方 IDE | Qt Creator 提供项目管理、代码编辑、UI 设计、调试一体化支持 |
| 商业友好 | GPL/LGPL/Commercial 三种许可,商业使用无传染性(LGPL 动态链接) |
1.2 Qt 6 vs Qt 5 — 选择哪个版本
强烈推荐使用 Qt 6。Qt 5 已于 2023年12月终止标准支持(有些模块仍在扩展支持中)。Qt 6 在性能、C++ 标准支持、模块化上均有重大提升。
- Qt 6.5 / 6.6 / 6.7:当前主流 LTS 系列,推荐使用 Qt 6.7 LTS
- Qt 6 废弃了很多 Qt 5 API:如
QRegExp(改为QRegularExpression)、部分字符串 API 变更 - Qt 6 全面支持 C++17/20:大量使用
QVariant减少,模板元编程更强大 - Qt 6 新增模块:
Qt Graphs(可视化图表)、Qt Quick 3D(3D 渲染)、Qt ShaderTools
2. 开发环境搭建
2.1 安装 Qt
推荐方式:通过 Qt 官方在线安装器 安装。
安装步骤:
- 注册 Qt 账号(免费)
- 运行安装器,选择组件时注意:
- Qt 6.7.x LTS → Windows MSVC 2019/2022 64-bit(Windows 平台必选)
- Qt Creator(IDE,独立勾选)
- Qt 6.x.x Win64(MinGW 编译器可选,建议同时装 MSVC 版本,兼容性更好)
- Qt 6.x.x Android(如需安卓开发)
- Qt 6.x.x Sources(Qt 源码,调试时需要)
- 验证安装:打开 Qt Creator → 帮助 → 关于插件,确认 Qt 版本
注解:MSVC 编译器由 Visual Studio 提供,建议安装 Visual Studio 2022 Community(免费)。MinGW 是 GCC 的 Windows 移植版,两者均可使用 Qt,混用可能引发 ABI 不兼容问题,推荐始终使用同一套编译器。
2.2 编译器配置
Qt Creator 中配置编译器:
工具 → 选项 → Kits(Kits 配置页面)
→ 编译器(Compilers)
→ C++: MSVC 2022 或 MinGW 13.x
→ C: 同上
→ Qt 版本(Qt Versions)
→ 添加 qmake.exe 路径(如 C:\Qt\6.7.3\msvc2019_64\bin\qmake.exe)
→ Kits
→ 选择默认 Kit,确保编译器与 Qt 版本匹配
2.3 CMake vs qmake — 选择构建系统
Qt 6 主推 CMake 作为构建系统,qmake 仍支持但逐步废弃。
| 对比项 | qmake | CMake |
|---|---|---|
| 学习曲线 | 低,Qt 专属语法 | 陡峭,但更通用 |
| Qt 6 官方支持 | 降级支持 | 首选推荐 |
| 多平台 | 一般 | 优秀 |
| 大型项目 | 中小项目够用 | 强大 |
| IDE 兼容性 | Qt Creator 深度集成 | VS Code / CLion / Qt Creator 均支持 |
注解:新项目建议使用 CMake,老项目迁移成本高可继续用 qmake。本文档示例以 CMake 为主。
2.4 基础开发工具链
C++ 编译器 → MSVC 19.x(Visual Studio 2022)或 MinGW-w64(GCC 13+)
构建系统 → CMake 3.24+ 或 qmake
调试器 → CDB(MSVC 自带)或 GDB(MinGW)
版本控制 → Git(配合 .gitignore)
3. Qt 项目工程结构
3.1 推荐的 Qt CMake 项目结构
MyProject/ # 项目根目录
├── CMakeLists.txt # 根级构建配置
├── .gitignore
├── README.md
├── docs/ # 文档目录
├── src/ # 源代码目录
│ ├── main.cpp # 程序入口
│ ├── CMakeLists.txt # src 子目录构建配置
│ ├── MyApp/ # 主应用程序模块
│ │ ├── MyApp.pro # (如果用 qmake 则需要 .pro 文件)
│ │ ├── mainwindow.h
│ │ ├── mainwindow.cpp
│ │ ├── mainwindow.ui # Qt Designer 生成的 UI 文件
│ │ └── MyApp.rc # Windows 资源文件(图标等)
│ └── Core/ # 核心业务逻辑模块
│ ├── CMakeLists.txt
│ ├── datamanager.h
│ └── datamanager.cpp
├── resources/ # 资源文件(图片、音视频等)
│ ├── icons/
│ └── images/
├── translations/ # 国际化翻译文件
│ ├── myapp_zh_CN.ts
│ └── myapp_en.ts
└── tests/ # 单元测试目录
└── TestCore/
注解:将 UI 文件(
.ui)与业务逻辑(.h/.cpp)分离是 Qt 的标准做法。.ui文件由 Qt Designer 可视化编辑,编译时由uic工具自动生成对应 C++ 代码。
3.2 CMakeLists.txt 示例(Qt 6 CMake 最小配置)
cmake_minimum_required(VERSION 3.24)
project(MyProject VERSION 1.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 启用 Qt 自动化模块(处理 UI、MOC、RCC 等)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
# 查找 Qt 组件(只引入需要的模块,减小依赖)
find_package(Qt6 REQUIRED COMPONENTS
Core
Gui
Widgets
Network
Sql
)
# 可执行文件
add_executable(myapp
src/main.cpp
src/mainwindow.cpp
src/mainwindow.h
src/mainwindow.ui
resources/icons/app.icns
)
# 链接 Qt 库
target_link_libraries(myapp PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Widgets
Qt6::Network
Qt6::Sql
)
# Windows 特定配置
if(WIN32)
set_target_properties(myapp PROPERTIES
WIN32_EXECUTABLE ON # 以 GUI 程序运行,不弹出控制台
)
endif()
注解:
CMAKE_AUTOMOC ON是 Qt6 CMake 的关键配置,它自动运行 Qt 的 MOC(元对象编译器),无需手动为每个含 Q_OBJECT 的类调用qt6_wrap_cpp。CMAKE_AUTORCC ON处理资源文件,CMAKE_AUTOUIC ON处理 UI 文件。
4. 核心模块与常用技术
4.1 模块总览
Qt6 按功能分为多个模块,以下是开发中最常用的模块:
| 模块 | 头文件 | 用途 |
|---|---|---|
| Qt Core | <QCoreApplication>, <QObject>, <QDebug> |
核心非 GUI 类:事件循环、对象模型、容器 |
| Qt Widgets | <QMainWindow>, <QPushButton>, <QLayout> |
传统桌面 GUI 组件 |
| Qt Gui | <QGuiApplication>, <QPainter>, <QFont> |
图形相关底层 API |
| Qt Quick / QML | <QQmlApplicationEngine>, <QQuickView> |
声明式 UI(适合移动/嵌入式) |
| Qt Network | <QTcpSocket>, <QNetworkAccessManager> |
TCP/UDP/HTTP/FTP |
| Qt Sql | <QSqlDatabase>, <QSqlQuery> |
数据库支持(MySQL/SQLite/PostgreSQL/ODBC) |
| Qt Multimedia | <QMediaPlayer>, <QAudioOutput> |
音视频播放与录制 |
| Qt Charts | <QChartView>, <QBarSeries> |
2D 图表(折线/柱状/饼图等) |
| Qt SVG | <QSvgGenerator>, <QSvgWidget> |
SVG 矢量图形 |
| Qt SerialBus | <QModbusRtuSerialMaster> |
工业通信:Modbus RTU/TCP |
| Qt SerialPort | <QSerialPort> |
串口通信(RS232/485) |
| Qt StateMachine | <QStateMachine>, <QState> |
状态机框架 |
| Qt Test | <QTest> |
单元测试框架 |
| Qt OPC UA | <QOpcUaClient> |
工业 4.0 OPC UA 协议 |
| Qt 3D | <Qt3DRender> |
3D 渲染与建模 |
| Qt WebSockets | <QWebSocket> |
WebSocket 通信 |
| Qt JSON | <QJsonDocument>, <QJsonObject> |
JSON 解析与序列化 |
4.2 Qt Core — 信号与槽(Signal & Slot)
信号与槽是 Qt 独有的对象间通信机制,是 Qt 区别于其他 C++ 框架的核心特性。
原理注解:信号(Signal)是一个特殊的”发出”动作,槽(Slot)是一个可以被调用的普通 C++ 成员函数。MOC(元对象编译器)在编译时为每个含 Q_OBJECT 宏的类生成额外的元对象代码,使信号与槽在运行时通过字符串连接得以工作。
基本语法:
// mybutton.h
#pragma once
#include <QPushButton>
#include <QWidget>
class MyButton : public QPushButton {
Q_OBJECT // 【重要】声明元对象,必须放在类的 private 区段之前
public:
explicit MyButton(QWidget* parent = nullptr);
~MyButton() override;
signals: // 【注解】signals 区段声明的函数自动具有 public 属性
void clickedWithData(int value); // 自定义信号,参数可传递给槽
public slots: // 【注解】slots 区段已废弃写法,现在可以省略不写(任何普通成员函数或 static 函数均可作为槽)
void onButtonClicked(); // 槽函数,处理按钮点击
void processData(int data); // 另一个槽
};
// mybutton.cpp
#include "mybutton.h"
#include <QDebug>
MyButton::MyButton(QWidget* parent) : QPushButton(parent) {
// 【注解】方式1:使用 connect 连接信号与槽(推荐语法,Qt5+)
connect(this, &QPushButton::clicked, this, &MyButton::onButtonClicked);
// 【注解】方式2:使用 Lambda(现代 C++ 推荐写法)
connect(this, &QPushButton::clicked, this, [](bool checked) {
qDebug() << "Button clicked, checked state:" << checked;
});
// 【注解】方式3:旧式语法(兼容 Qt4,但不支持类型安全检查,尽量避免)
// connect(this, SIGNAL(clicked()), this, SLOT(onButtonClicked()));
}
void MyButton::onButtonClicked() {
qDebug() << "Button was clicked!";
emit clickedWithData(42); // 【注解】emit 关键字仅是约定,表明"发出信号"
}
void MyButton::processData(int data) {
qDebug() << "Received data:" << data;
}
connect 的5种重载形式:
// 1. 函数指针(类型安全,编译期检查,推荐)
connect(sender, &SenderClass::signalName, receiver, &ReceiverClass::slotName);
// 2. Lambda(最灵活,适合短逻辑)
connect(sender, &SenderClass::signalName, this, [=](int arg) {
// ...
});
// 3. functor / std::function(需要 Qt::QueuedConnection 等场景)
connect(sender, &SenderClass::signalName, context,
std::function<void(int)>([](int x) { qDebug() << x; }));
// 4. QOverload(处理重载信号/槽)
connect(comboBox, QOverload<int>::of(&QComboBox::activated), this, &MyClass::handleIndex);
// 5. SIGNAL / SLOT 宏(旧式,运行时解析,编译期不检查)
connect(sender, SIGNAL(someSignal(int)), receiver, SLOT(handleInt(int)));
注解:
Q_OBJECT宏展开后会调用QMetaObject::connectSlotsByName(this),这使得 Qt Designer 中 UI 文件的自动连接成为可能——如果在 UI 的某个按钮的objectName为btnSave,则存在on_btnSave_clicked()槽函数时将自动连接。
4.3 Qt Widgets — 常用 UI 组件
4.3.1 主窗口与布局
// mainwindow.h
#pragma once
#include <QMainWindow>
#include <QMenuBar>
#include <QStatusBar>
#include <QToolBar>
#include <QDockWidget>
#include <QTextEdit>
#include <QSplitter>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QStackedWidget>
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget* parent = nullptr);
~MainWindow() override;
private slots:
void onNewFile();
void onOpenFile();
void onSaveFile();
void switchPage(int index); // 切换堆叠页面
private:
void setupMenuBar(); // 设置菜单栏
void setupToolBar(); // 设置工具栏
void setupDockWidget(); // 设置停靠窗口
void setupStatusBar(); // 设置状态栏
QTextEdit* m_textEdit; // 文本编辑器(中心部件)
QDockWidget* m_dockWidget; // 停靠窗口
QStackedWidget* m_stackedWidget; // 页面切换控件
};
// mainwindow.cpp
#include "mainwindow.h"
#include <QFileDialog>
#include <QMessageBox>
#include <QLabel>
#include <QListWidget>
#include <QPushButton>
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent)
{
setWindowTitle("Qt Application - v1.0");
resize(1200, 800);
// ====== 中心部件:使用 QStackedWidget 实现页面切换 ======
m_stackedWidget = new QStackedWidget(this);
setCentralWidget(m_stackedWidget);
// 第1页:文本编辑器
m_textEdit = new QTextEdit(this);
m_stackedWidget->addWidget(m_textEdit);
// 第2页:占位页
auto* placeholder = new QLabel("Page 2: Placeholder", this);
placeholder->setAlignment(Qt::AlignCenter);
m_stackedWidget->addWidget(placeholder);
// ====== 布局系统 ======
// Qt 布局三剑客:
// QVBoxLayout — 垂直布局(从上到下排列)
// QHBoxLayout — 水平布局(从左到右排列)
// QGridLayout — 网格布局(表格形式,支持行列跨度)
// QFormLayout — 表单布局(Label-Field 对,适合设置对话框)
// QSplitter — 可拖拽分割的动态布局
// 示例:水平 + 垂直 嵌套布局
auto* leftPanel = new QListWidget(this);
leftPanel->addItems({"Item 1", "Item 2", "Item 3", "Item 4"});
auto* rightPanel = new QTextEdit(this);
auto* splitter = new QSplitter(Qt::Horizontal, this); // 水平分割
splitter->addWidget(leftPanel);
splitter->addWidget(rightPanel);
splitter->setStretchFactor(0, 1); // 第0个部件拉伸因子
splitter->setStretchFactor(1, 3); // 第1个部件拉伸因子为3(占更大比例)
m_stackedWidget->addWidget(splitter); // 添加为第3页
setupMenuBar();
setupToolBar();
setupDockWidget();
setupStatusBar();
}
void MainWindow::setupMenuBar() {
// 菜单栏
auto* fileMenu = menuBar()->addMenu("文件(&F)");
QAction* newAction = fileMenu->addAction("新建(&N)",
this, &MainWindow::onNewFile, QKeySequence("Ctrl+N"));
QAction* openAction = fileMenu->addAction("打开(&O)",
this, &MainWindow::onOpenFile, QKeySequence("Ctrl+O"));
QAction* saveAction = fileMenu->addAction("保存(&S)",
this, &MainWindow::onSaveFile, QKeySequence("Ctrl+S"));
fileMenu->addSeparator();
QAction* exitAction = fileMenu->addAction("退出(&X)",
this, &QWidget::close, QKeySequence("Ctrl+Q"));
// 快捷键注解:&F 表示 Alt+F 显示菜单,&N 表示在菜单中 Alt+N 快捷键
}
void MainWindow::setupToolBar() {
auto* toolbar = addToolBar("主工具栏");
toolbar->setMovable(false); // 锁定工具栏不可拖动
QAction* newAct = new QAction("新建", this);
toolbar->addAction(newAct);
toolbar->addSeparator();
toolbar->addWidget(new QLabel("搜索:", this));
toolbar->addWidget(new QLineEdit(this)); // 添加入控件到工具栏
}
void MainWindow::setupDockWidget() {
// 停靠窗口:可浮动、可折叠的侧边面板
m_dockWidget = new QDockWidget("属性面板", this);
m_dockWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
m_dockWidget->setWidget(new QListWidget(this));
addDockWidget(Qt::RightDockWidgetArea, m_dockWidget);
}
void MainWindow::setupStatusBar() {
// 永久部件(显示在状态栏最右侧,不随消息滚动)
auto* permLabel = new QLabel("就绪", this);
statusBar()->addPermanentWidget(permLabel);
statusBar()->showMessage("欢迎使用 Qt 应用", 3000); // 3秒后消失的临时消息
}
void MainWindow::onNewFile() {
m_textEdit->clear();
statusBar()->showMessage("新建文件", 2000);
}
void MainWindow::onOpenFile() {
QString fileName = QFileDialog::getOpenFileName(
this,
"打开文件", // 对话框标题
QDir::homePath(), // 起始路径
"文本文件 (*.txt);;所有文件 (*.*)" // 文件过滤器(语法:描述(*.ext);;描述2(*.ext2))
);
if (!fileName.isEmpty()) {
QFile file(fileName);
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
m_textEdit->setPlainText(file.readAll());
file.close();
setWindowTitle(fileName + " - Qt Application");
}
}
}
void MainWindow::onSaveFile() {
QString fileName = QFileDialog::getSaveFileName(
this, "保存文件", QDir::homePath(), "文本文件 (*.txt)");
if (!fileName.isEmpty()) {
QFile file(fileName);
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
file.write(m_textEdit->toPlainText().toUtf8());
file.close();
QMessageBox::information(this, "成功", "文件已保存!");
}
}
}
注解:
QFileDialog::getOpenFileName是静态便捷函数,返回用户选择的文件路径字符串。如果用户取消,返回空字符串。文件过滤器的;;分隔多个过滤器,每个过滤器的格式为描述(*.ext)。
4.3.2 常用控件一览
// ====== 按钮 ======
QPushButton* btn = new QPushButton("点击我", this);
btn->setIcon(QIcon(":/icons/btn.png")); // 从 Qt 资源系统加载图标
btn->setCheckable(true); // 可切换状态(开关按钮)
btn->setDefault(true); // 默认按钮(回车触发)
QRadioButton* radio1 = new QRadioButton("选项A", this);
QRadioButton* radio2 = new QRadioButton("选项B", this);
radio1->setChecked(true); // 默认选中
QCheckBox* checkBox = new QCheckBox("记住密码", this);
checkBox->setTristate(true); // 三态复选框(含半选状态)
// ====== 输入控件 ======
QLineEdit* lineEdit = new QLineEdit(this);
lineEdit->setPlaceholderText("请输入用户名...");
lineEdit->setMaxLength(50);
lineEdit->setEchoMode(QLineEdit::Password); // 密码模式
QTextEdit* textEdit = new QTextEdit(this); // 富文本编辑器
QPlainTextEdit* plainEdit = new QPlainTextEdit(this); // 纯文本(性能更好)
QSpinBox* spinBox = new QSpinBox(this); // 整数微调框
spinBox->setRange(0, 100);
spinBox->setValue(50);
QDoubleSpinBox* dblSpin = new QDoubleSpinBox(this); // 浮点数微调框
dblSpin->setRange(0.0, 100.0);
dblSpin->setDecimals(2); // 2位小数
QSlider* slider = new QSlider(Qt::Horizontal, this); // 滑块
slider->setRange(0, 100);
QComboBox* combo = new QComboBox(this); // 下拉组合框
combo->addItem("选项1", QVariant(1)); // 第二个参数为关联数据
combo->addItem("选项2", QVariant(2));
combo->setEditable(true); // 可编辑
QDateEdit* dateEdit = new QDateEdit(QDate::currentDate(), this);
QTimeEdit* timeEdit = new QTimeEdit(QTime::currentTime(), this);
QDateTimeEdit* dtEdit = new QDateTimeEdit(QDateTime::currentDateTime(), this);
// ====== 显示控件 ======
QLabel* label = new QLabel("Hello Qt!", this);
label->setPixmap(QPixmap(":/images/photo.jpg")); // 显示图片
label->setAlignment(Qt::AlignCenter);
QProgressBar* progressBar = new QProgressBar(this);
progressBar->setRange(0, 100);
progressBar->setValue(60);
progressBar->setTextVisible(true);
// ====== 容器控件 ======
QGroupBox* groupBox = new QGroupBox("分组", this); // 带标题的分组框
QVBoxLayout* groupLayout = new QVBoxLayout(groupBox);
groupLayout->addWidget(new QRadioButton("A"));
groupLayout->addWidget(new QRadioButton("B"));
QTabWidget* tabWidget = new QTabWidget(this); // 标签页
tabWidget->addTab(new QTextEdit, "标签1");
tabWidget->addTab(new QWidget, "标签2");
// ====== 视图/模型控件 ======
// Qt 的 Model/View 架构(详见 4.4 节)
QListView* listView = new QListView(this);
QTableView* tableView = new QTableView(this);
QTreeView* treeView = new QTreeView(this);
QColumnView* colView = new QColumnView(this);
QListWidget* listWidget = new QListWidget(this); // 便捷类(内置 Model)
QTreeWidget* treeWidget = new QTreeWidget(this);
QTableWidget* tableWidget = new QTableWidget(5, 3, this); // 5行3列
4.4 Model/View 架构
Qt 的 Model/View 架构将数据(Model)、显示(View)、交互(Controller)三者分离,是 Qt 处理列表/表格/树数据的标准方式。
架构注解:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ View │────▶│ Model │◀────│ 原始数据 │
│ (QTableView)│ │(QAbstractTableModel)│ (数据库等)│
└─────────────┘ └─────────────┘
│ │
│ ┌──────────────┐│
└───▶│ Controller │
│ (QItemDelegate)│
└──────────────┘
自定义表格模型示例:
// persontablemodel.h
#pragma once
#include <QAbstractTableModel>
#include <QVector>
#include <QVariant>
struct Person {
QString name;
int age;
QString email;
};
class PersonTableModel : public QAbstractTableModel {
Q_OBJECT
public:
enum ColumnRole { Name = 0, Age, Email, ColumnCount };
// 【注解】枚举 ColumnRole 末尾 ColumnCount 用于定义列数
explicit PersonTableModel(QObject* parent = nullptr);
// ====== 必须重写的纯虚函数 ======
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
// ====== 可重写的函数(支持编辑) ======
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
bool setData(const QModelIndex& index, const QVariant& value,
int role = Qt::EditRole) override;
// ====== 数据操作 API ======
void addPerson(const Person& person);
void removePerson(int row);
void clearAll();
private:
QVector<Person> m_persons;
};
// persontablemodel.cpp
#include "persontablemodel.h"
PersonTableModel::PersonTableModel(QObject* parent)
: QAbstractTableModel(parent)
{
}
int PersonTableModel::rowCount(const QModelIndex& parent) const {
// 【注解】parent 用于树模型,表格模型 parent 不为 invalid 时表示子表,此处忽略
if (parent.isValid()) return 0;
return m_persons.size();
}
int PersonTableModel::columnCount(const QModelIndex& parent) const {
Q_UNUSED(parent);
return ColumnCount;
}
QVariant PersonTableModel::data(const QModelIndex& index, int role) const {
if (!index.isValid()) return QVariant();
if (index.row() >= m_persons.size()) return QVariant();
const Person& p = m_persons[index.row()];
if (role == Qt::DisplayRole || role == Qt::EditRole) {
switch (index.column()) {
case Name: return p.name;
case Age: return p.age;
case Email: return p.email;
}
}
return QVariant();
}
QVariant PersonTableModel::headerData(int section, Qt::Orientation orientation,
int role) const {
if (role != Qt::DisplayRole) return QVariant();
if (orientation != Qt::Horizontal) return QVariant();
static const QString headers[ColumnCount] = {"姓名", "年龄", "邮箱"};
return headers[section];
}
Qt::ItemFlags PersonTableModel::flags(const QModelIndex& index) const {
// 【注解】flags 返回项的可选状态:可选择、可编辑、可勾选等
if (!index.isValid()) return Qt::NoItemFlags;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
}
bool PersonTableModel::setData(const QModelIndex& index,
const QVariant& value, int role) {
if (role != Qt::EditRole) return false;
if (!index.isValid() || index.row() >= m_persons.size()) return false;
Person& p = m_persons[index.row()];
switch (index.column()) {
case Name: p.name = value.toString(); break;
case Age: p.age = value.toInt(); break;
case Email: p.email = value.toString(); break;
default: return false;
}
// 【注解】dataChanged 信号通知 View 数据已更新,View 会重新请求 data()
emit dataChanged(index, index, {role});
return true;
}
void PersonTableModel::addPerson(const Person& person) {
beginInsertRows(QModelIndex(), m_persons.size(), m_persons.size());
m_persons.append(person);
endInsertRows(); // 【注解】begin/end 成对调用,确保 View 正确处理插入动画
}
void PersonTableModel::removePerson(int row) {
if (row < 0 || row >= m_persons.size()) return;
beginRemoveRows(QModelIndex(), row, row);
m_persons.remove(row);
endRemoveRows();
}
void PersonTableModel::clearAll() {
beginResetModel(); // 【注解】批量更新开始,与 endResetModel() 成对使用
m_persons.clear();
endResetModel();
}
View 使用示例:
// 在 MainWindow 中使用
void MainWindow::setupTableView() {
auto* model = new PersonTableModel(this);
auto* view = new QTableView(this);
view->setModel(model);
// 添加测试数据
model->addPerson({"张三", 28, "zhangsan@example.com"});
model->addPerson({"李四", 35, "lisi@example.com"});
model->addPerson({"王五", 42, "wangwu@example.com"});
// 视图配置
view->setSelectionBehavior(QAbstractItemView::SelectRows); // 整行选中
view->setSelectionMode(QAbstractItemView::SingleSelection); // 单选
view->setAlternatingRowColors(true); // 交替行颜色
view->resizeColumnsToContents(); // 自动列宽
view->setColumnWidth(PersonTableModel::Email, 250); // 邮箱列加宽
view->setCornerButtonEnabled(false); // 左上角按钮禁用
}
注解:对于简单场景,
QTableWidget/QListWidget/QTreeWidget是更便捷的选择(它们是封装好的便捷类,内置默认模型)。但当需要处理大数据集(>10000行)、共享数据源或需要精确控制时,必须使用QAbstract*Model的自定义模型。
4.5 Qt Network — 网络编程
4.5.1 HTTP 请求(QNetworkAccessManager)
// httpclient.h
#pragma once
#include <QWidget>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QJsonDocument>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
class HttpClient : public QWidget {
Q_OBJECT
public:
explicit HttpClient(QWidget* parent = nullptr);
private slots:
void onGetRequest();
void onPostRequest();
void onReplyFinished();
void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
private:
void sendRequest(const QString& url, const QByteArray& data = QByteArray(),
const QString& method = "GET");
QNetworkAccessManager* m_manager;
QLabel* m_resultLabel;
QLabel* m_progressLabel;
};
// httpclient.cpp
#include "httpclient.h"
#include <QNetworkRequest>
#include <QHttpMultiPart>
#include <QJsonObject>
#include <QJsonDocument>
#include <QNetworkReply>
#include <QUrl>
#include <QUrlQuery>
#include <QDebug>
HttpClient::HttpClient(QWidget* parent)
: QWidget(parent)
{
m_manager = new QNetworkAccessManager(this);
// 【注解】QNetworkAccessManager 会自动管理 cookie、代理、缓存
// 不需要手动 delete 网络请求产生的 reply(QNetworkReply)
// reply 的 deleteLater() 会在 finished 信号触发后由 manager 处理
auto* layout = new QVBoxLayout(this);
m_resultLabel = new QLabel("等待请求...", this);
m_progressLabel = new QLabel(this);
auto* getBtn = new QPushButton("发送 GET 请求", this);
auto* postBtn = new QPushButton("发送 POST 请求(JSON)", this);
layout->addWidget(getBtn);
layout->addWidget(postBtn);
layout->addWidget(m_progressLabel);
layout->addWidget(m_resultLabel);
connect(getBtn, &QPushButton::clicked, this, &HttpClient::onGetRequest);
connect(postBtn, &QPushButton::clicked, this, &HttpClient::onPostRequest);
}
void HttpClient::onGetRequest() {
// GET 请求示例:获取 JSONPlaceholder 的示例数据
sendRequest("https://jsonplaceholder.typicode.com/posts/1");
}
void HttpClient::onPostRequest() {
// POST 请求示例:发送 JSON 数据
QJsonObject json;
json["title"] = "Qt 网络编程";
json["body"] = "QNetworkAccessManager 示例";
json["userId"] = 1;
QByteArray data = QJsonDocument(json).toJson(QJsonDocument::Compact);
sendRequest("https://jsonplaceholder.typicode.com/posts", data, "POST");
}
void HttpClient::sendRequest(const QString& url, const QByteArray& data,
const QString& method) {
QNetworkRequest request(QUrl(url));
// 设置请求头
request.setHeader(QNetworkRequest::ContentTypeHeader,
"application/json; charset=utf-8");
request.setHeader(QNetworkRequest::UserAgentHeader,
"QtNetworkClient/1.0");
request.setRawHeader("Accept", "application/json");
QNetworkReply* reply = nullptr;
if (method == "GET") {
reply = m_manager->get(request);
} else if (method == "POST") {
reply = m_manager->post(request, data);
} else if (method == "PUT") {
reply = m_manager->put(request, data);
} else if (method == "DELETE") {
reply = m_manager->deleteResource(request);
}
// 【注解】直接使用 lambda 作为槽(Qt5+ 语法)
connect(reply, &QNetworkReply::finished, this, &HttpClient::onReplyFinished);
connect(reply, &QNetworkReply::downloadProgress,
this, &HttpClient::onDownloadProgress);
// 错误处理连接
connect(reply, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error),
this, [](QNetworkReply::NetworkError error) {
qWarning() << "Network error:" << error;
});
m_resultLabel->setText("请求中...");
}
void HttpClient::onReplyFinished() {
auto* reply = qobject_cast<QNetworkReply*>(sender());
if (!reply) return;
if (reply->error() == QNetworkReply::NoError) {
QByteArray response = reply->readAll();
// 【注解】尝试解析为 JSON
QJsonParseError parseError;
QJsonDocument doc = QJsonDocument::fromJson(response, &parseError);
if (parseError.error == QJsonParseError::NoError) {
m_resultLabel->setText(
QString::fromUtf8(QJsonDocument::toJson(doc,
QJsonDocument::Indented))); // 格式化 JSON
} else {
// 非 JSON 响应,直接显示原文
m_resultLabel->setText(QString::fromUtf8(response).left(500));
}
} else {
m_resultLabel->setText("错误: " + reply->errorString());
}
reply->deleteLater(); // 【注解】手动调用 deleteLater() 确保安全删除
}
void HttpClient::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
if (bytesTotal > 0) {
int percent = static_cast<int>(bytesReceived * 100 / bytesTotal);
m_progressLabel->setText(QString("下载进度: %1%").arg(percent));
}
}
4.5.2 TCP 套接字
// tcpserver.h
#pragma once
#include <QTcpServer>
#include <QTcpSocket>
#include <QWidget>
#include <QTextEdit>
#include <QVBoxLayout>
#include <QPushButton>
#include <QLabel>
#include <QVector>
class TcpServer : public QWidget {
Q_OBJECT
public:
explicit TcpServer(QWidget* parent = nullptr);
~TcpServer() override;
private slots:
void onNewConnection(); // 客户端连接
void onClientDisconnected(); // 客户端断开
void onReadyRead(); // 收到数据
void onSendMessage(); // 发送消息
private:
void broadcastMessage(const QString& message);
QTcpServer* m_server;
QVector<QTcpSocket*> m_clients; // 【注解】维护所有已连接客户端
QTextEdit* m_logWidget;
quint16 m_port;
};
// tcpserver.cpp
#include "tcpserver.h"
#include <QNetworkInterface>
#include <QMessageBox>
#include <QInputDialog>
TcpServer::TcpServer(QWidget* parent)
: QWidget(parent)
, m_server(new QTcpServer(this))
, m_port(8080)
{
auto* layout = new QVBoxLayout(this);
m_logWidget = new QTextEdit(this);
m_logWidget->setReadOnly(true);
auto* sendBtn = new QPushButton("广播消息给所有客户端");
layout->addWidget(new QLabel("TCP 服务器日志:"));
layout->addWidget(m_logWidget);
layout->addWidget(sendBtn);
// 【注解】incomingConnection() 在新连接到达时自动调用
// 也可以使用 NewConnection 信号(二选一)
connect(m_server, &QTcpServer::newConnection,
this, &TcpServer::onNewConnection);
connect(sendBtn, &QPushButton::clicked, this, &TcpServer::onSendMessage);
// 启动服务器
if (!m_server->listen(QHostAddress::Any, m_port)) {
QMessageBox::critical(this, "错误",
"无法启动服务器: " + m_server->errorString());
} else {
// 显示本机所有 IP
QString ipList;
for (const QHostAddress& addr : QNetworkInterface::allAddresses()) {
if (addr.protocol() == QAbstractSocket::IPv4Protocol) {
ipList += addr.toString() + "\n";
}
}
m_logWidget->append(QString("服务器启动,监听端口 %1\nIP:\n%2")
.arg(m_port).arg(ipList));
}
}
void TcpServer::onNewConnection() {
// 【注解】每次信号触发时,调用 nextPendingConnection() 获取新连接
// 必须在事件循环中尽快调用,否则连接可能丢失
while (m_server->hasPendingConnections()) {
QTcpSocket* clientSocket = m_server->nextPendingConnection();
m_clients.append(clientSocket);
m_logWidget->append(QString("客户端连接: %1:%2")
.arg(clientSocket->peerAddress().toString())
.arg(clientSocket->peerPort()));
// 连接信号
connect(clientSocket, &QTcpSocket::readyRead,
this, &TcpServer::onReadyRead);
connect(clientSocket, &QTcpSocket::disconnected,
this, &TcpServer::onClientDisconnected);
connect(clientSocket, &QTcpSocket::errorOccurred,
this, [this](QAbstractSocket::SocketError error) {
m_logWidget->append("Socket 错误: " + QString::number(error));
});
}
}
void TcpServer::onReadyRead() {
auto* client = qobject_cast<QTcpSocket*>(sender());
if (!client) return;
// 【注解】TCP 是流协议,readAll() 可能收到多条合并的数据或一条截断的数据
// 实际应用中需要自定义协议(如 Length-Prefix、JSONL 等)处理粘包
QByteArray data = client->readAll();
QString message = QString::fromUtf8(data).trimmed();
m_logWidget->append(QString("[%1:%2] >> %3")
.arg(client->peerAddress().toString())
.arg(client->peerPort())
.arg(message));
// 广播给所有其他客户端
broadcastMessage(QString("[服务器广播] %1").arg(message));
}
void TcpServer::onClientDisconnected() {
auto* client = qobject_cast<QTcpSocket*>(sender());
if (!client) return;
m_clients.removeAll(client);
m_logWidget->append(QString("客户端断开: %1:%2")
.arg(client->peerAddress().toString())
.arg(client->peerPort()));
client->deleteLater();
}
void TcpServer::onSendMessage() {
bool ok;
QString msg = QInputDialog::getText(this, "广播消息", "输入消息:",
QLineEdit::Normal, "", &ok);
if (ok && !msg.isEmpty()) {
broadcastMessage(msg);
}
}
void TcpServer::broadcastMessage(const QString& message) {
QByteArray data = message.toUtf8();
for (QTcpSocket* client : m_clients) {
if (client->state() == QAbstractSocket::ConnectedState) {
client->write(data + "\n"); // 附加换行符作为简单分隔符
}
}
m_logWidget->append(QString("[广播] %1").arg(message));
}
注解:TCP 是面向流的协议,不保留消息边界。需要自定义应用层协议。常用方法:1) 固定长度消息;2) Length-Prefix(前4字节表示长度,后续为数据);3) 特殊分隔符(如换行符);4) JSON-RPC 等。
4.6 Qt SQL — 数据库操作
// databasemanager.h
#pragma once
#include <QObject>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QSqlRecord>
#include <QDebug>
#include <QFile>
#include <QDir>
class DatabaseManager : public QObject {
Q_OBJECT
public:
static DatabaseManager& instance(); // 单例模式
bool initDatabase(const QString& dbName);
// CRUD 操作
bool createTable();
bool insertRecord(const QString& name, int age, const QString& email);
QVector<QVariantList> selectAllRecords();
bool updateRecord(int id, const QString& name, int age);
bool deleteRecord(int id);
signals:
void databaseOpened();
void databaseError(const QString& error);
private:
explicit DatabaseManager(QObject* parent = nullptr);
~DatabaseManager() override;
Q_DISABLE_COPY(DatabaseManager) // 【注解】禁用拷贝构造,防止单例被复制
QSqlDatabase m_database;
QString m_connectionName;
};
// databasemanager.cpp
#include "databasemanager.h"
DatabaseManager::DatabaseManager(QObject* parent)
: QObject(parent)
, m_connectionName("db_connection_" + QString::number(reinterpret_cast<quintptr>(this)))
{}
DatabaseManager& DatabaseManager::instance() {
static DatabaseManager inst; // 【注解】Meyers Singleton,线程安全(C++11+)
return inst;
}
bool DatabaseManager::initDatabase(const QString& dbName) {
// 【注解】每个数据库连接必须有唯一的连接名
// 如果不指定,默认使用 "qt_sql_default_connection"(仅允许一个默认连接)
m_database = QSqlDatabase::addDatabase("QSQLITE", m_connectionName);
// SQLite:单文件数据库,路径可自定义
// 其他驱动:QMYSQL(MySQL/MariaDB)、QPSQL(PostgreSQL)、QODBC(ODBC)
QString dbPath = QDir::currentPath() + "/" + dbName + ".db";
m_database.setDatabaseName(dbPath);
if (!m_database.open()) {
qWarning() << "数据库打开失败:" << m_database.lastError().text();
emit databaseError(m_database.lastError().text());
return false;
}
qDebug() << "数据库打开成功:" << dbPath;
emit databaseOpened();
return createTable();
}
bool DatabaseManager::createTable() {
QSqlQuery query(m_database);
// 【注解】QSqlQuery 可直接执行原生 SQL
// 推荐做法:所有用户输入通过绑定变量,防止 SQL 注入
bool ok = query.exec(R"(
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
age INTEGER NOT NULL DEFAULT 0,
email TEXT
)
)");
if (!ok) {
qWarning() << "建表失败:" << query.lastError().text();
}
return ok;
}
bool DatabaseManager::insertRecord(const QString& name, int age,
const QString& email) {
QSqlQuery query(m_database);
// 【注解】使用占位符 ? 而非字符串拼接,防 SQL 注入
query.prepare("INSERT INTO users (name, age, email) VALUES (?, ?, ?)");
query.bindValue(0, name);
query.bindValue(1, age);
query.bindValue(2, email);
return query.exec();
}
QVector<QVariantList> DatabaseManager::selectAllRecords() {
QVector<QVariantList> results;
QSqlQuery query(m_database);
query.setForwardOnly(true); // 【注解】仅向前遍历,节省内存(大数据集必备)
if (query.exec("SELECT id, name, age, email FROM users")) {
while (query.next()) {
QSqlRecord record = query.record();
QVariantList row;
for (int i = 0; i < record.count(); ++i) {
row.append(record.value(i));
}
results.append(row);
}
}
return results;
}
bool DatabaseManager::updateRecord(int id, const QString& name, int age) {
QSqlQuery query(m_database);
query.prepare("UPDATE users SET name = ?, age = ? WHERE id = ?");
query.bindValue(0, name);
query.bindValue(1, age);
query.bindValue(2, id);
return query.exec();
}
bool DatabaseManager::deleteRecord(int id) {
QSqlQuery query(m_database);
query.prepare("DELETE FROM users WHERE id = ?");
query.bindValue(0, id);
return query.exec();
}
DatabaseManager::~DatabaseManager() {
if (m_database.isOpen()) {
m_database.close();
}
// 【注解】关闭连接后需移除(否则下次用同一连接名会复用旧连接)
QSqlDatabase::removeDatabase(m_connectionName);
}
注解:SQLite 适合桌面应用的数据持久化(单文件、零配置、事务支持)。生产环境或多人并发场景使用 MySQL/PostgreSQL。Qt SQL 使用插件系统,加载驱动只需
QSqlDatabase::addDatabase("QMYSQL")并确保对应驱动 DLL 在路径中。
4.7 Qt 多线程
Qt 提供了多种多线程方式,按复杂度从低到高排列:
4.7.1 QThread + Worker 对象(推荐方式)
// worker.h
#pragma once
#include <QObject>
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include <QVector>
#include <QElapsedTimer>
class Worker : public QObject {
Q_OBJECT
public:
explicit Worker(QObject* parent = nullptr);
~Worker() override;
void startWork();
void stopWork();
void pauseWork();
void resumeWork();
signals:
void progressUpdated(int percent, const QString& status);
void workFinished(const QString& result);
void errorOccurred(const QString& error);
public slots:
void process(); // 工作线程的实际任务入口
private:
void doComputation(); // 模拟耗时计算
bool m_running = false;
bool m_paused = false;
QMutex m_mutex;
QWaitCondition m_pauseCondition;
QVector<int> m_results;
};
// worker.cpp
#include "worker.h"
#include <QDebug>
#include <QtMath>
Worker::Worker(QObject* parent) : QObject(parent) {}
Worker::~Worker() = default;
void Worker::startWork() {
QMutexLocker locker(&m_mutex);
m_running = true;
m_paused = false;
m_results.clear();
}
void Worker::stopWork() {
{
QMutexLocker locker(&m_mutex);
m_running = false;
}
m_pauseCondition.wakeAll(); // 唤醒可能阻塞的线程
}
void Worker::pauseWork() {
QMutexLocker locker(&m_mutex);
m_paused = true;
}
void Worker::resumeWork() {
{
QMutexLocker locker(&m_mutex);
m_paused = false;
}
m_pauseCondition.wakeAll();
}
void Worker::process() {
qDebug() << "Worker 线程 ID:" << QThread::currentThreadId();
QElapsedTimer timer;
timer.start();
doComputation();
int elapsedMs = timer.elapsed();
QString result = QString("完成 %1 项计算,耗时 %2 ms").arg(m_results.size()).arg(elapsedMs);
emit workFinished(result);
}
void Worker::doComputation() {
const int total = 1000000;
for (int i = 0; i < total; ++i) {
// ====== 检查暂停/停止信号 ======
{
QMutexLocker locker(&m_mutex);
if (!m_running) return;
while (m_paused) {
// 【注解】wait() 会释放锁,等待 wakeAll() 唤醒后重新加锁
m_pauseCondition.wait(&m_mutex);
if (!m_running) return; // 被停止唤醒时也要退出
}
}
// ====== 模拟计算 ======
double val = qSqrt(i) * qSin(i / 1000.0);
m_results.append(static_cast<int>(val * 1000));
// 每 1% 报告一次进度
if (i % (total / 100) == 0) {
int percent = static_cast<int>(i * 100.0 / total);
// 【注解】信号跨线程自动使用 Qt::QueuedConnection,保证线程安全
emit progressUpdated(percent, QString("处理中... %1%").arg(percent));
}
}
}
// threadmanager.h
#pragma once
#include <QObject>
#include <QThread>
#include <QProgressBar>
#include <QPushButton>
#include <QVBoxLayout>
#include <QLabel>
#include <QWidget>
class ThreadManager : public QWidget {
Q_OBJECT
public:
explicit ThreadManager(QWidget* parent = nullptr);
~ThreadManager() override;
private slots:
void startThread();
void stopThread();
void onProgress(int percent, const QString& status);
void onFinished(const QString& result);
private:
QThread* m_workerThread;
QObject* m_worker; // Worker 对象将在子线程中执行
QProgressBar* m_progressBar;
QLabel* m_statusLabel;
};
// threadmanager.cpp
#include "threadmanager.h"
#include "worker.h"
ThreadManager::ThreadManager(QWidget* parent)
: QWidget(parent)
{
auto* layout = new QVBoxLayout(this);
m_progressBar = new QProgressBar(this);
m_statusLabel = new QLabel("就绪", this);
auto* startBtn = new QPushButton("启动线程");
auto* stopBtn = new QPushButton("停止线程");
layout->addWidget(m_progressBar);
layout->addWidget(m_statusLabel);
layout->addWidget(startBtn);
layout->addWidget(stopBtn);
connect(startBtn, &QPushButton::clicked, this, &ThreadManager::startThread);
connect(stopBtn, &QPushButton::clicked, this, &ThreadManager::stopThread);
}
ThreadManager::~ThreadManager() {
if (m_workerThread) {
// 【注解】安全退出流程:
// 1. 发送停止信号
// 2. quit() 让事件循环退出
// 3. wait() 等待线程结束
QMetaObject::invokeMethod(m_worker, "stopWork", Qt::BlockingQueuedConnection);
m_workerThread->quit();
m_workerThread->wait(5000); // 超时5秒
if (m_workerThread->isRunning()) {
m_workerThread->terminate(); // 【注解】terminate 是最后手段,不推荐
qWarning() << "线程未正常退出,已强制终止";
}
}
}
void ThreadManager::startThread() {
// 创建线程
m_workerThread = new QThread(this);
// 创建 Worker 对象(不要指定 parent!)
// 【注解】Worker 不能有 parent,因为 QThread 才是线程归属
m_worker = new Worker();
// 将 Worker 移入子线程
m_worker->moveToThread(m_workerThread);
// 线程启动时执行 process()
connect(m_workerThread, &QThread::started, m_worker, &Worker::process);
// Worker 完成后清理
connect(m_worker, &Worker::workFinished, this, &ThreadManager::onFinished);
connect(m_worker, &Worker::workFinished, m_workerThread, &QThread::quit);
connect(m_workerThread, &QThread::finished, m_workerThread, &QThread::deleteLater);
connect(m_workerThread, &QThread::finished, m_worker, &QObject::deleteLater);
// 进度更新(跨线程信号,自动使用 QueuedConnection)
connect(m_worker, &Worker::progressUpdated, this, &ThreadManager::onProgress);
// 初始化并启动
QMetaObject::invokeMethod(m_worker, "startWork", Qt::QueuedConnection);
m_workerThread->start();
}
void ThreadManager::stopThread() {
if (m_workerThread && m_workerThread->isRunning()) {
QMetaObject::invokeMethod(m_worker, "stopWork", Qt::QueuedConnection);
m_workerThread->quit();
m_workerThread->wait(5000);
m_progressBar->setValue(0);
m_statusLabel->setText("已停止");
}
}
void ThreadManager::onProgress(int percent, const QString& status) {
m_progressBar->setValue(percent);
m_statusLabel->setText(status);
}
void ThreadManager::onFinished(const QString& result) {
m_progressBar->setValue(100);
m_statusLabel->setText(result);
}
注解:
QObject::moveToThread()是 Qt 线程模型的核心:它改变对象的事件处理线程(对象所在的线程决定了哪个线程的事件循环处理它的 slot)。含 Q_OBJECT 的对象必须先 moveToThread,再连接信号,不能在对象已移动到其他线程后再添加信号槽。QMutexLocker在构造时自动加锁,析构时自动解锁(C++ RAII 模式),防止异常时锁泄漏。
4.8 Qt Quick / QML — 现代声明式 UI
QML(Qt Modeling Language)是一种声明式语言,适合移动端、嵌入式和需要动态 UI 的场景。Qt6 推荐 Qt Quick 3(QML + Qt Quick)+ Qt Shader Tools 做现代 UI。
适用场景对比:
| 场景 | 推荐技术 |
|---|---|
| 传统桌面应用(表单/列表/窗口) | Qt Widgets |
| 高度自定义 UI / 动画 / 移动端风格 | Qt Quick / QML |
| 数据可视化 / 图表 | Qt Charts / Qt DataVisualization |
| 3D 渲染 | Qt 3D / Qt Quick 3D |
| 嵌入式 HMI | Qt Quick + Qt Wayland |
混合编程(QML + C++)示例:
// qmlbridge.h — C++ 注册到 QML 的桥接类
#pragma once
#include <QObject>
#include <QQmlEngine>
#include <QQmlContext>
class QmlBridge : public QObject {
Q_OBJECT
// 【注解】QML 中通过 AppData { ... } 使用此类
// 需在 main() 中通过 qmlRegisterSingletonType 或 setContextProperty 注册
Q_PROPERTY(QString appName READ appName CONSTANT)
Q_PROPERTY(int version READ version CONSTANT)
public:
QmlBridge(QObject* parent = nullptr) : QObject(parent), m_version(10607) {}
QString appName() const { return "MyQmlApp"; }
int version() const { return m_version; }
// 可被 QML 调用的 C++ 方法
Q_INVOKABLE QString formatTime(qint64 msSinceEpoch) const;
Q_INVOKABLE void saveToFile(const QString& path, const QString& content);
public slots:
void receiveFromQml(const QString& message); // QML 发来的消息
signals:
void sendToQml(const QString& message); // 发给 QML 的消息
private:
int m_version;
};
QString QmlBridge::formatTime(qint64 msSinceEpoch) const {
return QDateTime::fromMSecsSinceEpoch(msSinceEpoch)
.toString("yyyy-MM-dd HH:mm:ss");
}
void QmlBridge::receiveFromQml(const QString& message) {
qDebug() << "收到 QML 消息:" << message;
emit sendToQml("C++ 已收到: " + message);
}
// main.qml — 主 QML 文件
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
ApplicationWindow {
id: root
visible: true
width: 480
height: 320
title: "Qt Quick 示例"
// ====== 布局 ======
ColumnLayout {
anchors.fill: parent
anchors.margins: 16
spacing: 12
// 标题
Label {
text: appBridge.appName + " v" + (appBridge.version / 10000.0).toFixed(2)
font.pixelSize: 20
font.bold: true
Layout.alignment: Qt.AlignHCenter
}
// 按钮
Button {
text: "点击发送消息到 C++"
Layout.alignment: Qt.AlignHCenter
onClicked: {
// 调用 C++ 注册的 Q_INVOKABLE 方法
var timestamp = Date.now()
var timeStr = appBridge.formatTime(timestamp)
console.log("格式化时间:", timeStr)
appBridge.receiveFromQml("Hello from QML at " + timeStr)
}
}
// 消息显示区
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: "#f5f5f5"
border.color: "#ddd"
border.width: 1
radius: 4
Label {
id: messageLabel
anchors.centerIn: parent
text: "等待 C++ 响应..."
color: "#333"
}
}
// 接收 C++ 信号
Connections {
target: appBridge
function onSendToQml(msg) {
messageLabel.text = msg
console.log("C++ 信号:", msg)
}
}
}
}
// main.cpp — QML 与 C++ 混合入口
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQuickStyle>
#include "qmlbridge.h"
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
// 注册 C++ 对象到 QML 全局上下文
// 【注解】setContextProperty 将 C++ 对象暴露为 QML 中的全局变量 appBridge
QQmlApplicationEngine engine;
QmlBridge* bridge = new QmlBridge(&engine); // parent 设为 engine
engine.rootContext()->setContextProperty("appBridge", bridge);
// 加载 QML 文件
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject* obj, const QUrl& objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(EXIT_FAILURE);
});
engine.load(url);
return app.exec();
}
4.9 Qt 资源系统(qrc)
Qt 资源系统将图片、图标、音频、QML 等文件打包进可执行文件,无需外部文件依赖。
资源文件创建(在 .pro / CMakeLists.txt 中引用):
<!-- resources.qrc -->
<RCC>
<qresource prefix="/">
<file>main.qml</file>
<file>images/logo.png</file>
<file>images/bg.jpg</file>
<file>icons/app.icns</file>
<file>icons/app.ico</file>
</qresource>
<!-- 国际化翻译 -->
<qresource prefix="/i18n">
<file>translations/myapp_zh_CN.qm</file>
<file>translations/myapp_en.qm</file>
</qresource>
</RCC>
CMake 中引用:
# CMakeLists.txt 中添加资源
set(RESOURCES resources.qrc)
target_sources(myapp PRIVATE ${RESOURCES})
代码中使用资源路径:
// 使用 : 前缀,表示从 qrc 资源系统加载
QPixmap pixmap(":/images/logo.png");
QIcon icon(":/icons/app.ico");
QFile file(":/main.qml");
// 加载翻译文件
QTranslator translator;
translator.load(":/i18n/myapp_zh_CN.qm");
app.installTranslator(&translator);
注解:资源路径的
prefix前缀可通过<qresource prefix="/custom">自定义。:/开头的路径为 qrc 路径。如果文件不在 qrc 中,使用相对路径(如images/logo.png)或绝对文件系统路径。
4.10 Qt 插件系统(Plugin)
Qt 的插件系统允许模块化开发动态加载插件。
// ====== 定义插件接口 ======
// plugininterface.h — 插件必须实现的接口
#pragma once
#include <QString>
// 【注解】插件接口必须是纯虚基类
// Q_DECLARE_INTERFACE 告诉 Qt 元对象系统此接口的标识符
class PluginInterface {
public:
virtual ~PluginInterface() = default;
virtual QString name() const = 0;
virtual void doWork() = 0;
};
#define PluginInterface_iid "com.myapp.PluginInterface/1.0"
Q_DECLARE_INTERFACE(PluginInterface, PluginInterface_iid)
// ====== 实现插件 ======
// myplugin.h
#pragma once
#include <QObject>
#include <QPluginLoader>
#include <plugininterface.h>
class MyPlugin : public QObject, public PluginInterface {
Q_OBJECT
Q_PLUGIN_METADATA(IID PluginInterface_iid FILE "myplugin.json") // 【注解】插件元数据
Q_INTERFACES(PluginInterface) // 声明实现哪个接口
public:
QString name() const override { return "My Plugin"; }
void doWork() override;
};
// myplugin.cpp
#include "myplugin.h"
#include <QDebug>
void MyPlugin::doWork() {
qDebug() << "MyPlugin is working!";
}
// ====== 加载插件 ======
// main.cpp 片段
void loadPlugins() {
QDir pluginsDir = QCoreApplication::applicationDirPath() + "/plugins";
QPluginLoader loader;
for (const QString& fileName : pluginsDir.entryList(QDir::Files)) {
loader.setFileName(pluginsDir.filePath(fileName));
// 【注解】load() 动态加载插件 DLL/so,isLoaded() 检查是否成功
if (loader.load()) {
QObject* plugin = loader.instance();
if (plugin) {
PluginInterface* iface = qobject_cast<PluginInterface*>(plugin);
if (iface) {
qDebug() << "加载插件:" << iface->name();
iface->doWork();
}
}
} else {
qWarning() << "插件加载失败:" << loader.errorString();
}
}
}
4.11 国际化(i18n / lrelease)
Qt 的国际化系统支持应用程序界面文字的多语言切换。
翻译流程:
1. 在代码中用 tr() 包裹所有用户可见字符串
↓
2. 运行 lupdate(从 .cpp/.h/.qml 中提取 tr() 字符串)→ .ts 文件(XML 格式)
↓
3. 用 Qt Linguist(linguist.exe)编辑 .ts 文件,填入翻译
↓
4. 运行 lrelease → .qm 文件(二进制压缩格式,供运行时加载)
// 在代码中使用 tr()
QLabel* label = new QLabel(tr("用户名:"), this);
QPushButton* btn = new QPushButton(tr("确定"), this);
QMessageBox::information(this, tr("提示"), tr("操作成功!"));
// 运行时切换语言
void MainWindow::switchLanguage(const QString& locale) {
static QTranslator translator;
static QString currentLocale;
// 卸载旧翻译
if (!currentLocale.isEmpty()) {
QCoreApplication::removeTranslator(&translator);
}
// 加载新翻译
QString qmFile = QString(":/i18n/myapp_%1.qm").arg(locale);
if (translator.load(qmFile)) {
QCoreApplication::installTranslator(&translator);
currentLocale = locale;
// 【注解】重新翻译现有 UI
retranslateUi();
}
}
5. 常用代码范式与示例
5.1 单例模式
// singleton.h
#pragma once
#include <QObject>
#include <QMutex>
#include <QMutexLocker>
class Singleton : public QObject {
Q_OBJECT
public:
static Singleton& getInstance() {
// 【注解】C++11 magic static,线程安全,且只在首次访问时构造
static Singleton instance;
return instance;
}
void doSomething();
// 禁止拷贝和赋值
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
private:
explicit Singleton(QObject* parent = nullptr);
~Singleton() override;
QMutex m_mutex;
};
5.2 事件过滤器
// 拦截其他对象的事件(全局快捷键、输入限制等场景)
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget* parent = nullptr);
protected:
bool eventFilter(QObject* watched, QEvent* event) override;
private:
QLineEdit* m_searchEdit;
};
// 安装事件过滤器(在构造函数中)
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent)
{
m_searchEdit = new QLineEdit(this);
m_searchEdit->installEventFilter(this); // 【注解】拦截 m_searchEdit 的所有事件
}
// 重写 eventFilter
bool MainWindow::eventFilter(QObject* watched, QEvent* event) {
if (watched == m_searchEdit) {
if (event->type() == QEvent::KeyPress) {
auto* ke = static_cast<QKeyEvent*>(event);
// 限制只能输入数字和字母
if (!ke->text().isEmpty() &&
!ke->text().at(0).isLetterOrNumber() &&
ke->key() != Qt::Key_Backspace) {
return true; // 事件被拦截,不再传递
}
}
}
// 默认处理:事件继续传递
return QMainWindow::eventFilter(watched, event);
}
5.3 自定义属性动画
// QPropertyAnimation 用于 animate 属性(需定义 READ 函数)
QPushButton* btn = new QPushButton("动画按钮", this);
btn->setGeometry(0, 100, 100, 40);
// 位置动画
QPropertyAnimation* posAnim = new QPropertyAnimation(btn, "pos", this);
posAnim->setDuration(1000); // 1秒
posAnim->setStartValue(QVariant(QPoint(0, 100)));
posAnim->setEndValue(QVariant(QPoint(500, 100)));
posAnim->setEasingCurve(QEasingCurve::OutBounce); // 弹跳曲线
posAnim->start();
// 多个动画组合
QParallelAnimationGroup* group = new QParallelAnimationGroup(this);
group->addAnimation(posAnim);
// ... 添加其他动画
group->start();
5.4 JSON / XML 处理
// ====== JSON ======
QJsonObject obj;
obj["name"] = "测试";
obj["score"] = 95.5;
obj["tags"] = QJsonArray({"Qt", "C++", "GUI"});
QJsonDocument doc(obj);
// 序列化
QByteArray jsonData = doc.toJson(QJsonDocument::Indented);
// 反序列化
QJsonParseError error;
QJsonDocument doc2 = QJsonDocument::fromJson(jsonData, &error);
if (error.error == QJsonParseError::NoError) {
QString name = doc2.object()["name"].toString();
}
// ====== XML(SAX 解析,适合大文件) ======
// QtXmlPatterns 或 QXmlStreamReader
QXmlStreamReader xml;
xml.setDevice(&file);
while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::StartElement) {
if (xml.name() == "user") {
QString id = xml.attributes().value("id").toString();
qDebug() << "User ID:" << id;
}
}
}
6. 进阶主题
6.1 Qt 测试框架(Qt Test)
// test_math.h
#pragma once
#include <QObject>
#include <QtTest/QtTest>
class TestMath : public QObject {
Q_OBJECT
private slots:
// 每个 private slot 都是一个测试函数
void testAddition();
void testDivision(); // 除零测试
void testStringList();
void testBenchmark(); // 性能基准测试
};
void TestMath::testAddition() {
int result = 2 + 3;
// 【注解】QCOMPARE 宏在不相等时打印详细错误
// 其他宏:QCOMPARE(actual, expected)、QVERIFY(condition)、QFAIL(message)
QCOMPARE(result, 5);
}
void TestMath::testDivision() {
// 【注解】QTest::ignoreMessage() 抑制预期中的警告输出
QTest::ignoreMessage(QtWarningMsg, "Division by zero");
double result = 1.0 / 0.0; // 结果为 inf
QBENCHMARK {
// 【注解】QBENCHMARK 块中的代码会被多次运行以测量性能
volatile double x = 1.0 / 3.0;
Q_UNUSED(x);
}
}
void TestMath::testStringList() {
QStringList list = {"apple", "banana", "cherry"};
QString joined = list.join(", ");
QCOMPARE(joined, "apple, banana, cherry");
}
运行测试:
# CMakeLists.txt 中添加测试
enable_testing()
find_package(Qt6 REQUIRED COMPONENTS Test)
add_executable(test_math test_math.cpp)
target_link_libraries(test_math PRIVATE Qt6::Test)
add_test(NAME test_math COMMAND test_math)
# 命令行运行
ctest --output-on-failure
# 或直接运行
./test_math
6.2 QML 与 3D(Qt Quick 3D)
Qt 6.6+ 的 Qt Quick 3D 提供了声明式 3D 场景:
import QtQuick
import QtQuick3D
View3D {
id: view3d
anchors.fill: parent
// 透视相机
PerspectiveCamera {
id: camera
position: Qt.vector3d(0, 2, 5)
lookAt: Qt.vector3d(0, 0, 0)
}
// 环境光
DirectionalLight {
eulerRotation.x: -30
ambientColor: "#404040"
}
// 3D 模型节点
Node {
// 旋转的立方体
Model {
source: "#Cube"
materials: DefaultMaterial {
diffuseColor: "#3498db"
}
eulerRotation.y: view3d.Runtime.doubleValue // 每帧旋转
}
// 地面(平面)
Model {
source: "#Rectangle"
scale: Qt.vector3d(10, 1, 10)
position.y: -1
materials: DefaultMaterial {
diffuseColor: "#2ecc71"
}
}
}
// 逐帧旋转动画
property double angle: 0
NumberAnimation on angle {
from: 0
to: 360
duration: 4000
loops: Animation.Infinite
running: true
}
onAngleChanged: {
// 旋转逻辑
}
}
6.3 Qt 与 Python 绑定(PySide6)
Qt 官方提供 Qt for Python(PySide6),与 C++ Qt API 完全对应:
# PySide6 示例(Python 3.10+)
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton
from PySide6.QtCore import Slot
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("PySide6 示例")
self.setFixedSize(400, 300)
btn = QPushButton("点击我", self)
btn.clicked.connect(self.on_click)
@Slot()
def on_click(self):
print("按钮被点击!")
if __name__ == "__main__":
app = QApplication()
window = MainWindow()
window.show()
app.exec()
注解:PySide6 与 PyQt5/PyQt6 功能几乎一致,但许可证不同——PySide6 是 LGPL(官方),PyQt6 是 GPL/Commercial。PySide6 由 Qt Company 官方维护,API 命名更接近 C++ Qt。
7. 优质项目推荐与学习资源
7.1 官方示例项目
| 项目名 | 地址 | 说明 |
|---|---|---|
| Qt 官方示例集 | https://doc.qt.io/qt6/examples.html | 按模块分类的官方示例,代码质量最高 |
| Qt GitHub Examples | https://github.com/qt/qtbase/tree/dev/examples | Qt 源码仓中的示例 |
| Qt Marketplace | https://marketplace.qt.io/ | Qt 官方组件市场 |
7.2 知名开源 Qt 项目(真实可参考)
| 项目 | 地址 | 技术亮点 |
|---|---|---|
| OBS Studio | https://github.com/obsproject/obs-studio | 直播/录屏软件,Qt GUI + FFmpeg,结构清晰 |
| Qt Creator | https://github.com/qt-creator/qt-creator | Qt IDE 本身,QtWidgets 典范 |
| Wireshark | https://gitlab.com/wireshark/wireshark | 网络协议分析,跨平台 UI |
| VirtualBox | https://www.virtualbox.org/wiki/Source_code_Download | 虚拟机软件,复杂 Qt GUI |
| KDE 桌面环境 | https://invent.kde.org/ | 大量 Qt 应用实践 |
| Tiled | https://github.com/mapeditor/tiled | 2D 瓦片地图编辑器,Qt Widgets 经典 |
| Sigma File Manager | https://github.com/beta-team/sigma | 现代 Windows 文件管理器,Qt Widgets |
| CuteTorrent | https://github.com/NicoAICP/CuteTorrent | BT 下载客户端 |
| MuseScore | https://github.com/musescore/MuseScore | 乐谱软件,跨平台 |
| RNote | https://github.com/flxzt/rnote | 手写笔记应用,Qt + Rust |
7.3 Qt Creator 插件开发
| 项目 | 地址 | 说明 |
|---|---|---|
| FakeVim | Qt Creator 内置 | Vim 模拟插件,插件开发参考 |
| Qt Creator 插件开发文档 | https://doc.qt.io/qtcreator/creator-plugin-howto.html | 官方教程 |
| C++ Counter | https://github.com/qt-creator/counter-texteditor | 简单插件示例 |
7.4 学习资料推荐
| 类型 | 资源 | 说明 |
|---|---|---|
| 官方文档 | https://doc.qt.io/qt6/ | 最权威的参考资料 |
| 书籍(英文) | Advanced Qt Programming — Johan Thelin | Qt 中高级开发者必读 |
| 书籍(中文) | Qt Creator 快速入门(第4版) — 霍永彪 | 中文入门首选 |
| 在线课程 | Bilibili Qt 开发系列教程 | 中文视频教程丰富 |
| 社区 | https://forum.qt.io/ | Qt 官方论坛 |
| Stack Overflow | [qt] [qt5] [qt6] 标签 | 问题搜索首选 |
| Qt Chinese 社区 | https://www.qtcn.org/ | 国内老牌 Qt 社区 |
7.5 工具链与辅助库
| 工具/库 | 地址 | 说明 |
|---|---|---|
| Qt Designer | 内置 | 可视化 UI 设计器 |
| Qt Linguist | 内置 | 翻译编辑工具 |
| qmake | 内置 | Qt 构建工具 |
| CMake | https://cmake.org | 跨平台构建系统(Qt6 首选) |
| vcpkg | https://github.com/microsoft/vcpkg | C++ 包管理器(可安装 Qt) |
| Conan | https://conan.io | 另一个 C++ 包管理器 |
| Qwt | https://qwt.sourceforge.io/ | 工业图表/控件库 |
| KDAB Tools | https://github.com/KDAB/KDAB-with-CMake | KDAB CMake 最佳实践 |
| QtWebView | 内置 | 内嵌浏览器控件 |
| QtWebEngine | 内置 | Chromium 内核浏览器(QtWebEngine) |
8. 常见问题与调试技巧
8.1 常见编译错误
错误1:undefined reference to vtable for xxx
原因:类中声明了 Q_OBJECT 但未正确运行 MOC(元对象编译器)
解决:
1. 确认 CMakeLists.txt 中 set(CMAKE_AUTOMOC ON)
2. 确认类的 .cpp 文件被包含在 add_executable 中
3. 清理构建目录重新构建(cmake --build . --clean-first)
错误2:MSVC 编译报错字符编码
原因:源文件编码与编译器编码不匹配(常见于中文 Windows)
解决:
1. CMakeLists.txt 添加:add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
2. 或在 .pro 中添加:msvc:QMAKE_CXXFLAGS += /utf-8
3. 建议将源文件保存为 UTF-8(带 BOM)或 GBK
错误3:QSqlDatabase: driver not loaded
原因:SQL 驱动插件未找到
解决:
1. 确保 Qt 的 sqldrivers 目录(plugins/sqldrivers/)在运行目录
2. 或手动复制对应驱动 DLL(如 qsqlmysql.dll)到可执行文件目录
3. 使用 QSqlDatabase::drivers() 检查可用的驱动
错误4:QWidget: Must construct a QApplication first
原因:在 QCoreApplication 或 QGuiApplication 未创建时创建了 QWidget
解决:确保 main() 中 QApplication 在任何 QWidget 实例化之前构造
8.2 调试技巧
1. qDebug() — 条件日志
// 条件编译:发布版关闭调试输出
// .pro 中:DEFINES += QT_NO_DEBUG_OUTPUT
qDebug() << "Variable value:" << value;
qDebug() << "Object name:" << sender()->objectName();
// 分类日志
qWarning() << "警告信息";
qCritical() << "严重错误";
qFatal() << "致命错误(会终止程序)"; // Qt 6 已废弃,改为 qFatal() + abort()
// 自定义日志类别(Qt 5.2+)
QLoggingCategory myCategory("com.myapp.network");
qCWarning(myCategory) << "网络异常";
// 输出到文件
// main.cpp 中添加:
void customMessageHandler(QtMsgType type, const QMessageLogContext& context,
// const QString& msg) {
// QFile file("app.log");
// file.open(QIODevice::WriteOnly | QIODevice::Append);
// QTextStream ts(&file);
// ts << QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss");
// ts << " [" << context.category << "] " << msg << "\n";
//}
// qInstallMessageHandler(customMessageHandler);
2. Qt Creator 调试器使用
F5 — 开始调试
F9 — 设置/取消断点
F10 — 单步跳过
F11 — 单步进入
Shift+F11 — 单步跳出
断点窗口 — 查看/编辑断点(条件断点、日志断点)
监视窗口 — 添加表达式实时监视变量值
本地变量窗口 — 显示当前栈帧的所有局部变量
3. 内存泄漏检测
// Qt Creator 的 AddressSanitizer / Valgrind(Linux)
// Windows 上使用 Visual Studio 的诊断工具
// Qt 自带的内存追踪(仅调试模式)
// 在 .pro 中:QT += core_private
// 使用 QMemDump 检查内存
#include <QDebug>
#include <QCoreApplication>
// 【注解】调试模式下 Qt 自动追踪未删除的 QObject
// 程序退出时输出警告(如果有 QObject 子对象未被 delete)
4. 性能分析
// QElapsedTimer — 精确计时
QElapsedTimer timer;
timer.start();
someFunction();
qDebug() << "耗时:" << timer.elapsed() << "ms";
// Qt Creator 性能分析工具
// 帮助 → 关于 Qt Creator → 插件 → 启用 Qt Creator 性能分析插件
// 然后:分析 → CPU Usage Analyzer
9. 附录
9.1 Qt 6 新特性速查
| 特性 | 说明 |
|---|---|
| QAnyStringView | 统一的字符串视图类型 |
| QtPro门前(替代 QDesktopServices) | 更好的平台集成 |
| QList 优化 | Qt 6 中 QList 与 QVector 合并,性能改进 |
| qsizetype | 统一使用 qsizetype 而非 int/long 表示大小 |
| 属性系统改进 | 支持更多 C++20 特性 |
| QtHttpServer | 内置 HTTP/1.1 服务器 |
| Qt Graphs | 新增高性能图表模块 |
| Qt Cooperative Cache | 新的缓存模块 |
| QConsoleAppender | 日志输出到控制台 |
9.2 Qt 信号槽 vs 其他框架对比
| 对比维度 | Qt 信号槽 | C++ std::function + callback | C# event/delegate |
|---|---|---|---|
| 编译期检查 | 旧宏无,新式有 | 有(模板) | 有 |
| 跨线程通信 | 自动(QueuedConnection) | 需手动实现 | 需 Invoke |
| 语法复杂度 | 中 | 低 | 低 |
| 断点调试支持 | 有限 | 无 | 有 |
| 运行时连接 | 支持(按信号名) | 需额外框架 | 支持 |
| 依赖 | Qt 元对象系统 | 仅标准库 | 仅 .NET |
9.3 推荐开发习惯
1. 始终使用 qmake / CMake 配置文件管理项目,避免手动敲编译命令
2. 所有包含 Q_OBJECT 的类放到单独的 .h/.cpp 文件,不要内联
3. UI 设计与业务逻辑分离:UI 类只负责界面,逻辑放到独立类
4. 善用 QVariant 在 Qt 类型系统间传递数据
5. 大数据量使用 Model/View 架构,而非在 Widget 里直接塞数据
6. 多线程永远用 QThread + Worker 对象模式,不用继承 QThread 重写 run()
7. 使用智能指针(QSharedPointer / std::shared_ptr)管理 QObject 父子关系
8. 网络请求始终设置超时,防止程序卡死
9. 使用 QSettings 存储配置,跨平台兼容
10. 重要代码路径写单元测试(Qt Test),持续集成
9.4 Qt 6 最低环境要求
| 平台 | 最低要求 |
|---|---|
| Windows | Windows 10 1809+(Qt 6.5+ 推荐 Windows 11) |
| macOS | macOS 10.15 (Catalina)+ |
| Linux | GCC 9 / Clang 12+,GLIBC 2.28+ |
| Android | Android API 21+(Qt 6.5+ 推荐 API 24) |
| iOS | iOS 13+ |
| 编译器 | MSVC 2019 16.8+、GCC 11+、Clang 13+ |
| Qt 官方文档:https://doc.qt.io/qt6/ | Qt GitHub:https://github.com/qt-project |







