C++ Qt 开发完全指南

文档版本:v1.0 | 更新日期:2026-06-05
Qt 版本:Qt 6.x(LTS 长期支持版)| C++ 标准:C++17 / C++20
本文档涵盖 Qt 开发全流程:环境搭建、核心概念、常用技术栈、代码范式、优质项目推荐,以及详细的注解说明。


目录

  1. Qt 开发概述
  2. 开发环境搭建
  3. Qt 项目工程结构
  4. 核心模块与常用技术
  5. 常用代码范式与示例
  6. 进阶主题
  7. 优质项目推荐与学习资源
  8. 常见问题与调试技巧
  9. 附录

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 官方在线安装器 安装。

安装步骤

  1. 注册 Qt 账号(免费)
  2. 运行安装器,选择组件时注意:
    • 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 源码,调试时需要)
  3. 验证安装:打开 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_cppCMAKE_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 的某个按钮的 objectNamebtnSave,则存在 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