Qt Model-View 架构

Model/View(模型/视图)结构是Qt中用界面组件显示与编辑数据的一种结构,视图(view)是显示和编辑数据的界面组件,而Model(模型)则是视图和原始数据之间的接口。Model/View典型应用是在数据库应用程序中。比如数据库中的一个数据表可以在一个TableView组件中显示和编辑。

Model/View基本原理

GUI(图形用户界面)应用程序(使用图形界面与用户进行可视化交互)一个很重要的特点就是由用户直接在界面上编辑和修改数据,典型的就是数据库应用程序(直接与数据库的数据进行交互),用户在界面上进行各种操作,实际上是修改了界面组件所关联的数据库数据。

而Model-View架构则实现了将用户界面与数据管理分离,又通过数据源的方式连接起来。就是因为Model-View结构将数据源与显示界面分离开来,所以可以将一个数据模型放在不同的视图中显示,不同的视图可以针对同一数据模型设计不同的用户界面,大大提高了可视化的灵活性,也体现了数据模型的重用性。

下面显示的是Model-View的基本架构(来自官方文档)

b42a274d99e640dba84fa8471a51631d.png

Model-View架构中,模型与数据源进行通信,为结构中的其他组件提供接口。通信的性质取决于数据源的类型和模型的实现方式(例如,从数据库中获取数据需要用到SQL语句,而从API中获得数据则可能要发送HTTP请求)。View从Model中获得索引(index),这些索引是数据项的引用,通过这些索引,视图可以借助模型检索数据源中的数据。

数据模型

所有基于项数据(item data)的数据模型都是基于QAbstractItemModel这个基类的,这个基类定义了视图组件和代理存取数据的接口。数据不需要存储在模型里面,模型只需访问数据源接口。

Qt提供了很多模型类进行项数据处理,本文详细介绍以下三个:

f12070bf7a5647c38ff2cc5bf49423b5.png

模型索引

QModelIndex表示模型索引的类。QModelIndex提供数据存取的一个临时指针,在视图中,通过模型索引定位显示的项,当用户操作此项时,视图会使用该模型索引与模型进行交互。比如:

//获取数据
QModelIndex index = model->index(row, column, parentIndex);
QVariant data = model->data(index);
//设置数据
model->setData(index, newValue);
//查找父项
QModelIndex parentIndex = index.parent();

因为模型内部组织数据的结构随时可能改变,所以该模型索引是临时指针。

视图组件

视图组件在显示数据时候,只需要调用视图类的setModel()函数,为视图组件设置一个数据模型,就可以让视图组件和模型相关联,在视图组件上的修改将自动保存到关联的数据模型里面。视图组件类的数据采用单独的数据模型,不存储数据(itemModel),用项的方式集成了数据模型功能。

本文将介绍的数据视图有:

f2f4b7d347bb4dc8868b9abb092ea7f7.png

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记录当前点击的文件名称。

部分运行展示如下:

63a7df810cee443a8e33ecb12e214c55.png

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();
    }

    程序部分运行图:

  • af3e5f4920514aaa8290d97294e037f6.png

  • 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 架构及其相关知识点,无疑将成为开发人员打造优质、高效应用程序的必备技能之一。

若有讲述不当,还请多多指教!🐣🐣🐣.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值