QT-MVC-编程-MODELDELEGATEVIEW五
目录
QT MVC 编程 MODEL/DELEGATE/VIEW(五)
一、ProxyModel代理模型
- Qt的MVC结构中,模型(Model)负责管理数据,视图(View)负责显示数据,而委托(Delegate)负责处理数据的显示和编辑。假设有这样一种应用场景,你有一个十分复杂和详细的Model,现在需要用几个不同的View把它展示出来,每个View有自己的展示主题,需要从Model中选择不同的数据字段(栏目),该采用何种解决方案呢?
- 这个问题可以用三种方法来解决:
- 1)定义多个Model,每个Model是原始Model的子集。这种方法最简单,但是需要从一个大的Model中复制数据,在数据量大的时候,性能会受到影响,同时,由于每增加一个View就需要新定义一个Model,缺乏灵活性和扩展性。
- 2)对于同一个模型,不同的View通过设置不同的委托,并调整列的可见性来筛选数据,以便显示不同的效果。这种方应该可以满足需求,但在某些情况下不够方便,隐藏列在性能上仍然不高效。
- 3)使用代理模型(Proxy
- Model),比如QSortFilterProxyModel或者自定义的代理模型,来对原始模型进行过滤或转换,从而在不同的视图中显示不同的列和数据形式。
- 通过代理模型来重新映射列的索引,或者对数据进行转换,这样不同的视图可以绑定不同的代理模型,每个代理模型处理特定的列和显示方式。这种方法的好处是可以在不影响原始模型的情况下,为每个视图定制不同的表现方式。
- 每个视图的代理模型负责列的筛选和顺序,而每个视图的委托负责具体单元格的显示方式,这种组合方式既具有高性能,又有灵活扩展性,例如为每个视图创建不同的代理模型来处理列的过滤,视图1的代理模型显示列0、2、4,视图2的代理模型显示列1、3。同时,视图1为列1(进度)设置进度条委托,列2(日期)设置日期格式委托等等。
- 下面的例子将演示代理模型的实现。
- 原始模型一共30行、8列
- model.setHorizontalHeaderLabels({“项目名”, “年龄”, “进度”, “状态”,
- “日期”,“性别”,“身高cm”,“体重Kg”});
- 运行效果如下:
- 1、视图1
- 1)获取”项目名", “进度”, “日期"三列,并将列名改为"项目名称”、“进度预警”、“完成日期”
- 2)通过委托实现特殊显示:
- ProgressDelegate:将数值转换为进度条显示
- DateFormatDelegate:将日期时间格式化为指定字符串
- 3)对行数据进行了过滤,仅显示进度小于50%的项目(因此可以看到行号不连续)
- 2、视图2
获取”年龄",
- “进度”, “状态"三列,并将列名改为"用户年龄”、“当前进度%"、“激活状态”
- 对”激活状态”进行了过滤,仅仅显示处于”激活”的数据
- 3、视图3
- 使用源数据模型,显示全部数据。
- 由上图可见,通过代理模型截取源模型中相关行、列数据,可分别支持不同视图的显示 。
- main.cpp如下
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include “proxymodel.h”
- void initModel(QStandardItemModel& model);
- // 进度条委托
- class ProgressDelegate : public QStyledItemDelegate {
- public:
- void paint(QPainter *painter, const QStyleOptionViewItem &option,
- const QModelIndex &index) const override {
- int progress = index.data().toInt();
- QStyleOptionProgressBar progressBar;
- progressBar.rect = option.rect.adjusted(2, 2, -2, -2);
- progressBar.minimum = 0;
- progressBar.maximum = 100;
- progressBar.progress = progress;
- progressBar.textVisible = true;
- progressBar.text = QString::number(progress) + “%”;
- QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBar, painter);
- }
- };
- // 日期格式化委托
- class DateFormatDelegate : public QStyledItemDelegate {
- public:
- void paint(QPainter *painter, const QStyleOptionViewItem &option,
- const QModelIndex &index) const override {
- QDateTime date = index.data().toDateTime();
- QString text = date.toString(“yyyy-MM-dd”);
- painter->save();
- painter->drawText(option.rect, Qt::AlignCenter, text);
- painter->restore();
- }
- };
- int main(int argc, char *argv[]) {
- QApplication app(argc, argv);
- QTabWidget page;
- QTableView view[3];
- // 创建数据模型
- QStandardItemModel model(30, 8);
- initModel(model);
- //—————————————————-
- // 创建第一个代理模型(显示列0,2,4)
- ProxyModel proxy1;
- proxy1.setSourceModel(&model);
- proxy1.setVisibleColumns({0, 2, 4});
- proxy1.setProgressLimit(50);//仅仅显示进度小于20%
- proxy1.setColumnHeader(0, “项目名称”); // 修改源列0的标题
- proxy1.setColumnHeader(2, “进度预警”); // 修改源列2的标题
- proxy1.setColumnHeader(4, “完成日期”); // 修改源列4的标题
- // 创建第二个代理模型(显示列1,2,3)
- ProxyModel proxy2;
- proxy2.setSourceModel(&model);
- proxy2.setVisibleColumns({1,2,3});
- proxy2.setStatus(true);
- proxy2.setColumnHeader(1, “用户年龄”); // 修改源列1的标题
- proxy2.setColumnHeader(2, “当前进度%”); // 修改源列2的标题
- proxy2.setColumnHeader(3, “激活状态”); // 修改源列3的标题
- //—————————————————-
- // 第一个视图:显示产品信息
- view[0].setModel(&proxy1);
- view[0].setItemDelegateForColumn(1, new ProgressDelegate()); // 进度列
- view[0].setItemDelegateForColumn(2, new DateFormatDelegate()); // 日期列
- page.addTab(&view[0],“视图1 - 项目进度”);
- // 第二个视图:显示状态信息
- view[1].setModel(&proxy2);
- page.addTab(&view[1],“视图2 - 用户状态”);
- // 第三个视图:显示原始数据
- view[2].setModel(&model);
- page.addTab(&view[2],“视图3 - 原始数据”);
- page.resize(QSize(1024,768));
- page.show();
- return app.exec();
- }
- //初始化一些测试数据
- void initModel(QStandardItemModel& model)
- {
- model.setHorizontalHeaderLabels({“项目名”, “年龄”, “进度”, “状态”, “日期”,“性别”,“身高cm”,“体重Kg”});
- for (int row = 0; row < 30; ++row) {
- model.setData(model.index(row, 0), QString(“项目 %1”).arg(row+1));
- model.setData(model.index(row, 1), 25 + qrand()%10); // 年龄
- model.setData(model.index(row, 2), qrand()%100); // 进度
- model.setData(model.index(row, 3), row % 2 ? “激活” : “未激活”); // 状态
- model.setData(model.index(row, 4), QDateTime::currentDateTime().addDays(row)); // 日期
- model.setData(model.index(row, 5), qrand()%2 ? “男” : “女”); // 性别
- model.setData(model.index(row, 6), 150+qrand()%30); // 身高cm
- model.setData(model.index(row, 7), 40+qrand()%40); // 体重Kg
- }
- 代理模型定义如下:
- proxymodel.h
- #ifndef ProxyModel_H
- #define ProxyModel_H
- #include
- #include
- class ProxyModel : public QSortFilterProxyModel
- {
- Q_OBJECT
- public:
- ProxyModel(QObject *parent = 0);
- void setVisibleColumns(const QList &columns);
- void setColumnHeader(int sourceColumn, const QString &header);
- void setProgressLimit(const int &progress); //设置行过滤条件
- void setStatus(const bool &status);
- protected:
- bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
- bool filterAcceptsColumn(int sourceColumn, const QModelIndex &) const override;
- QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
- private:
- bool dateInRange(const int &progress) const;
- private:
- QList m_visibleColumns; //可见列
- QMap m_columnHeaders; // 源列号到标题的映射
- int m_progress_limit; //数据筛选:“进度"栏
- bool m_status; //数据筛选:“状态"栏
- };
- #endif // ProxyModel_H
- proxymodel.cpp
- #include “proxymodel.h”
- ProxyModel::ProxyModel(QObject *parent)
- QSortFilterProxyModel(parent) { m_progress_limit = 100; m_status = false; } //设置可见列 void ProxyModel::setVisibleColumns(const QList &columns) { m_visibleColumns = columns; invalidateFilter(); } // 设置列标题(参数为源模型列号) void ProxyModel::setColumnHeader(int sourceColumn, const QString &header) { m_columnHeaders[sourceColumn] = header; } //设置"进度"的门限值 void ProxyModel::setProgressLimit(const int &progress) { m_progress_limit = progress; invalidateFilter(); } //设置"状态"过滤 void ProxyModel::setStatus(const bool &status) { m_status = status; invalidateFilter(); } //过滤行 bool ProxyModel::filterAcceptsRow(int sourceRow,const QModelIndex &sourceParent) const { bool status = true; //根据第2列 “进度” 值进行Row的筛选 QModelIndex index = sourceModel()->index(sourceRow, 2, sourceParent); //根据第3列 “状态” 进行Row的筛选 if(m_status) { QModelIndex index = sourceModel()->index(sourceRow, 3, sourceParent); status = sourceModel()->data(index).toString() == “激活”; } return (dateInRange(sourceModel()->data(index).toInt())&&status); } //过滤列 bool ProxyModel::filterAcceptsColumn(int sourceColumn, const QModelIndex &) const { return m_visibleColumns.contains(sourceColumn); } // 标题数据重写 QVariant ProxyModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { if (section >= 0 && section < m_visibleColumns.size()) { int srcCol = m_visibleColumns.at(section); // 获取对应的源列号 // 优先返回自定义标题 if (m_columnHeaders.contains(srcCol)) { return m_columnHeaders[srcCol]; } // 回退到源模型标题 return sourceModel()->headerData(srcCol, orientation, role); } } return QSortFilterProxyModel::headerData(section, orientation, role); } //行过滤的条件:判断进度是否小于指定值 bool ProxyModel::dateInRange(const int &progress) const { return (progress<m_progress_limit); }