Qt Widgets 架构与开发入门第一章-第三章

Qt Widgets 架构与开发入门


前言

面向 C++ 初学者的 Qt Widgets 学习指南。示例基于 Qt 6,大部分 API 在 Qt 5.15 也可用,差异之处会额外标注。


第 1 章 认识 Qt

1.1 Qt 是什么

Qt 是一个成熟的 跨平台 C++ 应用框架,一次编写、多平台编译运行(Windows、macOS、Linux、Android、iOS、嵌入式)。它不仅提供 GUI 组件,还包含网络、数据库、多媒体、并发、序列化等一整套基础设施,可以理解为「C++ 版的标准 SDK」。

Qt 的两大 UI 技术栈:

  • Qt Widgets:传统的桌面控件体系,基于 C++ 类继承,像 QPushButtonQTableView。适合复杂的、以数据展示为主的桌面应用。
  • Qt Quick / QML:声明式 UI + JavaScript,适合动画丰富、触控友好的现代界面。

本文档只讲 Qt Widgets

1.2 Qt Widgets vs Qt Quick

维度Qt WidgetsQt Quick
定位传统桌面现代 UI、移动、嵌入式
语言C++QML(声明式) + JS + C++
渲染CPU 光栅化GPU 加速
动画一般出色
数据密集型表格
学习曲线平缓(接近传统 GUI)需要额外学 QML

经验法则:数据密集、以表单/表格为主的工具型软件选 Widgets;强调视觉、动画、跨触屏的产品选 Quick。

1.3 Qt 模块划分

Qt 是模块化设计,常用模块:

  • Qt Core:非 GUI 基础设施,QObject、容器、字符串、文件、时间、事件循环。
  • Qt Gui:窗口系统抽象、事件、绘图底层(QPainterQImage)。
  • Qt Widgets:传统控件、布局、Model/View。
  • Qt Network:TCP/UDP、HTTP、SSL。
  • Qt Sql:数据库访问。
  • Qt Concurrent:高层并发工具。
  • Qt Test:单元测试。

在 CMake 中通过 find_package(Qt6 REQUIRED COMPONENTS Widgets) 按需引入。

1.4 版本与许可

  • Qt 目前主流版本是 Qt 6(推荐新项目)与 Qt 5.15 LTS(存量项目)。
  • 许可有两种:LGPLv3(开源,可动态链接商用,但需遵守 LGPL 义务)与 商业许可(适合静态链接或需规避 LGPL 的产品)。
  • 本文示例默认 Qt 6.5+。

第 2 章 开发环境搭建

2.1 安装 Qt

推荐 Qt Online Installer(需注册 Qt 账号):

  1. qt.io/download 下载安装器。
  2. 选择组件时至少勾选:
    • 一个 Qt 版本(如 6.7.x)
    • 对应平台的编译器套件(Windows 建议 MSVC 2019/2022 64-bit,或 MinGW)
    • Qt Creator(IDE)
    • CMakeNinja(通常自动带上)
  3. 磁盘至少预留 20 GB。

2.2 Qt Creator 界面速览

  • 欢迎(Welcome):项目模板、示例、教程入口。
  • 编辑(Edit):代码编辑器。
  • 设计(Design):打开 .ui 文件时的可视化设计器。
  • 调试(Debug):断点、变量查看、GDB/LLDB/CDB 集成。
  • 项目(Projects):配置构建套件(Kit)、编译选项、运行环境。

第一次打开时进入 Preferences → Kits,确认至少有一个绿色勾的 Kit(Qt 版本 + 编译器 + 调试器齐全)。

2.3 编译工具链

  • Windows:MSVC(与 Visual Studio 一致的 ABI,推荐用于发布产品)或 MinGW-w64(免装 VS,适合个人学习)。
  • macOS:自带 Clang。
  • Linux:发行版自带 GCC/Clang。

⚠️ Qt 库和你的应用必须使用 相同编译器/ABI 构建,否则会出现符号找不到、STL 不兼容等错误。

2.4 第一个项目:Hello, Qt Widgets

在 Qt Creator 中:File → New Project → Application (Qt) → Qt Widgets Application,选中 CMake 构建。

生成的 main.cpp 类似:

#include <QApplication>
#include <QLabel>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QLabel label("Hello, Qt Widgets");
    label.resize(300, 100);
    label.show();
    return app.exec();
}

关键三步:

  1. 创建 QApplication —— 每个 GUI 程序有且只有一个。
  2. 创建并 show() 一个顶层 widget。
  3. 调用 app.exec() 进入事件循环。

2.5 qmake 与 CMake

Qt 历史上用 qmake(.pro 文件),Qt 6 起 官方推荐 CMake。最小 CMake 示例:

cmake_minimum_required(VERSION 3.16)
project(HelloQt LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_AUTOMOC ON)   # 自动运行 MOC
set(CMAKE_AUTOUIC ON)   # 自动处理 .ui
set(CMAKE_AUTORCC ON)   # 自动处理 .qrc

find_package(Qt6 REQUIRED COMPONENTS Widgets)

add_executable(HelloQt main.cpp)
target_link_libraries(HelloQt PRIVATE Qt6::Widgets)

三个 AUTO* 开关帮你省去手动调用 moc/uic/rcc 的麻烦,新项目建议一律打开。


第 3 章 核心机制

Qt Widgets 的所有 API 都建立在几个核心机制之上。理解它们比记 API 更重要。

3.1 对象树与内存管理

Qt 中所有继承自 QObject 的对象可以构成 父子树。规则:

  • 构造时传入父对象:new QPushButton(&parentWidget);
  • 父对象析构时,自动 delete 所有子对象。
  • 因此 有父对象的 QObject 无需手动 delete,也不应放进 std::unique_ptr
auto *window = new QWidget;          // 顶层,无父对象
auto *btn = new QPushButton("OK", window); // 挂到 window 下
window->show();
// 程序退出、window 被 delete 时,btn 自动 delete

常见陷阱:栈上创建的 QObject 又设置了父对象,会 double free。

3.2 元对象系统(MOC)

Qt 用 MOC(Meta-Object Compiler) 给 C++ 加了三样东西:

  • 运行时反射(className()property())
  • 信号槽机制
  • 动态 qobject_cast

要让一个类支持这些能力:

class MyWidget : public QWidget
{
    Q_OBJECT   // 必写!否则信号槽用不了
public:
    explicit MyWidget(QWidget *parent = nullptr);
};

MOC 会在编译期扫描带 Q_OBJECT 的头文件,生成 moc_*.cpp 文件。用 CMake AUTOMOC ON 时全自动。

3.3 信号与槽

信号槽是 Qt 的招牌。它是一种类型安全的观察者模式:某个对象发出信号(signal),连接到它的槽函数(slot)会被调用。

class Counter : public QObject
{
    Q_OBJECT
public:
    int value() const { return m_value; }
public slots:
    void increment() {
        ++m_value;
        emit valueChanged(m_value);
    }
signals:
    void valueChanged(int newValue);
private:
    int m_value = 0;
};

连接方式(推荐现代语法):

Counter a, b;
QObject::connect(&a, &Counter::valueChanged,
                 &b, &Counter::increment);

// 也可以连到 lambda
QObject::connect(&a, &Counter::valueChanged, [](int v) {
    qDebug() << "value =" << v;
});

旧语法(字符串宏,不推荐):

connect(&a, SIGNAL(valueChanged(int)), &b, SLOT(increment()));

旧语法只在运行时检查,现代语法编译期检查,IDE 还能跳转和重构。

连接类型(Qt::ConnectionType):

  • Qt::AutoConnection(默认):同线程 = 直连;跨线程 = 队列连接。
  • Qt::DirectConnection:立即调用槽(在发送者线程)。
  • Qt::QueuedConnection:投递到接收者线程的事件队列,异步执行。
  • Qt::BlockingQueuedConnection:跨线程且阻塞等待返回(小心死锁)。

3.4 事件循环

QApplication::exec() 启动的就是主事件循环。它做三件事:

  1. 从操作系统收事件(鼠标、键盘、绘制、定时器)。
  2. 分发给对应的 QObject(通过 event() 方法)。
  3. 处理跨线程投递的槽调用(队列连接)。

主线程绝不能阻塞事件循环,否则界面会假死。耗时任务应放到线程或 QtConcurrent(第 9 章)。

QCoreApplication::processEvents() 可以在长任务中手动泵一次事件,让 UI 响应,但通常是设计不佳的信号,慎用。

3.5 属性系统

Q_PROPERTY 声明一个可反射、可绑定到 QSS/动画的属性:

class Star : public QWidget
{
    Q_OBJECT
    Q_PROPERTY(int rating READ rating WRITE setRating NOTIFY ratingChanged)
public:
    int rating() const { return m_rating; }
    void setRating(int r) {
        if (r == m_rating) return;
        m_rating = r;
        emit ratingChanged(r);
    }
signals:
    void ratingChanged(int);
private:
    int m_rating = 0;
};

好处:

  • 可以被 QPropertyAnimation 直接动画化。
  • 可以在 QSS 里通过属性选择器匹配。
  • 支持 setProperty("rating", 5) 动态设置。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值