Qt5 高级功能

个人博客:blogs.wurp.top

1. 模型/视图架构 (Model/View Architecture)

这是 Qt 中处理数据与显示分离的核心框架,是理解高级 Qt 开发的关键。

核心概念

  • 模型 (Model) : 继承自 QAbstractItemModel 的类。负责管理数据(如一个列表、一个数据库表),并提供统一的接口供视图和委托访问数据。它不关心数据如何显示。
  • 视图 (View) : 继承自 QAbstractItemView 的类(如 QListView, QTableView, QTreeView)。负责将模型中的数据展示给用户,并处理用户的操作(如点击、滚动)。
  • 委托 (Delegate) : 继承自 QAbstractItemDelegate 的类。负责渲染视图中的每个项(item),并在编辑时为项提供编辑器(如一个输入框、一个下拉菜单)。

为什么使用 M/V?

  • 分离关注点: 数据管理和用户界面分离,代码更清晰、更易维护。
  • 一个模型,多个视图 : 你可以让一个 QTableView 和一个 QListView 同时显示同一个模型的数据。在一个视图中修改数据,另一个视图会自动更新。
  • 自定义显示和编辑: 通过自定义委托,你可以完全控制每个单元格的显示和编辑方式(例如,为进度条数据绘制一个实际的进度条控件)。

示例:自定义一个简单的 Table 模型

cpp 复制代码
#include <QApplication>
#include <QTableView>
#include <QStandardItemModel>

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    // 1. 创建模型并设置数据 (这里使用便捷的 QStandardItemModel)
    QStandardItemModel model(4, 2); // 4行2列

    for (int row = 0; row < 4; ++row) {
        for (int col = 0; col < 2; ++col) {
            QStandardItem *item = new QStandardItem(QString("Row %1, Col %2").arg(row).arg(col));
            model.setItem(row, col, item);
        }
    }

    // 2. 创建视图并设置模型
    QTableView tableView;
    tableView.setModel(&model);
    tableView.show();

    return app.exec();
}

更高级的用法 : 你可以继承 QAbstractTableModel,重写 rowCount(), columnCount(), data(), 和 setData() 等函数,来封装你自己的数据结构(如一个自定义的容器或一个数据库连接)。


2. 图形视图框架 (Graphics View Framework)

这是一个用于管理和交互大量自定义 2D 图形项的系统。它非常适合开发绘图软件、图表、数据可视化、游戏地图编辑器等。

核心类

  • QGraphicsScene : 场景。它是一个容器,管理所有的图形项(QGraphicsItem)。负责处理事件传播、项之间的碰撞检测等。
  • QGraphicsItem : 图形项。这是你要在屏幕上显示的对象的基类(如椭圆、矩形、自定义图形)。Qt 提供了许多内置项(QGraphicsEllipseItem, QGraphicsTextItem 等),你也可以通过重写 paint()boundingRect() 来自定义项。
  • QGraphicsView: 视图窗口部件。用于将场景的内容可视化并提供观察视口。你可以有多个 View 来观察同一个 Scene,支持缩放和旋转。

高级特性

  • 坐标系系统 : 项有场景坐标、自身坐标和父项坐标。支持复杂的变换(QTransform)。
  • 碰撞检测QGraphicsScene 提供了函数来检测项之间的碰撞。
  • 动画 : 可以与 QPropertyAnimationQAnimationGroup 结合,轻松为图形项添加动画效果。
  • OpenGL 加速QGraphicsView 可以使用 setViewport(new QOpenGLWidget) 来启用 OpenGL 渲染后端,极大提升大量图形项的渲染性能。

简单示例

cpp 复制代码
#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    // 创建场景
    QGraphicsScene scene;
    scene.setSceneRect(0, 0, 400, 300); // 设置场景范围

    // 创建一个矩形图形项并添加到场景
    QGraphicsRectItem *rect = new QGraphicsRectItem(0, 0, 100, 50);
    rect->setPos(50, 50);
    rect->setBrush(Qt::blue);
    scene.addItem(rect);

    // 创建一个视图来显示场景
    QGraphicsView view(&scene);
    view.show();

    return app.exec();
}

3. 多线程与并发 (Multithreading & Concurrent)

Qt 提供了多种方式来处理多线程,从低级到高级,满足不同需求。

1. QThread (低级 API)

传统方式是继承 QThread 并重写 run() 方法。但官方现在更推荐使用以下方法

2. 移动对象到线程 (推荐)

这是 Qt 中最常用和优雅的多线程方法。原理是:

  • 创建一个工作类(Worker),它包含你的耗时操作。这个类继承自 QObject
  • 创建一个 QThread 对象。
  • Worker 对象 moveToThread 到这个新线程中。
  • 通过 信号和槽 来触发工作对象中的函数。这些槽函数将在新线程中执行。
cpp 复制代码
class Worker : public QObject {
    Q_OBJECT
public slots:
    void doWork(const QString ¶meter) {
        // ... 这里是耗时的操作 ...
        emit resultReady(result);
    }
signals:
    void resultReady(const QString &result);
};

// 在主线程中
QThread *thread = new QThread;
Worker *worker = new Worker;
worker->moveToThread(thread);

// 连接信号和槽
connect(ui->startButton, &QPushButton::clicked, [=](){
    // 这个 Lambda 在主线程,它发射信号到 worker 所在的线程
    emit startWork("Task Data"); 
});
connect(worker, &Worker::resultReady, this, &MainWindow::handleResults);
connect(thread, &QThread::finished, worker, &QObject::deleteLater); // 线程结束时清理 worker

thread->start();

3. Qt Concurrent (高级 API)

这是一个更高级的框架,用于并行处理算法,类似于 C++ 的 <future><thread>

  • QtConcurrent::run(): 在一个单独的线程中运行一个函数。
  • QtConcurrent::map(), filter(), filteredReduced(): 用于并行处理容器中的元素。
cpp 复制代码
// 在一个线程中运行一个普通函数
QFuture<void> future = QtConcurrent::run([](){
    // 在另一个线程中执行的代码
});

// 并行处理一个 QList
QList<int> list = {1, 2, 3, 4, 5};
QFuture<void> future = QtConcurrent::map(list, [](int &value) {
    value *= 2; // 将列表中的每个元素乘以2
});
future.waitForFinished(); // 等待操作完成
// 现在 list 是 {2, 4, 6, 8, 10}

4. 网络编程 (Networking)

Qt Network 模块提供了丰富的网络功能。

  • HTTP/HTTPS : 使用 QNetworkAccessManagerQNetworkRequestQNetworkReply 可以轻松实现 HTTP 通信。它是异步的,基于信号和槽。

    cpp 复制代码
    QNetworkAccessManager *manager = new QNetworkAccessManager(this);
    QNetworkRequest request(QUrl("https://www.example.com/api/data"));
    QNetworkReply *reply = manager->get(request);
    
    connect(reply, &QNetworkReply::finished, [=]() {
        if (reply->error() == QNetworkReply::NoError) {
            QByteArray data = reply->readAll();
            // 处理数据
        }
        reply->deleteLater();
    });
  • TCP/UDP

    • TCP : 使用 QTcpSocketQTcpServer 实现可靠的流式通信(如自定义协议)。
    • UDP : 使用 QUdpSocket 实现无连接的数据报通信(如广播、视频流)。
  • WebSocketsQWebSocket 提供了对 WebSocket 协议(RFC 6455)的完整支持,非常适合需要全双工、实时通信的应用。


5. QML 与 C++ 混合编程

Qt Quick (QML) 是用于构建现代、动感用户界面的声明式语言。将 QML 的前端优势与 C++ 的后端性能和控制力结合是 Qt 开发的高级模式。

核心交互方式

  1. 将 C++ 对象暴露给 QML

    cpp 复制代码
    // MyClass.h - 使用 Q_PROPERTY, Q_INVOKABLE 等导出
    class MyClass : public QObject {
        Q_OBJECT
        Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
    public:
        Q_INVOKABLE void doSomething();
        // ...
    };
    
    // main.cpp
    MyClass myObj;
    QQuickView view;
    view.engine()->rootContext()->setContextProperty("myObject", &myObj);
    view.setSource(QUrl("qrc:/main.qml"));
    view.show();
    
    // 在 QML 中直接使用
    // Text { text: myObject.name }
    // Button { onClicked: myObject.doSomething() }
  2. 在 C++ 中创建和管理 QML 组件 : 使用 QQmlComponentQQuickView 来加载和实例化 QML 文件,并与之交互。

  3. 在 QML 中调用 C++ 函数 : 使用 Q_INVOKABLE 标记的函数可以直接在 QML 中调用。


6. 其它重要高级主题

  • 自定义控件 (Custom Widgets) : 通过重写 paintEvent(), mousePressEvent() 等事件,你可以完全自定义一个独一无二的控件。
  • 事件系统 (Event System) : 理解事件过滤 (installEventFilter) 和事件处理 (event()) 是进行高级界面交互和自定义控件开发的基础。
  • 样式表 (Qt Style Sheets - QSS): 使用类似 CSS 的语法来美化应用程序的界面,这是实现应用程序换肤功能的基石。
  • 国际化 (Internationalization - i18n) : 使用 tr() 宏标记需要翻译的字符串,配合 Qt Linguist 工具,可以轻松实现多语言支持。
  • 插件系统 (Plugin System): 允许你通过插件来扩展应用程序的功能。Qt 自身就是通过插件来支持不同的数据库、图像格式等。

总结

功能模块 核心思想 典型应用场景
模型/视图 数据与显示分离,解耦 表格、列表、树形数据展示
图形视图 管理大量2D图形项 绘图软件、CAD、数据可视化、游戏编辑器
多线程 移动对象到线程,信号槽通信 后台耗时任务(计算、IO),保持UI响应
网络 QNetworkAccessManager 异步请求 HTTP API 调用、文件下载、TCP/UDP 通信
QML/C++ 声明式UI + 高性能逻辑 现代移动端/嵌入式UI、动态交互界面