QT--模型/视图
在 Qt 中,模型/视图架构(Model/View Architecture) 是一种用于分离数据和用户界面的设计模式。它将数据的存储(模型)与数据的显示(视图)分开,从而提高了代码的可维护性和灵活性。模型/视图架构广泛应用于 Qt 的 GUI 开发中,尤其是在处理表格、列表、树等复杂数据结构时。
1. 模型/视图架构的核心概念
1.1 模型(Model)
- 模型负责管理数据,并提供接口供视图和委托(Delegate)访问数据。
- 模型通过信号和槽机制通知视图数据的变化。
- Qt 提供了多种内置模型,如
QStandardItemModel
、QStringListModel
、QSqlTableModel
等。
1.2 视图(View)
- 视图负责显示模型中的数据,并提供用户交互功能(如选择、排序等)。
- 视图从模型中获取数据,并将其显示在用户界面上。
- Qt 提供了多种内置视图,如
QListView
、QTableView
、QTreeView
等。
1.3 委托(Delegate)
- 委托负责数据的编辑和显示样式。
- 委托可以自定义单元格的显示方式和编辑行为。
- Qt 提供了默认的委托
QStyledItemDelegate
,也可以自定义委托。
2. 模型/视图架构的类层次结构
2.1 模型类
QAbstractItemModel
:所有模型的基类。QStandardItemModel
:通用的模型,适合存储任意结构的数据。QStringListModel
:用于存储字符串列表的简单模型。QFileSystemModel
:用于显示文件系统的模型。QSqlTableModel
:用于与数据库交互的模型。
2.2 视图类
QAbstractItemView
:所有视图的基类。QListView
:用于显示列表数据的视图。QTableView
:用于显示表格数据的视图。QTreeView
:用于显示树形数据的视图。
2.3 委托类
QAbstractItemDelegate
:所有委托的基类。QStyledItemDelegate
:默认的委托,支持样式化和编辑。QItemDelegate
:功能与QStyledItemDelegate
类似,但更轻量级。
3. 模型/视图的基本使用
以下是一个简单的示例,展示如何使用 QStringListModel
和 QListView
来显示字符串列表。
3.1 使用 QStringListModel
和 QListView
#include <QApplication>
#include <QListView>
#include <QStringListModel>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// 创建模型
QStringListModel model;
QStringList list;
list << "Apple" << "Banana" << "Cherry" << "Date";
model.setStringList(list); // 设置数据到模型
// 创建视图
QListView view;
view.setModel(&model); // 将模型绑定到视图
view.setWindowTitle("QListView Example");
view.resize(200, 300);
view.show();
return app.exec();
}
3.2 运行效果
- 视图会显示一个列表,内容为
Apple
、Banana
、Cherry
和Date
。 - 如果修改模型中的数据,视图会自动更新。
4. 自定义模型
如果内置模型无法满足需求,可以继承 QAbstractItemModel
或其子类来实现自定义模型。
4.1 自定义模型的示例
以下是一个简单的自定义模型,用于存储和显示一个二维表格数据。
#include <QAbstractTableModel>
#include <QDebug>
class MyTableModel : public QAbstractTableModel {
Q_OBJECT
public:
MyTableModel(QObject *parent = nullptr) : QAbstractTableModel(parent) {
// 初始化数据
for (int row = 0; row < 3; ++row) {
QVector<QString> rowData;
for (int col = 0; col < 3; ++col) {
rowData.append(QString("(%1,%2)").arg(row).arg(col));
}
m_data.append(rowData);
}
}
// 返回行数
int rowCount(const QModelIndex &parent = QModelIndex()) const override {
return m_data.size();
}
// 返回列数
int columnCount(const QModelIndex &parent = QModelIndex()) const override {
return m_data.isEmpty() ? 0 : m_data.first().size();
}
// 返回数据
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override {
if (!index.isValid())
return QVariant();
if (role == Qt::DisplayRole) {
return m_data.at(index.row()).at(index.column());
}
return QVariant();
}
private:
QList<QVector<QString>> m_data; // 存储数据的二维列表
};
4.2 使用自定义模型
#include <QApplication>
#include <QTableView>
#include "MyTableModel.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// 创建自定义模型
MyTableModel model;
// 创建视图
QTableView view;
view.setModel(&model); // 将模型绑定到视图
view.setWindowTitle("Custom Table Model Example");
view.resize(300, 200);
view.show();
return app.exec();
}
4.3 运行效果
- 视图会显示一个 3x3 的表格,内容为
(0,0)
、(0,1)
、(0,2)
等。
5. 自定义委托
如果需要自定义单元格的显示或编辑行为,可以继承 QStyledItemDelegate
实现自定义委托。
5.1 自定义委托的示例
以下是一个简单的自定义委托,用于在单元格中显示红色背景的文本。
#include <QStyledItemDelegate>
#include <QPainter>
class MyDelegate : public QStyledItemDelegate {
Q_OBJECT
public:
MyDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) {}
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override {
if (index.column() == 1) { // 仅对第二列应用自定义样式
painter->fillRect(option.rect, Qt::red); // 设置红色背景
painter->drawText(option.rect, Qt::AlignCenter, index.data().toString()); // 绘制文本
} else {
QStyledItemDelegate::paint(painter, option, index); // 使用默认样式
}
}
};
5.2 使用自定义委托
#include <QApplication>
#include <QTableView>
#include "MyTableModel.h"
#include "MyDelegate.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// 创建自定义模型
MyTableModel model;
// 创建视图
QTableView view;
view.setModel(&model);
view.setItemDelegate(new MyDelegate(&view)); // 设置自定义委托
view.setWindowTitle("Custom Delegate Example");
view.resize(300, 200);
view.show();
return app.exec();
}
5.3 运行效果
- 视图会显示一个 3x3 的表格,其中第二列的单元格背景为红色,文本居中显示。
6. 模型/视图架构的优势
6.1 分离数据与显示
- 模型负责管理数据,视图负责显示数据,两者解耦,便于维护和扩展。
- 例如,可以通过不同的视图(如
QTableView
或QTreeView
)显示相同的数据模型。
6.2 数据驱动的界面
- 模型通过信号和槽机制通知视图数据的变化,视图会自动更新。
- 例如,修改模型中的数据后,视图会立即反映变化。
6.3 灵活性和可扩展性
- 可以轻松实现自定义模型、视图和委托,满足复杂的业务需求。
- 例如,可以自定义模型来处理数据库数据,或自定义委托来实现复杂的单元格显示。
7. 模型/视图架构的常见应用场景
7.1 表格数据展示
- 使用
QTableView
和QStandardItemModel
展示二维表格数据。 - 例如,展示 Excel 表格或数据库查询结果。
7.2 列表数据展示
- 使用
QListView
和QStringListModel
展示一维列表数据。 - 例如,展示文件列表或配置项。
7.3 树形数据展示
- 使用
QTreeView
和QStandardItemModel
展示树形结构数据。 - 例如,展示文件系统或组织结构。
7.4 数据库交互
- 使用
QSqlTableModel
或QSqlQueryModel
与数据库交互。 - 例如,展示数据库表数据或执行查询操作。
8. 模型/视图架构的信号与槽
模型/视图架构通过信号和槽机制实现数据变化的通知和响应。以下是一些常用的信号和槽:
8.1 模型信号
dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
:当模型数据发生变化时发出。rowsInserted(const QModelIndex &parent, int first, int last)
:当插入新行时发出。rowsRemoved(const QModelIndex &parent, int first, int last)
:当删除行时发出。
8.2 视图信号
clicked(const QModelIndex &index)
:当用户点击视图中的项时发出。doubleClicked(const QModelIndex &index)
:当用户双击视图中的项时发出。activated(const QModelIndex &index)
:当用户激活视图中的项时发出(例如按下回车键)。
8.3 示例:响应视图的点击事件
#include <QApplication>
#include <QListView>
#include <QStringListModel>
#include <QDebug>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// 创建模型
QStringListModel model;
QStringList list;
list << "Apple" << "Banana" << "Cherry" << "Date";
model.setStringList(list);
// 创建视图
QListView view;
view.setModel(&model);
view.setWindowTitle("Signal/Slot Example");
view.resize(200, 300);
// 连接视图的 clicked 信号
QObject::connect(&view, &QListView::clicked, [](const QModelIndex &index) {
qDebug() << "Clicked item:" << index.data().toString();
});
view.show();
return app.exec();
}
9. 模型/视图架构的性能优化
9.1 只更新必要的数据
- 当数据发生变化时,尽量只更新受影响的区域,而不是整个模型。
- 例如,使用
dataChanged
信号时,指定受影响的范围。
9.2 使用分层模型
- 对于大型数据集,可以使用分层模型(如
QTreeView
)来减少内存占用和提高性能。
9.3 延迟加载数据
- 对于需要从数据库或网络加载的数据,可以使用延迟加载策略,只在需要时加载数据。
9.4 优化委托
- 自定义委托时,尽量减少不必要的绘制操作,以提高性能。
10. 总结
Qt 的模型/视图架构是一种强大的设计模式,用于分离数据和用户界面,提高代码的可维护性和灵活性。以下是一些关键点:
- 模型:负责管理数据,提供接口供视图和委托访问数据。
- 视图:负责显示数据,并提供用户交互功能。
- 委托:负责数据的编辑和显示样式。
- 信号与槽:用于模型和视图之间的通信。
- 自定义模型和委托:可以实现复杂的业务需求。
通过合理使用模型/视图架构,可以构建高效、灵活且易于维护的应用程序。