Model/View(模型/视图)结构是Qt中用界面组件显示与编辑数据的一种结构,视图(view)是显示和编辑数据的界面组件,而Model(模型)则是视图和原始数据之间的接口。Model/View典型应用是在数据库应用程序中。比如数据库中的一个数据表可以在一个TableView组件中显示和编辑。
Model/View基本原理
GUI(图形用户界面)应用程序(使用图形界面与用户进行可视化交互)一个很重要的特点就是由用户直接在界面上编辑和修改数据,典型的就是数据库应用程序(直接与数据库的数据进行交互),用户在界面上进行各种操作,实际上是修改了界面组件所关联的数据库数据。
而Model-View架构则实现了将用户界面与数据管理分离,又通过数据源的方式连接起来。就是因为Model-View结构将数据源与显示界面分离开来,所以可以将一个数据模型放在不同的视图中显示,不同的视图可以针对同一数据模型设计不同的用户界面,大大提高了可视化的灵活性,也体现了数据模型的重用性。
下面显示的是Model-View的基本架构(来自官方文档)
Model-View架构中,模型与数据源进行通信,为结构中的其他组件提供接口。通信的性质取决于数据源的类型和模型的实现方式(例如,从数据库中获取数据需要用到SQL语句,而从API中获得数据则可能要发送HTTP请求)。View从Model中获得索引(index),这些索引是数据项的引用,通过这些索引,视图可以借助模型检索数据源中的数据。
数据模型
所有基于项数据(item data)的数据模型都是基于QAbstractItemModel这个基类的,这个基类定义了视图组件和代理存取数据的接口。数据不需要存储在模型里面,模型只需访问数据源接口。
Qt提供了很多模型类进行项数据处理,本文详细介绍以下三个:
模型索引
QModelIndex表示模型索引的类。QModelIndex提供数据存取的一个临时指针,在视图中,通过模型索引定位显示的项,当用户操作此项时,视图会使用该模型索引与模型进行交互。比如:
//获取数据
QModelIndex index = model->index(row, column, parentIndex);
QVariant data = model->data(index);
//设置数据
model->setData(index, newValue);
//查找父项
QModelIndex parentIndex = index.parent();
因为模型内部组织数据的结构随时可能改变,所以该模型索引是临时指针。
视图组件
视图组件在显示数据时候,只需要调用视图类的setModel()函数,为视图组件设置一个数据模型,就可以让视图组件和模型相关联,在视图组件上的修改将自动保存到关联的数据模型里面。视图组件类的数据采用单独的数据模型,不存储数据(itemModel),用项的方式集成了数据模型功能。
本文将介绍的数据视图有:
1、QFileSystemModel&QTreeView
QFileSystemModel
是 Qt 框架中用于处理文件系统相关操作,并在视图中展示文件系统结构和内容的模型类。用来访问和操作本地文件系统中的文件和文件夹,使得开发者可以轻松地将文件系统信息呈现给用户,并允许用户进行诸如浏览、选择文件或文件夹等操作。
常用方法
(1)设置根路径
setRootPath(const QString &path)
(2)获取文件或文件夹信息
-
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const
:该方法用于获取指定行、列以及父索引对应的文件或文件夹在模型中的索引。通过这个索引,可以进一步获取文件或文件夹的详细信息,或者在视图中定位到对应的项目。 -
QString filePath(const QModelIndex &index) const
:根据给定的模型索引,返回对应的文件或文件夹的完整路径。这在需要获取文件的实际路径以便进行后续操作(如打开文件、复制文件等)时非常有用。 -
QString fileName(const QModelIndex &index) const
:返回给定模型索引对应的文件或文件夹的名称。 -
qint64 fileSize(const QModelIndex &index) const
:获取给定模型索引对应的文件的大小(以字节为单位)。 -
QDateTime lastModified(const QModelIndex &index) const
:返回给定模型索引对应的文件或文件夹的最后修改时间。
(3)文件和文件夹操作
bool mkdir(const QModelIndex &parent, const QString &name)
:在指定的父索引对应的文件夹下创建一个名为name
的新文件夹。如果操作成功,返回true
;否则,返回false
。bool rename(const QModelIndex &index, const QString &newName)
:对给定模型索引对应的文件或文件夹进行重命名操作,将其名称改为newName
。同样,成功则返回true
;否则,返回false
。bool remove(const QModelIndex &index)
:删除给定模型索引对应的文件或文件夹。返回值表示删除操作是否成功。
案例实现:
主函数代码如下:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
QFileSystemModel *model=new QFileSystemModel;
model->setRootPath(QDir::currentPath());//设置当前文件路径
ui->treeView->setModel(model);//模型载入
ui->listView->setModel(model);
ui->tableView->setModel(model);
QLabel *label;//用来显示当前文件名的标签
label=new QLabel;
ui->statusbar->addWidget(label);//设置状态栏
//点击terrview信号连接
connect(ui->treeView,&QTreeView::clicked,ui->tableView,&QTableView::setRootIndex);
connect(ui->treeView,&QTreeView::clicked,ui->listView,&QListView::setRootIndex);
//lable名称设置
connect(ui->treeView,&QTreeView::clicked,[=](const QModelIndex &index){
QString fileName = model->fileName(index);
label->setText(QString("Selected File: %1").arg(fileName));
});
}
案例要求:在ui界面上分别设置三个view结构,用来展示文件模型信息。点击QtreeView上的文件索引,另外两个view结构也会相应显示当前根节点文件展示变化。同时左下角也会有lable记录当前点击的文件名称。
部分运行展示如下:
2、QStringListModel&QListView
常用方法:
(1)初始化设置字符串列表数据
setStringList(const QStringList &stringList)
:用于将一个新的QStringList
设置为模型的数据。这在需要更新模型中的字符串列表内容时很有用,比如从文件读取新的字符串列表后,通过此方法将其设置给模型,以便视图能展示新的内容。
(2)获取字符串列表数据
stringList() const
:返回模型当前管理的字符串列表。可以用于获取当前模型中的所有字符串,以便进行后续的处理或分析等操作。
(3)获取特定位置的字符串
data(const QModelIndex &index, int role = Qt::DisplayRole) const
:该方法用于获取指定索引位置的字符串。其中,index
是模型中的索引,通过视图的操作(如点击、选择等)通常可以获取到对应的索引;role
默认为Qt::DisplayRole
,表示获取用于展示的字符串数据。一般情况下,通过传入合适的索引,就可以获取到对应位置的字符串。
(4)添加和删除字符串
(3)修改项目属性
(4)增加删除项目
使用appendRow
方法
insertRows(int row, int count, const QModelIndex &parent = QModelIndex())
:用于在指定的行位置插入指定数量的字符串。removeRows(int row, int count, const QModelIndex &parent = QModelIndex())
:用于从指定的行位置删除指定数量的字符串。-
案例实现:
主函数代码如下
-
#include "mainwindow.h" #include "ui_mainwindow.h" #include<QStringListModel> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); mode=new QStringListModel(this); QStringList list; list<<"Default list"<<"1"<<"2"<<"3"<<"4"<<"5"<<"6"; mode->setStringList(list); ui->listView->setModel(mode); } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_pushButton_clicked()//列表恢复 { QStringList list; list<<"Restore list"<<"1"<<"2"<<"3"<<"4"<<"5"<<"6"; mode->setStringList(list); } void MainWindow::on_pushButton_2_clicked()//增添行 { mode->insertRow(mode->rowCount());//设置模型总行数 int x=mode->rowCount(); QModelIndex index=mode->index(x-1);//x-1是最后一项 mode->setData(index,"add row"); ui->listView->setCurrentIndex(index); } void MainWindow::on_pushButton_3_clicked() { int x=ui->listView->currentIndex().row(); mode->insertRow(x); QModelIndex index=mode->index(x); mode->setData(index,"insert row"); ui->listView->setCurrentIndex(index); } void MainWindow::on_pushButton_4_clicked() { mode->removeRow(ui->listView->currentIndex().row()); } void MainWindow::on_pushButton_5_clicked() { mode->removeRows(0,mode->rowCount()); } void MainWindow::on_pushButton_6_clicked() { ui->plainTextEdit->clear(); QStringList str=mode->stringList(); foreach(auto &s,str) { ui->plainTextEdit->appendPlainText(s); } } void MainWindow::on_pushButton_7_clicked() { ui->plainTextEdit->clear(); }
程序部分运行图:
-
-
3、QStandardItemModel&QTableView
QStandardItemModel
通过创建和管理QStandardItem
对象来构建数据结构,并为视图提供所需的数据和操作接口,使得视图能够准确地呈现出数据的层次关系和具体内容。- 在我认为本质上这就是个”升级版“的list结构。
-
常用方法:
-
(1)设置表头数据
- 对于表格形式的模型,需要设置表头数据来明确各列的含义。可以使用以下方法:
-
QStringList listVertical; listVertical<<"第一行"<<"第二行"<<"第三行"; model->setVerticalHeaderLabels(listVertical); QStringList listHorizontal; listHorizontal<<"第一列"<<"第二列"<<"第三列"; model->setHorizontalHeaderLabels(listHorizontal);
这样就初始化生成了一个3*3的表格。
-
(2)获取项目信息
QStandardItem *item(const QModelIndex &index)
:根据给定的模型索引,返回对应的QStandardItem
对象。通过这个对象可以获取项目的各种属性,如项目的文本内容(text()
方法)、图标(icon()
方法)、是否可编辑(isEditable()
方法)等。例如:-
QModelIndex index = model.index(0, 0); QStandardItem *item = model.item(index); QString text = item->text();
QString text(const QModelIndex &index)
:直接返回给定模型索引对应的项目的文本内容,是获取项目文本信息的一种简便方法。 - 对于获取到的
QStandardItem
对象,可以通过其自身的方法来修改项目的各种属性。例如:- 要修改项目的文本内容:
item->setText("新的文本内容");
- 要修改项目的图标:
item->setIcon(QIcon("新的图标路径"));
- 要设置项目是否可编辑:
item->setEditable(true);
- 要修改项目的文本内容:
appendRow
方法用于在QStandardItemModel
的末尾添加一行数据。它接受一个QList<QStandardItem *>
类型的参数,该列表中的每个QStandardItem
对象对应着新添加行中的一个单元格数据。- 示例代码:
-
QStandardItem *item1 = new QStandardItem("4-1"); QStandardItem *item2 = new QStandardItem("4-2"); QStandardItem *item3 = new QStandardItem("4-3"); QList<QStandardItem *> rowItems; rowItems << item1<< item2<< item3; model->appendRow(rowItems);
使用
insertRow
方法insertRow
方法可以在指定位置插入一行数据。它同样接受一个QList<QStandardItem *>
类型的参数来表示新插入行的单元格数据,只是还需要指定插入的行位置索引。-
QStandardItem *item1 = new QStandardItem("4-1"); QStandardItem *item2 = new QStandardItem("4-2"); QStandardItem *item3 = new QStandardItem("4-3"); QList<QStandardItem *> rowItems; rowItems << item1<< item2<< item3; model->insertRow(3,rowItems);
-
使用
removeRow
方法removeRow
方法用于从QStandardItemModel
中删除指定行的数据。需要指定要删除的行位置索引作为参数。-
model->removeRow(3);
(5)、设置Mode常用属性
设置行数列数:
model->setRowCount(5);
model->setColumnCount(5);
这样也相当于初始化了一个5*5的表,只不过没有设置初始化item。
(此处省略示意案例。)
结语
综上所述,Model-View 架构在软件开发领域中扮演着至关重要的角色。它通过清晰地划分数据管理与展示交互的职责,为开发人员提供了一种高效、灵活且易于维护的设计模式。
借助各种类型的模型(如 QStandardItemModel、QFileSystemModel、QStringListModel 等)对不同结构的数据进行精准把控,以及多样化的视图(如 QTreeView、QListView、QTableView 等)实现丰富多样的展示形式,再配合选择模型来精细化管理用户选择操作,同时依靠信号与槽机制确保各组件间的实时通信与数据一致性。
Model-View 架构通过分离数据管理和展示交互的功能,结合信号与槽机制以及选择模型等辅助手段,为开发高效、可维护、可扩展的应用程序提供了有力的支持。
这一系列的机制协同运作,不仅使得应用程序能够更好地处理复杂的数据场景,满足用户多样化的交互需求,而且极大地提升了代码的可扩展性和可维护性。随着软件开发的不断发展,深入理解和熟练运用 Model-View 架构及其相关知识点,无疑将成为开发人员打造优质、高效应用程序的必备技能之一。
若有讲述不当,还请多多指教!🐣🐣🐣.