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 模型
#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
提供了函数来检测项之间的碰撞。 - 动画: 可以与
QPropertyAnimation
或QAnimationGroup
结合,轻松为图形项添加动画效果。 - OpenGL 加速:
QGraphicsView
可以使用setViewport(new QOpenGLWidget)
来启用 OpenGL 渲染后端,极大提升大量图形项的渲染性能。
简单示例
#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
到这个新线程中。 - 通过 信号和槽 来触发工作对象中的函数。这些槽函数将在新线程中执行。
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); // 线程结束时清理 workerthread->start();
3. Qt Concurrent (高级 API)
这是一个更高级的框架,用于并行处理算法,类似于 C++ 的 <future>
和 <thread>
。
QtConcurrent::run()
: 在一个单独的线程中运行一个函数。QtConcurrent::map()
,filter()
,filteredReduced()
: 用于并行处理容器中的元素。
// 在一个线程中运行一个普通函数
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: 使用
QNetworkAccessManager
、QNetworkRequest
和QNetworkReply
可以轻松实现 HTTP 通信。它是异步的,基于信号和槽。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: 使用
QTcpSocket
和QTcpServer
实现可靠的流式通信(如自定义协议)。 - UDP: 使用
QUdpSocket
实现无连接的数据报通信(如广播、视频流)。
- TCP: 使用
-
WebSockets:
QWebSocket
提供了对 WebSocket 协议(RFC 6455)的完整支持,非常适合需要全双工、实时通信的应用。
5. QML 与 C++ 混合编程
Qt Quick (QML) 是用于构建现代、动感用户界面的声明式语言。将 QML 的前端优势与 C++ 的后端性能和控制力结合是 Qt 开发的高级模式。
核心交互方式
-
将 C++ 对象暴露给 QML:
// MyClass.h - 使用 Q_PROPERTY, Q_INVOKABLE 等导出 class MyClass : public QObject {Q_OBJECTQ_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() }
-
在 C++ 中创建和管理 QML 组件: 使用
QQmlComponent
或QQuickView
来加载和实例化 QML 文件,并与之交互。 -
在 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、动态交互界面 |