欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 金融 > Qt从入门到入土(九) -model/view(模型/视图)框架

Qt从入门到入土(九) -model/view(模型/视图)框架

2025/3/12 6:26:21 来源:https://blog.csdn.net/2302_82012065/article/details/146139519  浏览:    关键词:Qt从入门到入土(九) -model/view(模型/视图)框架

简介

Qt的模型/视图(Model/View)架构是一种用于分离数据处理和用户界面展示的设计模式。它允许开发者将数据存储和管理(模型)与数据的显示和交互(视图)解耦,从而提高代码的可维护性和可扩展性。

Model/View的基本结构如下图所示:

各部分功能如下所示:

  • 数据(Data):表示实际的数据
  • 模型(Model):与实际数据连接,并为视图组件提供数据接口。从原始数据中提取所需内容,用于视图进行显示和编辑。
  • 视图(View):从模型中获取每个数据项的模型索引,通过模型索引获取数据,然后为界面组件提供显示数据。

在Qt的模型/视图架构中:

  • 数据模型和显示界面分离,允许同一数据模型在多个视图中展示,且可在不修改模型的情况下自定义视图。

  • 代理功能允许用户自定义数据的显示和编辑方式。在标准视图中,代理通过模型索引与数据模型通信,并提供编辑器(如QLineEdit)。

  • 模型、视图和代理通过信号和槽进行通信:

    • 数据模型变化时,通知视图更新。

    • 视图操作时,通知模型和代理。

    • 编辑数据时,代理通知模型和视图编辑器状态。

数据模型

所有基于项数据的模型都继承自QAbstractItemModel类,该类定义了视图和代理访问数据的接口。数据模型中无需直接存储数据,数据可以来自其他类、文件、数据库或其他任何数据源。

抽象类不能直接使用,需要子类继承并实现一些虚函数。Qt中包含了用于项数据处理的模型类,如下表:

Model 类用途
QStringListModel用于处理字符串列表数据的数据模型类
QStandardltemModel标准的基于项数据的数据模型类,每个项数据可以是任何数据类型
QFileSystemModel计算机上文件系统的数据模型类
QSortFilterProxyModel与其他数据模型结合,提供排序和过滤功能的数据模型类
QSqlQueryModel用于数据库SQL查询结果的数据模型类
QSqlTableModel用于数据库的一个数据表的数据模型类
QSqlRelationalTableModel用于关系型数据表的数据模型类

如果现有的这些模型类无法满足需求,可以从 QAbstractltemModel、QAbstractListModel 或 QAbstractTableModel 继承,生成自己的数据模型类。

视图组件

视图组件(View)就是显示数据模型的数据的界面组件。

Qt中提供的视图组件,如下表:

QListView用于显示单列的列表数据,适用于一维数据的操作
QTreeView用于显示树状结构数据,适用于树状结构数据的操作
QTableView用于显示表格状数据,适用于二维表格型数据的操作
QColumnView用多个QListView显示树状层次结构,树状结构的一层用一个QListView显示
QHeaderView提供行表头或列表头的视图组件,如QTableView的行表头和列表头

代理(Delegate)

在Qt中,代理(Delegate)用于在视图组件中编辑数据,负责从模型获取数据并显示在编辑器中,编辑完成后将数据保存回模型。默认情况下,Qt使用QStyledItemDelegate作为代理,它基于QAbstractItemDelegate(抽象基类)。对于特殊数据编辑需求(如整数输入使用QSpinBox,选择数据使用QComboBox),可以通过继承QStyledItemDelegate创建自定义代理。

Model/View结构

如上图,在Qt的模型/视图架构中,所有数据模型类都继承自QAbstractItemModel,并以表格的层次结构表示数据,为视图组件和代理提供统一的数据存取接口。无论底层数据结构如何组织,数据模型的表现形式可以是列表、表格或树状结构。数据模型的基本单元是项(item),每个项由行号、列号和父项定义其位置。在列表和表格中,所有项共享一个顶层项,实际顶层项并不存在;而在树状结构中,行号、列号和父项的关系更复杂,但足以唯一确定一个项的位置并存取其数据。

模型索引

为了隔离数据表示和存取方式,Qt引入了模型索引(QModelIndex)的概念。每个数据项通过模型索引进行存取,视图组件和代理通过模型索引与数据模型交互。模型索引是一个临时指针,用于提取或修改数据。由于数据模型的内部结构可能变化,模型索引是临时的。如果需要持久化的模型索引,则使用QPersistentModelIndex

获取索引

数据模型的基本形式是表格数据,但底层数据不一定以二维数组存储。行号和列号仅是为了方便组件间交互的约定。通过模型索引(QModelIndex)的行号、列号和父项索引可以存取数据。

  • 列表和表格模型:顶层节点用QModelIndex()表示,所有数据项的父项是顶层项。

// 顶层节点总是用 QModelIndex() 表示

QModelIndex index1 = model->index(0, 0, QModelIndex());

QModelIndex index2 = model->index(1, 1, QModelIndex());

  • 树状结构模型:节点可以有父节点,构造模型索引时需指定行号、列号和父节点索引。例如,节点B的父节点是节点A,则其索引通过model->index(row, column, parentIndex)生成。

// 节点1 3的父节点是顶层节点,节点2的父节点是节点1

QModelIndex index1= model->index(0, 0, QModelIndex());
QModelIndex index3 = model->index(2, 1, QModelIndex());

QModelIndex index2 = model->index(1,0,index1); 

项的角色

在Qt的数据模型中,每个项可以设置不同角色(role)的数据,用于满足不同的显示或交互需求。QStandardItemModel中的QStandardItem通过setData(const QVariant &value, int role)方法设置数据,其中role指定数据的角色,默认为Qt::UserRole + 1。常见的角色包括:

  • Qt::DisplayRole:用于视图组件中显示的字符串。

  • Qt::ToolTipRole:用于鼠标悬停时的提示信息。

  • Qt::DecorationRole:用于装饰显示(如图标)。

  • Qt::UserRole:用于自定义数据。

获取项的数据时,也需要通过role指定获取哪种角色的数据,例如data(int role)。视图组件和代理会根据角色来解释和显示数据,不同组件对角色数据的处理方式可能不同,某些角色的数据也可能被忽略。

模型视图的基本使用

创建视图

视图就是一个窗口,所以使用模型/视图框架时无需使用QWidget,可以直接调用视图内置显示函数用于显示

// 创建视图
QListView view;

// 把模型交给视图显示
view.setModel(model);
view.show();

创建模型

// 创建模型
auto model = new QStandardItemModel(&a);

添加项

// 给模型添加数据项
model->appendRow(new QStandardItem("肖战"));
model->insertRow(0,new QStandardItem("蔡徐坤"));

添加子项

给模型中的QStandardItem添加子项,只对QTreeView起作用

QStandardItem* item = new QStandardItem("蔡徐坤");
                                                   
auto* subItem = new QStandardItem("个人信息");          
subItem->appendRow(new QStandardItem("18岁"));    
subItem->appendRow(new QStandardItem("身高190cm"));    
                                                   
item->appendRow(subItem);                          
item->appendRow(new QStandardItem("生活习惯"));         
                                                   
model->appendRow(item);

修改项

// 修改数据项
model->setItem(1,new QStandardItem("陈立农"));

model->setData(index,"大碗宽面",Qt::ItemDataRole::DisplayRole);   //也可用于修改数据项

删除项

// 删除数据项,从模型中移除并释放内存
//model->removeRow(0);
//model->removeRows(0,2);   // 指定起始行和行数
// 删除数据项,但不释放内存
auto itemLists = model->takeRow(1);
qDebug()<<itemLists.size();
for(auto &item:itemLists)
{
    // 需要手动释放内存
    delete item;
}
qDebug()<<model->rowCount()<<" "<<model->columnCount();

获取项的索引

// item索引
auto item = new QStandardItem("wyf");
model->appendRow(item);
QModelIndex index = model->indexFromItem(item);
qDebug()<<index;

查找项

// 查询数据项
auto res = model->findItems("蔡徐坤");
if(res.isEmpty())
    qDebug()<<"未找到该数据!";
for(auto& r:res)
{
    qDebug()<<"找到数据:"<<r->data(Qt::ItemDataRole::DisplayRole).toString();
}

设置角色数据

// 角色(ItemDataRole)
model->setData(index,"大碗宽面",Qt::ItemDataRole::DisplayRole);   //也可用于修改数据项
item->setData(QColor(0,0,0),Qt::ItemDataRole::DecorationRole);   //设置装饰角色
item->setData("我是帅哥",Qt::ItemDataRole::UserRole);             //设置用户角色
qDebug()<<item->data(Qt::UserRole).toString();                //获取用户角色信息 

视图信号连接槽

// 连接信号与槽
QObject::connect(&view,&QListView::clicked,[=](const QModelIndex& idx){
      qDebug()<<idx.data(Qt::DisplayRole).toString();
});

综合代码

#include <QStandardItemModel>
#include <QListView>
#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);// 创建模型auto model = new QStandardItemModel(&a);// 创建视图QListView view;// 把模型交给视图显示view.setModel(model);view.show();// 给模型添加数据项model->appendRow(new QStandardItem("肖战"));model->insertRow(0,new QStandardItem("蔡徐坤"));// 修改数据项model->setItem(1,new QStandardItem("陈立农"));// 删除数据项,从模型中移除并释放内存//model->removeRow(0);//model->removeRows(0,2);   // 指定起始行和行数// 删除数据项,但不释放内存auto itemLists = model->takeRow(1);qDebug()<<itemLists.size();for(auto &item:itemLists){// 需要手动释放内存delete item;}qDebug()<<model->rowCount()<<" "<<model->columnCount();// item索引auto item = new QStandardItem("wyf");model->appendRow(item);QModelIndex index = model->indexFromItem(item);qDebug()<<index;// 角色(ItemDataRole)model->setData(index,"大碗宽面",Qt::ItemDataRole::DisplayRole);   //也可用于修改数据项item->setData(QColor(0,0,0),Qt::ItemDataRole::DecorationRole);   //设置装饰角色item->setData("我是帅哥",Qt::ItemDataRole::UserRole);             //设置用户角色qDebug()<<item->data(Qt::UserRole).toString();                //获取用户角色信息// 查询数据项auto res = model->findItems("蔡徐坤");if(res.isEmpty())qDebug()<<"未找到该数据!";for(auto& r:res){qDebug()<<"找到数据:"<<r->data(Qt::ItemDataRole::DisplayRole).toString();}// 设置是否可选item->setCheckable(true);// 连接信号与槽QObject::connect(&view,&QListView::clicked,[=](const QModelIndex& idx){qDebug()<<idx.data(Qt::DisplayRole).toString();});// 添加子项,只对QTreeView视图起作用// auto subItem = new QStandardItem("个人信息");// subItem->appendRow(new QStandardItem("18岁"));// subItem->appendRow(new QStandardItem("身高180cm"));// model->item(0)->appendRow(subItem);return a.exec();
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词