前言
Qt版本:6.8.0
作用
QTransposeProxyModel
是 Qt 框架中用于行列转置的代理模型类,其核心作用是将源模型的行列结构进行互换,从而在不修改源模型的前提下,为视图提供转置后的数据展示方式。
1. 行列转置与结构映射
- 核心功能:将源模型的行变为列,列变为行。例如,源模型中索引
index(2, 3)
的数据,在代理模型中会映射为index(3, 2)
。 - 树形结构支持:若源模型是层级结构(如树形),父节点的索引也会被转置。例如,源模型的父索引
index(2, 0)
在代理中变为index(0, 2)
。 - 自动同步:源模型的数据或布局变更(如插入行、修改值)会通过代理模型自动同步到视图。
2. 视图适配与数据展示
- 多视图灵活展示:通过转置,同一数据可以在不同视图中以不同形式呈现。例如:
- 原始视图按行显示时间序列数据。
- 转置视图按列横向对比不同时间点的数据。
- 表头转置:源模型的横向表头(如列标题)在代理模型中变为纵向表头,反之亦然[ citation:1]。
3. 与视图交互的适配
- 操作转发:代理模型会将对视图的操作(如插入行、删除列)转换为对源模型的对应操作。例如:
- 在代理模型中调用
insertRows(0, 2)
实际会向源模型插入两列。
- 在代理模型中调用
- 选择模型同步:若多个视图共享同一源模型(一个原始视图,一个转置视图),需处理选择模型的同步。例如,需将原始视图的行选择转换为转置视图的列选择,需通过
QItemSelection
的映射函数实现。
4. 典型应用场景
-
数据横向对比
将纵向排列的数据转为横向,方便比较不同维度的数值。例如,将“月份”从行标签转为列标签,显示各月份的指标对比。 -
适配不同显示需求
当视图空间有限时,转置可优化布局。例如,移动端应用中,横向滚动显示原本纵向排列的数据。 -
树形数据转置
层级数据的行列互换。例如,将父子关系的树形结构转置为以列表示层级。 -
动态数据展示
结合其他代理模型(如QSortFilterProxyModel
),实现动态排序或过滤后的转置显示。
与其他代理模型的对比
代理模型 | 核心功能 | 适用场景 |
---|---|---|
QTransposeProxyModel | 行列互换 | 数据横向对比、视图适配 |
QIdentityProxyModel | 保持结构不变,扩展数据或拦截操作 | 数据格式化、权限控制 |
QSortFilterProxyModel | 动态排序和过滤 | 搜索过滤、条件筛选 |
使用示例
// 创建源模型和转置代理
QAbstractItemModel *sourceModel = new MyTableModel();
QTransposeProxyModel *proxyModel = new QTransposeProxyModel();
proxyModel->setSourceModel(sourceModel);
// 设置视图
QTableView originalView, transposedView;
originalView.setModel(sourceModel);
transposedView.setModel(proxyModel);
// 转置后,原模型的行变为代理模型的列,反之亦然
注意事项
- 索引转换复杂性:若需在转置视图中实现复杂交互(如跨视图拖放),需手动处理索引映射(通过
mapToSource
和mapFromSource
)。 - 性能考量:转置可能增加数据访问的复杂度,需避免在大型数据集上频繁操作。
QTransposeProxyModel
通过简单的行列互换,为数据展示提供了更多灵活性,适用于需要多角度分析或适配不同界面布局的场景。其核心优势在于非侵入式地扩展源模型的显示方式,同时保持数据源的独立性。
函数
QTransposeProxyModel(QObject *parent = nullptr)
构造函数,用于创建一个转置代理模型对象。
- 参数:
parent
(QObject*,可选),父对象指针,通常用于Qt对象树管理。 - 返回值:无。
~QTransposeProxyModel()
析构函数,销毁转置代理模型对象,释放资源。
- 参数:无。
- 返回值:无。
主要重写的公有函数
int columnCount(const QModelIndex &parent = QModelIndex()) const override
返回指定父项下的列数。对于转置模型,通常返回源模型的行数。
- 参数:
parent
(QModelIndex),父项索引,默认是无效索引(即顶层)。 - 返回值:int,列数。
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override
返回表头数据。对于转置模型,行列头会互换。
- 参数:
section
(int):表头的节号(行号或列号)。orientation
(Qt::Orientation):方向(Qt::Horizontal 水平或 Qt::Vertical 垂直)。role
(int):角色,通常为Qt::DisplayRole
。
- 返回值:QVariant,表头显示的数据。
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override
返回指定行列和父项的模型索引。
- 参数:
row
(int):行号。column
(int):列号。parent
(QModelIndex):父项索引,默认无效。
- 返回值:QModelIndex,指定位置的索引。
bool insertColumns(int column, int count, const QModelIndex &parent = QModelIndex()) override
在指定位置插入列。
- 参数:
column
(int):插入起始列号。count
(int):插入的列数。parent
(QModelIndex):父项索引。
- 返回值:bool,插入是否成功。
bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override
在指定位置插入行。
- 参数:
row
(int):插入起始行号。count
(int):插入的行数。parent
(QModelIndex):父项索引。
- 返回值:bool,插入是否成功。
QMap<int, QVariant> itemData(const QModelIndex &index) const override
返回指定索引的所有角色数据。
- 参数:
index
(QModelIndex),目标索引。 - 返回值:QMap<int, QVariant>,键为角色,值为数据。
QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override
将源模型的索引映射到代理模型的索引(转置后的位置)。
- 参数:
sourceIndex
(QModelIndex),源模型索引。 - 返回值:QModelIndex,代理模型中的索引。
QModelIndex mapToSource(const QModelIndex &proxyIndex) const override
将代理模型的索引映射回源模型的索引。
- 参数:
proxyIndex
(QModelIndex),代理模型索引。 - 返回值:QModelIndex,源模型中的索引。
bool moveColumns(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) override
移动列。
- 参数:
sourceParent
(QModelIndex):源父项。sourceRow
(int):源起始行。count
(int):移动的列数。destinationParent
(QModelIndex):目标父项。destinationChild
(int):目标子项位置。
- 返回值:bool,移动是否成功。
bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) override
移动行。
- 参数:同上,只是针对行。
- 返回值:bool,移动是否成功。
QModelIndex parent(const QModelIndex &index) const override
返回指定索引的父项索引。
- 参数:
index
(QModelIndex),目标索引。 - 返回值:QModelIndex,父项索引。
bool removeColumns(int column, int count, const QModelIndex &parent = QModelIndex()) override
移除指定位置的列。
- 参数:
column
(int):起始列号。count
(int):移除的列数。parent
(QModelIndex):父项索引。
- 返回值:bool,移除是否成功。
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override
移除指定位置的行。
- 参数:
row
(int):起始行号。count
(int):移除的行数。parent
(QModelIndex):父项索引。
- 返回值:bool,移除是否成功。
int rowCount(const QModelIndex &parent = QModelIndex()) const override
返回指定父项下的行数。对于转置模型,通常返回源模型的列数。
- 参数:
parent
(QModelIndex),父项索引。 - 返回值:int,行数。
bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role = Qt::EditRole) override
设置表头数据。
- 参数:
section
(int):节号。orientation
(Qt::Orientation):方向。value
(QVariant):要设置的数据。role
(int):角色,通常为Qt::EditRole
。
- 返回值:bool,设置是否成功。
bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles) override
设置指定索引的多个角色数据。
- 参数:
index
(QModelIndex):目标索引。roles
(QMap<int, QVariant>):角色与数据的映射。
- 返回值:bool,设置是否成功。
void setSourceModel(QAbstractItemModel *newSourceModel) override
设置源数据模型。
- 参数:
newSourceModel
(QAbstractItemModel*),新的源模型指针。 - 返回值:无。
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override
对指定列进行排序。
- 参数:
column
(int):要排序的列号。order
(Qt::SortOrder):排序顺序(升序或降序)。
- 返回值:无。
QSize span(const QModelIndex &index) const override
返回指定索引的单元格跨越的行和列数(用于合并单元格)。
- 参数:
index
(QModelIndex),目标索引。 - 返回值:QSize,宽为跨越的列数,高为跨越的行数。
示例
1. columnCount
QTransposeProxyModel proxy;
proxy.setSourceModel(sourceModel);
int cols = proxy.columnCount(); // 获取转置后表格的列数
2. headerData
QString header = proxy.headerData(0, Qt::Horizontal, Qt::DisplayRole).toString(); // 获取第0列的表头
3. index
QModelIndex idx = proxy.index(1, 2); // 获取转置后第1行第2列的索引
4. insertColumns
bool ok = proxy.insertColumns(1, 2); // 在转置后第1列插入2列
5. insertRows
bool ok = proxy.insertRows(0, 1); // 在转置后第0行插入1行
6. itemData
QModelIndex idx = proxy.index(0, 0);
QMap<int, QVariant> roles = proxy.itemData(idx); // 获取索引的所有角色数据
7. mapFromSource
QModelIndex srcIdx = sourceModel->index(2, 3);
QModelIndex proxyIdx = proxy.mapFromSource(srcIdx); // 源模型索引转为代理模型索引
8. mapToSource
QModelIndex proxyIdx = proxy.index(1, 2);
QModelIndex srcIdx = proxy.mapToSource(proxyIdx); // 代理模型索引转为源模型索引
9. moveColumns
proxy.moveColumns(QModelIndex(), 0, 1, QModelIndex(), 2); // 移动第0列到第2列
10. moveRows
proxy.moveRows(QModelIndex(), 1, 1, QModelIndex(), 3); // 移动第1行到第3行
11. parent
QModelIndex idx = proxy.index(0, 0);
QModelIndex parentIdx = proxy.parent(idx); // 获取索引的父项
12. removeColumns
proxy.removeColumns(0, 1); // 移除第0列
13. removeRows
proxy.removeRows(0, 2); // 移除第0行开始的2行
14. rowCount
int rows = proxy.rowCount(); // 获取转置后表格的行数
15. setHeaderData
proxy.setHeaderData(0, Qt::Vertical, "新表头", Qt::EditRole); // 设置第0行的表头
16. setItemData
QModelIndex idx = proxy.index(0, 0);
QMap<int, QVariant> roles;
roles[Qt::DisplayRole] = "新内容";
proxy.setItemData(idx, roles); // 设置索引的显示内容
17. setSourceModel
proxy.setSourceModel(sourceModel); // 设置源模型
18. sort
proxy.sort(1, Qt::DescendingOrder); // 按第1列降序排序
19. span
QModelIndex idx = proxy.index(0, 0);
QSize cellSpan = proxy.span(idx); // 获取单元格的跨行跨列信息
如需更详细的实际应用场景代码,可进一步说明。
如何使用
一般情况下,如果你直接使用 Qt 官方提供的 QTransposeProxyModel
,这些函数(如 rowCount
、columnCount
、mapFromSource
、mapToSource
等)已经在 QTransposeProxyModel
内部被正确重写,实现了行列转置的逻辑。你无需再自己重写这些函数,只需要像普通的代理模型一样使用它即可。
你只需要做的事情:
- 创建你的源模型(如 QAbstractTableModel 的子类)。
- 创建 QTransposeProxyModel 实例,并设置源模型:
QTransposeProxyModel *transposeModel = new QTransposeProxyModel(this); transposeModel->setSourceModel(yourSourceModel);
- 将
transposeModel
设置给你的视图(如 QTableView):tableView->setModel(transposeModel);
只有在以下情况下才需要重写
- 你有特殊的转置逻辑需求(比如只转置部分区域,或有特殊的行列映射规则)。
- 你需要对某些行为进行自定义(比如自定义 headerData 的显示方式等)。
否则,直接用 Qt 提供的 QTransposeProxyModel 即可,无需自己重写这些函数。