QT模型/视图结构:ListModel与TableModel

news/2024/12/14 1:00:07/

简介

模型/视图(model/view)结构是进行数据存储和界面展示的一种编程结构。此种方式将数据的存储与显示进行了解耦,视图组件显示模型中的数据,在视图组件里修改的数据会被自动保存到模型里。模型的数据来源可以是内存中的字符串列表或二维表格型数据,也可以是数据库中的数据表,一种模型可以用不同的视图组件来显示数据,所以模型/视图结构是一种高效、灵活的编程结构。

• 源数据(data)是原始数据,如数据库的一个数据表或 SQL 查询结果、内存中的一个字符串列表或磁盘文件系统结构等。

• 视图(view)也称为视图组件,是界面组件,视图从模型获得数据然后将其显示在界面上。 Qt 提供一些常用的视图组件,如 QListView、QTreeView 和 QTableView 等。

• 模型(model)也称为数据模型,与源数据通信,并为视图组件提供数据接口。它从源数 据提取需要的数据,用于视图组件进行显示和编辑。Qt 中有一些预定义的模型类,如 QStringListModel 是字符串列表的模型类,QSqlTableModel 是数据库中数据表的模型类。

• 代理(delegate)在视图与模型之间交互操作时提供的临时编辑器。模型向视图提供数据是单向的,一般仅用于显示。当需要在视图上编辑数据时,代理会为编辑数据提供一个编辑器,这个编辑器获取模型的数据、接受用户编辑的数据后又将其提交给模型。

模型结构有三种类型:列表模式、表格模式和树状模式,本文只探讨前两种用得较多的模式。

模型的索引

通过模型能访问的每个项都有一 个模型索引,视图组件和代理都 通过模型索引来获取数据。模型的基本形式是用行和列定义的表格数据,但这并不意味着底层的数据是用二维数组存储 的,使用行和列只是为了组件之间交互方便。当模型为列表或表格结构时,使用行号、列号访问数据比较直观,所有项的父项就是顶层项。

例如对于上述表格模型中的 3 个项 A、B、C获取模型索引的方式如下:

QModelIndex indexA = model->index(0, 0, QModelIndex()); 
QModelIndex indexB = model->index(1, 1, QModelIndex()); 
QModelIndex indexC = model->index(2, 1, QModelIndex());

在创建模型 索引的函数中需要传递行号、列号和父项的模型索引。对于列表模型和表格模型,顶层节点总是 用 QModelIndex()表示。

而当模型涉及到列表项时,情况比较复杂,父节点的表示需要注意

例如上述树状模型,节点 A 和节点 C 的父节点是顶层节点,但是,节点 B 的父节点是节点 A,特别需要注意节点B的访问情况:

QModelIndex indexA = model->index(0, 0, QModelIndex()); 
QModelIndex indexC = model->index(2, 1, QModelIndex());
QModelIndex indexB = model->index(1, 0, indexA);

项的角色

模型中每一个项都有角色,设置项的数据的函数 setData(),其函数原型定义如下:

bool QAbstractItemModel::setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole)

其中,index 是项的模型索引,value 是需要设置的数据,role 是设置数据的角色。

常见的一些角色和枚举值如下表

同样,在获取一个项的数据时也需要指定角色,以获取不同角色的数据。函数 data(),可返回一个项的不同角色的数据,其函数原型定义如下:

QVariant QAbstractItemModel::data(const QModelIndex &index, int role = Qt::DisplayRole)

通过为一个项的不同角色定义数据,可以告知视图组件和代理如何展示数据。

例如下图,项的 DisplayRole角色数据是显示的字符串,DecorationRole 角色数据是用于装饰显示的元素(如图标),ToolTipRole 角色数据是就地显示的提示信息。

关联数据模型和选择模型

视图组件需要设置关联的模型才能构成完整的模型/视图结构。相关函数定义如下:

void setModel(QAbstractItemModel *model) //设置数据模型

QAbstractItemModel *model() //返回关联的数据模型对象指针

不同的视图组件使用不同类型的模型,QListView 组件一般用 QStringListModel 对象作为数据 模型,用于编辑字符串列表;QTableView 一般用 QStandardItemModel 对象作为数据模型,用于编辑表格数据。视图组件还可以设置选择模型,在界面上选择的项发生变化时,通过选择模型可以获取所有被选择项的模型索引。

相关函数定义如下:

void setSelectionModel(QItemSelectionModel *selectionModel) //设置选择模型

QItemSelectionModel *selectionModel() //返回关联的选择模型对象指针

常用接口函数

QAbstractItemView 定义了很多接口函数,下面是常用的几个。

QModelIndex currentIndex() //返回当前项的模型索引,例如当前单元格的模型索引

void setCurrentIndex(const QModelIndex &index) //设置模型索引为 index 的项为当前项

void selectAll() //选择视图中的所有项,例如选择 QTableView 组件中的所有单元格

void clearSelection() //清除所有选择

如果设置为单选,视图组件上就只有一个当前项,函数 currentIndex()返回当前项的模型索引,

通过模型索引就可以从模型中获取项的数据。

常用信号

QAbstractItemView 定义了几个信号,常用的几个信号定义如下,信号触发条件见注释。

void clicked(const QModelIndex &index) //点击某个项时

void doubleClicked(const QModelIndex &index) //双击某个项时

void entered(const QModelIndex &index) //鼠标移动到某个项上时

void pressed(const QModelIndex &index) //鼠标左键或右键被按下时

QStringListModel QListView

QStringListModel 内部存储了一个字符串列表,这个字符串列表的内容自动显示在关联的 QListView 组件上,在 QListView 组件上双击某一行时,可以通过默认的代理组件(QLineEdit 组件)修改这一行字符串的内容,修改后的这行字符串自动保存到数据模型的字符串列表里。

在字符串列表中添加或删除行是通过 QStringListModel 的接口函数实现的,QListView 没有接 口函数用于修改数据,它只是用作数据显示和编辑的界面组件。通过 QStringListModel 的接口函 数修改字符串列表的内容后,关联的 QListView 组 件会自动更新显示内容。

例程详解

构造函数中创建了数据模型 m_model 并初始化其字符串列表数据,再将 m_model 设置 为界面上的 QListView 组件 listView 的数据模型,构造模型/视图结构。 程序运行后,界面上的 listView 里就会显示初始化的字符串列表的内容。

MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);//初始化一个字符串列表的内容m_strList<<"北京"<<"上海"<<"天津"<<"河北"<<"山东"<<"四川"<<"重庆"<<"广东"<<"河南";m_model= new QStringListModel(this);    //创建数据模型m_model->setStringList(m_strList);      //为模型设置StringList,会导入StringList的内容ui->listView->setModel(m_model);        //为listView设置数据模型:将数据模型与界面组件绑定ui->chkEditable->setChecked(true);ui->listView->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::SelectedClicked);}

数据模型的操作

可以对数据模型进行操作,例如添加项、删除项、 移动项等,相关操作会自动更新到界面


void MainWindow::on_btnIniList_clicked()
{ //恢复列表m_model->setStringList(m_strList);    //重新载入到模型,界面会自动发生变化
}void MainWindow::on_btnListAppend_clicked()
{ //添加项m_model->insertRow(m_model->rowCount());  //在尾部插入一个数据项QModelIndex index=m_model->index(m_model->rowCount()-1,0, QModelIndex());  //获取刚插入的项的模型索引m_model->setData(index,"new item",Qt::DisplayRole);    //设置显示文字,界面会自动发生变化ui->listView->setCurrentIndex(index);    //将光标定位到新加的项
}void MainWindow::on_btnListInsert_clicked()
{//插入项QModelIndex index=ui->listView->currentIndex();  //当前项的模型索引m_model->insertRow(index.row());       //在当前项的前面插入一项m_model->setData(index,"inserted item",Qt::DisplayRole);   //设置显示文字
//    theModel->setData(index,Qt::AlignRight,Qt::TextAlignmentRole);  //设置对齐方式,不起作用ui->listView->setCurrentIndex(index);   //设置当前项
}void MainWindow::on_btnListDelete_clicked()
{//删除当前项QModelIndex index=ui->listView->currentIndex();  //获取当前项的模型索引m_model->removeRow(index.row());       //删除当前项
}void MainWindow::on_btnListClear_clicked()
{//清除列表//删除row=0为起始,长度为rowCount()的项,在这里即所有项m_model->removeRows(0,m_model->rowCount());   //清除数据模型的所有项
}void MainWindow::on_btnListSort_clicked(bool checked)
{//排序if (checked)      //同样通过操作model即可自动更新到界面m_model->sort(0,Qt::AscendingOrder);   //升序elsem_model->sort(0,Qt::DescendingOrder);  //降序
}void MainWindow::on_btnListMoveUp_clicked()
{//上移int curRow=ui->listView->currentIndex().row();  //当前行号QModelIndex index=QModelIndex();m_model->moveRow(index,curRow,index,curRow-1);
}void MainWindow::on_btnListMoveDown_clicked()
{//下移int curRow=ui->listView->currentIndex().row();  //当前行号QModelIndex index=QModelIndex();m_model->moveRow(index,curRow,index,curRow+2);
}

对数据的操作都是通过数据模型的接口函数实现的。在数据模型 m_model 中添加或删除项后,界面组件 listView 中会立刻自动将其显示出来。

在对数据模型进行插入、添加、删除项操作后,内容会立即在 listView 上显示出来,这是数 据模型与视图组件之间信号与槽的作用的结果,当数据模型的内容发生改变时,通知视图组件更 新显示。在 listView 上双击一行进入编辑状态,修改一行的文字后,修改的文字也会保存到数据 模型里。 数据模型内保存着最新的数据内容,对 QStringListModel 模型来说,通过函数stringList()可以 得到其最新的数据副本。

QStandardItemModel 和 QTableView

QStandardItemModel 是以项为基本数据单元的模型类,每个项是一个 QStandardItem 对象。 项可以存储各种角色的数据,如文字、字体、对齐方式、图标、复选状态等。QStandardItemModel模型可以存储列表、表格、树3种模型数据。如果以多行多列的二维数组形式存储项,就是表格模型;如果表格模型只有一列,就是列表模型;如果在存储项时为项指定父项,就可以构成树状模型。

QStandardItemModel 数据模型中的每个项是一个 QStandardItem 对象。QStandardItem 存储了一 个项的各种特性参数,还可以存储用户自定义数据。一个项可以添加子项,子项也是 QStandardItem 类型的对象,所以,QStandardItem 也可以作为树状模型的项。

相关函数

void setRowCount(int rows) //设置数据模型的行数
void setColumnCount(int columns) //设置数据模型的列数void setItem(int row, int column, QStandardItem *item) //用于表格模型
void setItem(int row, QStandardItem *item) //用于列表模型QStandardItem *item(int row, int column = 0) //根据行号和列号返回项
QStandardItem *itemFromIndex(const QModelIndex &index) //根据模型索引返回项QModelIndex indexFromItem(const QStandardItem *item)void appendRow(const QList<QStandardItem *> &items) //用于表格模型
void appendRow(QStandardItem *item) //用于列表模型void appendColumn(const QList<QStandardItem *> &items) //在表格模型中添加列void insertRow(int row, const QList<QStandardItem *> &items) //用于表格模型
void insertRow(int row, QStandardItem *item) //用于列表模型bool insertRow(int row, const QModelIndex &parent = QModelIndex()) //用于树状模型void insertColumn(int column, const QList<QStandardItem *> &items) //用于表格模型
bool insertColumn(int column, const QModelIndex &parent = QModelIndex()) //用于树状模型QList<QStandardItem *> takeRow(int row) //移除一行,适用于表格模型
QList<QStandardItem *> takeColumn(int column) //移除一列,适用于表格模型QStandardItem *takeItem(int row, int column = 0) //移除一个项,适用于列表模型

QStandardItemModel 新定义了一个信号 itemChanged(),在任何一个项的数据发生变化时,此

信号就会被发射。信号函数定义如下,其中的参数 item 是数据发生了变化的项。

void itemChanged(QStandardItem *item)

例程详解

构造函数中创建数据模型和选择模型,并设置关联的界面组件(tableView)

MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);m_model = new QStandardItemModel(2,FixedColumnCount,this);     //创建数据模型m_selection = new QItemSelectionModel(m_model,this);           //创建选择模型//选择当前单元格变化时的信号与槽connect(m_selection,&QItemSelectionModel::currentChanged,this,&MainWindow::do_currentChanged);//    connect(theSelection,SIGNAL(currentChanged(QModelIndex,QModelIndex)),//            this,SLOT(do_currentChanged(QModelIndex,QModelIndex)));//为tableView设置数据模型ui->tableView->setModel(m_model);  //设置数据模型ui->tableView->setSelectionModel(m_selection); //设置选择模型ui->tableView->setSelectionMode(QAbstractItemView::ExtendedSelection);ui->tableView->setSelectionBehavior(QAbstractItemView::SelectItems);setCentralWidget(ui->splitter);//创建状态栏组件labCurFile = new QLabel("当前文件:",this);labCurFile->setMinimumWidth(200);labCellPos = new QLabel("当前单元格:",this);labCellPos->setMinimumWidth(180);labCellPos->setAlignment(Qt::AlignHCenter);labCellText = new QLabel("单元格内容:",this);labCellText->setMinimumWidth(150);ui->statusBar->addWidget(labCurFile);ui->statusBar->addWidget(labCellPos);ui->statusBar->addWidget(labCellText);
}void MainWindow::do_currentChanged(const QModelIndex &current, const QModelIndex &previous)
{ //选择单元格变化时的响应Q_UNUSED(previous);if (current.isValid()){labCellPos->setText(QString::asprintf("当前单元格:%d行,%d列",current.row(),current.column()));    //显示模型索引的行和列号QStandardItem *aItem=m_model->itemFromIndex(current);   //从模型索引获得ItemlabCellText->setText("单元格内容:"+aItem->text());       //显示item的文字QFont   font=aItem->font();ui->actFontBold->setChecked(font.bold());//   为了简化难度,没有设置ActionGroup,不能互斥选择
//        Qt::Alignment align=aItem->textAlignment();
//        ui->actAlignLeft->setChecked(align == Qt::AlignLeft);
//        ui->actAlignCenter->setChecked(align == Qt::AlignHCenter);
//        ui->actAlignRight->setChecked(align == Qt::AlignRight);}
}

读取ttxt文件内容并初始化Model,界面组件显示会自动发生变化

void MainWindow::iniModelData(QStringList &aFileContent)
{int rowCnt=aFileContent.size();    //文本行数,第1行是标题m_model->setRowCount(rowCnt-1);    //实际数据行数//设置表头QString header=aFileContent.at(0);  //第1行是表头QStringList headerList=header.split(QRegularExpression("\\s+"),Qt::SkipEmptyParts);m_model->setHorizontalHeaderLabels(headerList);    //设置表头文字//设置表格数据int j;QStandardItem   *aItem;for (int i=0;i<rowCnt-1;i++){QString aLineText=aFileContent.at(i);   //获取 数据区 的一行//一个或多个空格、TAB等分隔符隔开的字符串, 分解为一个StringListQStringList tmpList=aLineText.split(QRegularExpression("\\s+"),Qt::SkipEmptyParts);for (j=0;j<FixedColumnCount-1;j++){ //不包含最后一列aItem=new QStandardItem(tmpList.at(j)); //创建itemm_model->setItem(i,j,aItem);          //设置Item}aItem=new QStandardItem(headerList.at(j));  //最后一列是CheckableaItem->setCheckable(true);                  //设置为CheckableaItem->setBackground(QBrush(Qt::yellow));if (tmpList.at(j)=="0")aItem->setCheckState(Qt::Unchecked);    //根据数据设置check状态elseaItem->setCheckState(Qt::Checked);m_model->setItem(i,j,aItem);     //设置Item}
}

数据修改-添加-插入-删除

void MainWindow::on_actAppend_triggered()
{ //在表格最后添加行QList<QStandardItem*>    aItemList;     //列表QStandardItem   *aItem;for(int i=0;i<FixedColumnCount-1;i++)   //不包含最后1列{aItem=new QStandardItem("0");       //创建ItemaItemList<<aItem;   //添加到列表}//获取最后一列的表头文字QString str=m_model->headerData(m_model->columnCount()-1,Qt::Horizontal,Qt::DisplayRole).toString();aItem=new QStandardItem(str);   //创建 "测井取样"ItemaItem->setCheckable(true);aItemList<<aItem;               //添加到列表m_model->insertRow(m_model->rowCount(),aItemList);    //插入一行QModelIndex curIndex=m_model->index(m_model->rowCount()-1,0); //创建最后一行的ModelIndexm_selection->clearSelection(); //清空选择项m_selection->setCurrentIndex(curIndex,QItemSelectionModel::Select);    //设置刚插入的行为当前选择行
}void MainWindow::on_actInsert_triggered()
{//插入行QList<QStandardItem*>    aItemList;  //QStandardItem的列表类QStandardItem   *aItem;for(int i=0;i<FixedColumnCount-1;i++)    //创建前5列{aItem=new QStandardItem("0"); //新建一个QStandardItemaItemList<<aItem;//添加到列表类}
//    aItem=new QStandardItem("优"); //新建一个QStandardItem
//    aItemList<<aItem;//添加到列表类QString str;    //获取表头文字         //创建最后一列str=m_model->headerData(m_model->columnCount()-1,Qt::Horizontal,Qt::DisplayRole).toString();aItem=new QStandardItem(str); //创建ItemaItem->setCheckable(true);//设置为可使用CheckBoxaItemList<<aItem;//添加到列表类QModelIndex curIndex=m_selection->currentIndex(); //获取当前选中项的模型索引m_model->insertRow(curIndex.row(),aItemList);  //在当前行的前面插入一行m_selection->clearSelection();//清除已有选择m_selection->setCurrentIndex(curIndex,QItemSelectionModel::Select);
}void MainWindow::on_actDelete_triggered()
{ //删除行QModelIndex curIndex=m_selection->currentIndex();//获取当前选择单元格的模型索引if (curIndex.row()==m_model->rowCount()-1)//最后一行m_model->removeRow(curIndex.row()); //删除最后一行else{m_model->removeRow(curIndex.row());//删除一行,并重新设置当前选择行m_selection->setCurrentIndex(curIndex,QItemSelectionModel::Select);}
}

遍历数据模型

void MainWindow::on_actModelData_triggered()
{//模型数据导出到PlainTextEdit显示ui->plainTextEdit->clear(); //清空QStandardItem   *aItem;QString str;
//    int i,j;//获取表头文字for (int i=0;i<m_model->columnCount();i++){aItem=m_model->horizontalHeaderItem(i); //获取表头的一个项数据str=str+aItem->text()+"\t"; //用TAB间隔文字}ui->plainTextEdit->appendPlainText(str); //添加为文本框的一行//获取数据区的每行for (int i=0;i<m_model->rowCount();i++){str="";for(int j=0; j<m_model->columnCount()-1;j++){aItem=m_model->item(i,j);str=str+aItem->text()+QString::asprintf("\t"); //以 TAB分隔}aItem=m_model->item(i,FixedColumnCount-1); //最后一行是逻辑型if (aItem->checkState()==Qt::Checked)str=str+"1";elsestr=str+"0";ui->plainTextEdit->appendPlainText(str);}
}


http://www.ppmy.cn/news/1554895.html

相关文章

基于Ubuntu系统,2台服务器使用Docker搭建MinIO集群及验证方法

MinIO集群简介 MinIO是一个高性能的分布式对象存储系统&#xff0c;兼容Amazon S3&#xff08;Simple Storage Service&#xff09;云存储服务的API。它支持横向扩展&#xff0c;提供高可用性和数据冗余&#xff0c;非常适合处理大量非结构化数据&#xff0c;如图片、视频、日志…

高级java每日一道面试题-2024年12月12日-数据库篇-mysql 深度分页如何优化?

如果有遗漏,评论区告诉我进行补充 面试官: mysql 深度分页如何优化? 我回答: 在Java高级面试中&#xff0c;关于MySQL深度分页优化的提问&#xff0c;是一个考察数据库性能优化能力和对MySQL索引、查询机制理解深度的问题。以下是对MySQL深度分页优化的详细解答&#xff1a…

jQuery理论

jQuery定义 jQuery是一个快速、简洁的JavaScript库&#xff0c;它能够简化HTML文档遍历、事件处理、动画设计和Ajax交互。jQuery的设计目标是通过尽可能少的代码实现更多的功能&#xff0c;并且提供一种优雅的方式来处理HTML文档、处理事件、创建动画效果以及实现Ajax交互。. …

docker tdengine windows快速体验

#拉取镜像 docker pull tdengine/tdengine:2.6.0.34#容器运行 docker run -d --name td2.6 --restartalways -p 6030:6030 -p 6041:6041 -p 6043:6043 -p 6044-6049:6044-6049 -p 6044-6045:6044-6045/udp -p 6060:6060 tdengine/tdengine:2.6.0.34#容器数据持久化到本地 #/va…

速通前端篇 —— HTML

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a;速通前端 目录 HTML的介绍 如何创建HTML文件 HTML 文件基本结构 HTML常用标签 title标签 标题标签 h1-h6 段落标签 p 换行标签 b…

python数据采集-URL编码处理

1. 导入必要的库 - urllib.request&#xff1a;用于发送HTTP请求。 - urllib.parse&#xff1a;用于对URL进行编码。 - fake_useragent.UserAgent&#xff1a;用于生成随机的用户代理&#xff0c;模拟真实的浏览器行为。 - webbrowser&#xff1a;用于在浏览器中打开文…

ffmpeg使用自定义字体添加字幕

一&#xff0c;背景与问题描述 背景 最近工作项目要求&#xff0c;需要使用SRT字幕文件&#xff0c;为对应的视频添加字幕&#xff0c;并且需要使用指定的字体&#xff08;如Roboto、Poppins等&#xff09; 调研发现&#xff0c;使用以下命令可以为视频添加字幕 ffmpeg -i …

MacOS 下 pico/pico2 学习笔记

1.安装开发工具 cmake brew install cmakeopenocd brew install openocdarm-none-eabi-gcc 用 brew 安装的版本会出现如下错误&#xff1a; arm-none-eabi-gcc: fatal error: cannot read spec file nosys.specs: No such file or directory用 arm 官方的版本没有问题。 cd …