当前位置: 首页 > news >正文

Qt模型/视图结构

文章目录

  • 一、模型/视图结构概述
  • 二、QStringListModel 和 QListView
  • 三、QStandardItemModel 和 QTableView
  • 四、自定义代理
  • 五、QFileSystemModel 和 QTreeView


一、模型/视图结构概述

模型/视图结构是一种将数据存储和界面展示分离的编程方法。模型存储数据,视图组件显示模型中的数据,在视图组件里修改的数据会被自动保存到模型里。模型的数据来源可以是内存中的字符串列表或二维表格型数据,也可以是数据库中的数据表,一种模型可以用不同的视图组件来显示数据。

如在数据库应用程序中,界面上的数据来源于数据库,用户在界面上修改数据,修改后的数据又保存到数据库。 Qt 使用模型/视图结构来处理这种关系,模型/视图的基本结构如图所示:

示例图片

它包括以下几个部分:

  • 源数据(data)是原始数据,如数据库的一个数据表或 SQL 查询结果、内存中的一个字符串列表或磁盘文件系统结构等。
  • 视图(view)也称为视图组件,是界面组件,视图从模型获得数据然后将其显示在界面上。如QListView、QTreeView 和 QTableView 等。
  • 模型(model)也称为数据模型,它从源数据提取需要的数据,用于视图组件进行显示和编辑。如 QStringListModel 是字符串列表的模型类,QSqlTableModel 是数据库中数据表的模型类。
  • 代理(delegate)在视图与模型之间交互操作时提供的临时编辑器。模型向视图提供数据是单向的,一般仅用于显示。当需要在视图上编辑数据时,代理会为编辑数据提供一个编辑器,这个编辑器获取模型的数据、接受用户编辑的数据后又将其提交给模型。例如在 QTableView 组件上双击一个单元格来编辑数据时,在单元格里就会出现一个 QLineEdit 组件,这个编辑框就是代理提供的临时编辑器。

由于通过模型/视图结构将源数据与显示和编辑界面分离,我们可以将一个模型在不同的视图中显示,也可以为一些特殊源数据设计自定义模型,或者在不修改模型的情况下设计特殊的视图组件。模型、视图和代理使用信号和槽进行通信。当源数据发生变化时,模型发射信号通知视图组件;当用户在界面上操作数据时,视图组件发射信号表示操作信息;在编辑数据时,代理会发射信号告知模型和视图组件编辑器的状态。

所有基于项(item)的模型类都是基于 QAbstractItemModel 类的,这个类定义了视图组件和代理存取数据的接口。模型只是在内存中临时存储数据,模型的数据来源可以是其他类、文件、数据库或任何数据源。Qt中几个主要的模型类的继承关系如图所示:

示例图片

QAbstractItemModel 的父类是 QObject,它是抽象模型类,不能直接用于创建实例对象。常用的几个模型类如表所示:

示例图片

视图就是用于显示模型中的数据的界面组件,Qt提供的视图组件主要有以下几个:

  • QListView:用于显示单列的列表数据,适用于一维数据的操作。
  • QTreeView:用于显示树状结构数据,适用于树状结构数据的操作。
  • QTableView:用于显示表格数据,适用于二维表格数据的操作。
  • QColumnView:用多个 QListView 显示树状结构数据,树状结构的一层用一个QListView 显示。
  • QUndoView:用于显示 undo 指令栈内数据的视图组件,是QListView的子类。

只需调用视图类的 setModel() 函数为视图组件设置一个模型,模型的数据就可以显示在视图组件上。在视图组件上修改数据后,数据可以自动保存到模型里。

代理就是在视图组件上为编辑数据提供的临时编辑器,例如在 QTableView 组件上编辑一个单元格的数据时,默认会提供一个 QLineEdit 编辑框。代理负责从模型获取相应的数据,然后将其显示在编辑器里,修改数据后又将编辑器里的数据保存到模型中。QAbstractItemDelegate 是所有代理类的基类,作为抽象类,它不能直接用于创建对象。它有两个子类,即 QItemDelegate 和 QStyledItemDelegate ,这两个类的功能基本相同,而 QStyledItemDelegate 能使用 Qt 样式表定义的当前样式绘制代理组件,所以,QStyledItemDelegate 是视图组件使用的默认的代理类。

在模型/视图结构中,QAbstractItemModel 是所有模型类的基类,不管底层的数据结构是如何组织数据的,QAbstractItemModel 的子类都以表格的层次结构展示数据,视图组件按照这种规则来存取模型中的数据,但是展示给用户的形式不一样。如图所示的是模型的 3 种常见展示形式,分别是列表模型(list model)、表格模型(table model)和树状模型(tree model):

示例图片

不管模型的表现形式是怎样的,模型中存储数据的基本单元都是项(item),每个项有一个行号和一个列号,还有一个父项(parent item)。3 个模型都有一个隐藏的根项(root item),列表模型的存储结构就是一列,表格模型的存储结构是规则的二维数组,树状模型的项可以有子项,结构复杂一点。

模型中引入了模型索引(model index)的概念。通过模型能访问的每个项都有一个模型索引,视图组件和代理都通过模型索引来获取数据。QModelIndex 是表示模型索引的类。模型索引提供访问数据的临时指针,用于通过模型提取或修改数据。模型索引是临时的,例如对于一个 QTreeView 组件,获得一个节点的模型索引后又修改了模型的数据,那么前面获得的那个模型索引可能就不再指向原来那个节点了。

模型的基本形式是用行和列定义的表格数据,但这并不意味着底层的数据是用二维数组存储的,使用行和列只是为了组件之间交互方便。一个模型索引包含行号和列号。要获得一个模型索引,必须提供 3 个参数:行号、列号、父项的模型索引。例如,对于上图中的表格模型中的 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 的父节点是顶层节点,获取模型索引的代码是:

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

节点 B 的父节点是节点 A,节点 B 的模型索引由下面的代码生成:

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

在为模型的一个项设置数据时,可以为项设置不同角色的数据。QAbstractItemModel 类定义了设置项的数据的函数 setData(),其函数原型定义如下:

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

其中,index 是项的模型索引,value 是需要设置的数据,role 是设置数据的角色。可以为一个项设置不同角色的数据,角色参数 role 用枚举类型 Qt::ItemDataRole 的枚举值表示。

示例图片

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

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

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

注意:在模型/视图结构中,添加或删除数据都是由模型类的接口函数实现的,视图组件只是用于显示数据,没有操作数据的接口函数。在 QListView 或 QTableView 等视图组件上双击编辑一行字符串或一个单元格时由代理临时提供编辑器,编辑后的数据保存到模型里。

函数 rowCount() 返回行数,columnCount() 返回列数,两个函数的原型定义如下:

int rowCount(const QModelIndex &parent = QModelIndex()) 
int columnCount(const QModelIndex &parent = QModelIndex())

这两个函数中都需要传递一个参数 parent,这是父项的模型索引。对于列表模型和表格模型,parent 使用默认的参数 QModelIndex()即可,得到的行数和列数就是模型的行数和列数。对于树状模型,parent 需要设置为父节点的模型索引,函数返回的是父节点下的节点的行数和列数。

可以用以下函数在模型中插入或删除一个或多个数据行:

bool insertRow(int row, const QModelIndex &parent = QModelIndex()) 
bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) 
bool removeRow(int row, const QModelIndex &parent = QModelIndex()) 
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex())

可以用以下函数在模型中插入或删除一个或多个数据列:

bool insertColumn(int column, const QModelIndex &parent = QModelIndex()) 
bool insertColumns(int column, int count, const QModelIndex &parent = QModelIndex()) 
bool removeColumn(int column, const QModelIndex &parent = QModelIndex()) 
bool removeColumns(int column, int count, const QModelIndex &parent = QModelIndex())

函数 moveRow() 可以移动一个行,函数 moveColumn() 可以移动一个列:

bool moveRow(const QModelIndex &sourceParent, int sourceRow, const QModelIndex &destinationParent, int destinationChild) 
bool moveColumn(const QModelIndex &sourceParent, int sourceColumn, const QModelIndex &destinationParent, int destinationChild)

函数 sort() 将数据按某一列排序,可指定排序方式,默认是升序方式:

void sort(int column, Qt::SortOrder order = Qt::AscendingOrder)

函数 setData() 为一个项设置数据,函数 data() 返回一个项的数据,其函数原型定义如下:

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

函数 clearItemData() 用于清除一个项的所有角色的数据,函数定义如下:

bool clearItemData(const QModelIndex &index)

QAbstractItemModel 的这些函数一般都是虚函数,子类会重新实现其需要用到的函数,以符合模型类的具体操作。

下面来看 QAbstractItemView 类,它是所有视图组件类的父类,定义了视图组件类共有的一些接口。需要为视图组件设置数据模型才能构成完整的模型/视图结构,相关函数定义如下:

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

不同的视图组件使用不同类型的模型,QListView 组件一般用 QStringListModel 对象作为数据模型,用于编辑字符串列表;QTableView 一般用 QStandardItemModel 对象作为数据模型,用于编辑表格数据。视图组件还可以设置选择模型,在界面上选择的项发生变化时,通过选择模型可以获取所有被选择项的模型索引。例如,QTableView 在允许选择多个单元格时,使用 QItemSelectionModel 类对象作为选择模型就比较有用,可以获得所有被选单元格的模型索引,从而能方便地对所选择的项进行处理。相关函数定义如下:

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

QAbstractItemView 类定义了一些属性,这些属性是子类 QListView 和 QTableView 共有的。

  • editTriggers 属性。表示视图组件是否可以编辑数据,以及进入编辑状态的方式。
  • alternatingRowColors 属性。这个属性设置各行是否交替使用不同的背景色。如果设置为 true,会使用系统默认的一种颜色。如果要自定义背景色,需要用 Qt 样式表。
  • selectionMode 属性。这个属性表示在视图组件上选择项的操作模式,对于 QTableView 比较有意义。
  • selectionBehavior 属性。这个属性表示点击鼠标时选择操作的行为,对于 QTableView 比较有意义。

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

QModelIndex currentIndex() //返回当前项的模型索引,例如当前单元格的模型索引
void setCurrentIndex(const QModelIndex &index) //设置模型索引为 index 的项为当前项
void selectAll() //选择视图中的所有项,例如选择 QTableView 组件中的所有单元格
void clearSelection() //清除所有选择

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 作为视图组件,构成模型/视图结构,编辑字符串列表。示例代码:

QStringList m_strList; //保存初始字符串列表内容
QStringListModel *m_model; //数据模型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); //初始化数据ui->listView->setModel(m_model); //设置数据模型ui->chkEditable->setChecked(true); ui->listView->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::SelectedClicked); 
}

窗口基类是 QMainWindow,左侧的分组框里是一些按钮和一个 QListView 组件,右侧的分组框里是两个按钮和一个 QPlainTextEdit 组件。程序运行后,界面上的 listView 里就会显示初始化的字符串列表的内容:

示例图片

QStringListModel 是字符串列表数据的模型类,与 QListView 组件搭配组成模型/视图结构,适合处理字符串列表数据。QStringListModel 有两种参数形式的构造函数,定义如下:

QStringListModel(const QStringList &strings, QObject *parent = nullptr) 
QStringListModel(QObject *parent = nullptr)

QStringListModel 对象内部有一个字符串列表,对模型数据的修改就是对 QStringListModel 对象内部字符串列表的修改。QStringListModel 新定义的函数只有两个,定义如下:

void setStringList(const QStringList &strings) //设置字符串列表,初始化模型数据
QStringList stringList() //返回模型内部的字符串列表

对数据的操作都是通过数据模型的接口函数实现的:

void MainWindow::on_btnListInsert_clicked() {// “插入项”按钮QModelIndex index= ui->listView->currentIndex(); //当前项的模型索引m_model->insertRow(index.row()); m_model->setData(index,"inserted item",Qt::DisplayRole); ui->listView->setCurrentIndex(index); 
} 
void MainWindow::on_btnListDelete_clicked() {// “删除项”按钮QModelIndex index= ui->listView->currentIndex(); //获取当前项的模型索引m_model->removeRow(index.row()); 
}

在数据模型 m_model 中添加或删除项后,界面组件 listView 中会立刻自动将其显示出来。

在这个示例中,QStringListModel 和 QListView 结合组成了模型/视图结构,对字符串列表数据进行编辑。在测试过程中发现不能为 listView 显示的列表显示图标和复选框,不能设置文字对齐方式,即使用函数 setData() 设置了 DecorationRole、TextAlignmentRole 等角色的数据。这是因为 QStringListModel 内部仅保存字符串列表,并没有数据结构保存其他角色的数据。如果需要每一行带复选框的列表框,还是要使用 QListWidget。

三、QStandardItemModel 和 QTableView

QTableView 继承自 QAbstractItemView 类,新定义的属性主要用于控制显示效果,在 UI 可视化设计时就可以设置 QTableView 组件的各种属性。QTableView 组件有水平表头和垂直表头,都是 QHeaderView 对象,可以设置和返回表头对象,相关函数定义如下:

void QTableView::setHorizontalHeader(QHeaderView *header) //设置水平表头
void QTableView::setVerticalHeader(QHeaderView *header) //设置垂直表头
QHeaderView *QTableView::horizontalHeader() //返回水平表头对象指针
QHeaderView *QTableView::verticalHeader() //返回垂直表头对象指针

当 QTableView 组件使用一个 QStandardItemModel 对象作为数据模型时,它会自动创建表头对象,垂直表头一般显示行号,水平表头一般显示列的标题。

QStandardItemModel 是以项为基本数据单元的模型类,每个项是一个 QStandardItem 对象。项可以存储各种角色的数据,如文字、字体、对齐方式、图标、复选状态等。如果以多行多列的二维数组形式存储项,就是表格模
型;如果表格模型只有一列,就是列表模型;如果在存储项时为项指定父项,就可以构成树状模型。如果一个 QStandardItemModel 对象是表格模型,将它设置为 QTableView 组件的数据模型后,视图组件就用表格的形式显示模型的数据,并且根据每个单元格的项定义的各种角色数据控制显示效果。对数据的操作是通过 QStandardItemModel 类的接口函数实现的。QStandardItemModel 的父类是
QAbstractItemModel,QStandardItemModel 新定义了一些接口函数,下面介绍常用的一些接口函数。

  • QStandardItemModel 以二维数组的形式存储项数据,所以可以设置行数和列数:
void setRowCount(int rows) //设置数据模型的行数
void setColumnCount(int columns) //设置数据模型的列数

如果设置的列数大于 1,模型就是表格模型;如果设置的列数为 1,模型就可以看作列表模型。

  • 设置了模型的行数和列数后,就相当于设置了模型的表格大小,还需要用函数 setItem() 为表格的每个单元设置一个 QStandardItem 对象:
void setItem(int row, int column, QStandardItem *item) //用于表格模型
void setItem(int row, QStandardItem *item) //用于列表模型
  • 函数 item() 根据行号和列号返回模型中某个单元的项,函数 itemFromIndex() 根据模型索引返回某个单元的项,这两个函数定义如下:
QStandardItem *item(int row, int column = 0) //根据行号和列号返回项
QStandardItem *itemFromIndex(const QModelIndex &index) //根据模型索引返回项
QModelIndex indexFromItem(const QStandardItem *item) //根据项返回其模型索引
  • 函数 appendRow() 用于在模型最后添加一行,并且为添加行的每个单元设置 QStandardItem 对象:
void appendRow(const QList<QStandardItem *> &items) //用于表格模型
void appendRow(QStandardItem *item) //用于列表模型
void appendColumn(const QList<QStandardItem *> &items) //在表格模型中添加列
  • 函数 insertRow()用于在模型中插入一行,有 3 种参数形式,其中的 2 种定义如下:
void insertRow(int row, const QList<QStandardItem *> &items) //用于表格模型
void insertRow(int row, QStandardItem *item) //用于列表模型

下面这种参数形式的函数适用于树状模型,参数 parent 是父节点的模型索引,row 是插入位置的行号。这种参数形式的函数的功能是在父节点下面插入一个节点,但是没有为节点设置 QStandardItem 对象,需要再调用函数 setItem() 为新插入的节点设置 QStandardItem 对象:

bool insertRow(int row, const QModelIndex &parent = QModelIndex()) //用于树状模型

函数 insertColumn() 用于在模型中插入列,只有表格模型或树状模型才需要插入列:

void insertColumn(int column, const QList<QStandardItem *> &items) //用于表格模型
bool insertColumn(int column, const QModelIndex &parent = QModelIndex()) //用于树状模型
  • 可以从表格模型中移除一行或一列,模型的行数或列数就会相应减 1,但是移除的 QStandardItem 对象不会被删除,需要单独用 delete 删除。移除行或列的两个函数定义如下,返回值是被移除的 QStandardItem 对象列表:
QList<QStandardItem *> takeRow(int row) //移除一行,适用于表格模型
QList<QStandardItem *> takeColumn(int column) //移除一列,适用于表格模型

函数 takeItem()用于移除一个项,它适用于列表模型:

QStandardItem *takeItem(int row, int column = 0) //移除一个项,适用于列表模型
  • 数据模型有水平表头,水平表头的列数等于表格模型的列数,表头的每个单元也是 QStandardItem 对象,可以用函数 setHorizontalHeaderItem() 为表头的某一列设置项:
void setHorizontalHeaderItem(int column, QStandardItem *item) //为表头某列设置项
void setHorizontalHeaderLabels(const QStringList &labels) // 用一个字符串列表的内容设置表头各列的文字
QStandardItem *horizontalHeaderItem(int column) //返回水平表头中的一个项
QStandardItem *takeHorizontalHeaderItem(int column) //移除水平表头中的一个项
void setVerticalHeaderItem(int row, QStandardItem *item) 
void setVerticalHeaderLabels(const QStringList &labels) 
QStandardItem *verticalHeaderItem(int row) 
QStandardItem *takeVerticalHeaderItem(int row)
  • QStandardItemModel 有一个接口函数 clear(),它用于清除模型内的所有项,行数和列数都会
    变为 0。
  • QStandardItemModel 新定义了一个信号 itemChanged(),在任何一个项的数据发生变化时,此信号就会被发射。信号函数定义如下,其中的参数 item 是数据发生了变化的项:
void itemChanged(QStandardItem *item)

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

示例图片

可以用 QStandardItem::setData() 函数设置各种角色的数据,函数定义如下:

void QStandardItem::setData(const QVariant &value, int role = Qt::UserRole + 1)

QStandardItem 的一些单独的函数相当于设置了项的一些固定角色的数据,例如函数 setText() 设置了 Qt::DisplayRole 角色的数据,函数 setToolTip()设置了 Qt::ToolTipRole 角色的数据。所以,setData() 一般用于设置用户自定义数据,可以设置多个用户数据,从 Qt::UserRole 递增角色数值即可。

函数 data() 返回指定角色的数据,函数 clearData() 清除用 setData() 函数设置的所有角色的数据:

QVariant QStandardItem::data(int role = Qt::UserRole + 1) 
void QStandardItem::clearData()

一个 QStandardItem 对象可以添加子项,子项也是 QStandardItem 对象,这样就可以构造不限层级的树状结构,也就是树状模型。QStandardItem 以二维表格的形式管理子项,可以添加多行多列的子项。QStandardItem 管理子项的函数有如下这些:

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) //插入一行,只有一个项
void insertRows(int row, int count) //在 row 行插入 count 个空行,未设置 item 
void insertColumn(int column, const QList<QStandardItem *> &items) //插入一列多个项
void insertColumns(int column, int count) //在column列插入count个空列,未设置item 
void removeColumn(int column) //删除序号为 column 的列
void removeColumns(int column, int count) //从 column 列开始,删除 count 个列
void removeRow(int row) //删除序号为 row 的行,存储的项也被删除
void removeRows(int row, int count) //从 row 行开始,删除 count 个行
int rowCount() //返回子项的行数
int columnCount() //返回子项的列数
bool hasChildren() //这个项是否有子项
QStandardItem *child(int row, int column = 0) //根据行号和列号返回子项

QStandardItem 的很多函数与 QStandardItemModel 中的函数名称相同,功能也相同,只是操作对象不同。QStandardItemModel 管理的是模型的顶层项,如果是列表模型或表格模型,各个项没有子项,QStandardItemModel 就直接管理模型中的所有项。如果是树状模型,那么 QStandardItemModel 管理的就是所有顶层项(不是根项),也就是目录树中的一级节点,而各个一级节点的直接子节点则通过 QStandardItem 类来管理,依次递推下去,就可以形成不限层级的树状模型。

一个视图组件需要设置一个数据模型,还可以设置一个选择模型。QItemSelectionModel 是选择模型类,它的功能是跟踪视图组件上的选择操作,给出选择范围。例如,给 QTableView 组件设置一个选择模型后,在 QTableView 组件上选择多个单元格时,通过选择模型就可以得到所有被选单元格的模型索引。需要用函数 setModel() 为选择模型设置数据模型,函数定义如下:

void QItemSelectionModel::setModel(QAbstractItemModel *model) //为选择模型设置数据模型

将数据模型、选择模型、视图组件这 3 种对象做好关联设置后,在视图组件上进行选择操作时,选择模型就可以跟踪视图组件上的选择操作。QItemSelectionModel 有一些接口函数可用于给出选择的项的模型索引等信息:

bool hasSelection() //是否有被选择的项,例如被选择的单元格
QModelIndex currentIndex() //返回当前项的模型索引,例如当前单元格
bool isSelected(const QModelIndex &index) //模型索引为 index 的项是否被选中
QModelIndexList selectedIndexes() //返回所有被选择项的模型索引列表,列表未排序
QModelIndexList selectedRows(int column = 0) //返回 column 列所有被选择项的模型索引列表
QModelIndexList selectedColumns(int row = 0) //返回 row 行所有被选择项的模型索引列表

QItemSelectionModel 有几个函数用于清除选择,例如取消选择 QTableView 表格中被选择的单元格:

void clear() //清除选择模型,会触发 selectionChanged()和 currentChanged()信号
void clearCurrentIndex() //清除当前索引,会触发 currentChanged()信号
void clearSelection() //清除所有选择,会触发 selectionChanged()信号

QItemSelectionModel 定义了几个信号。选择的当前项发生变化时会触发 currentChanged() 信号:

void currentChanged(const QModelIndex &current, const QModelIndex &previous)

选择发生变化时,例如在 QTableView 视图组件上选择多个单元格,或取消选择一些单元格,都会触发 selectionChanged() 信号:

void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)

四、自定义代理

在模型/视图结构中,代理的作用就是在视图组件进入编辑状态编辑某个项时,提供一个临时的编辑器用于数据编辑,编辑完成后再把数据提交给数据模型。例如,在 QTableView 组件上双击一个单元格时,代理会提供一个临时的编辑器,默认是 QLineEdit 编辑框,在这个编辑框里修改项的文字,按 Enter 键或焦点移动到其他单元格时完成编辑,编辑框内的文字会保存到数据模型。

若要替换 QTableView 组件提供的默认代理组件,就需要为 QTableView 组件的某列或某个单元格设置自定义代理。自定义代理类需要从 QStyledItemDelegate 类继承,创建自定义代理类的实例后,再将其设置为整个视图组件或视图组件的某行或某列的代理,以替代默认代理的功能。QAbstractItemView 类定义了设置自定义代理的 3 个函数:

void setItemDelegate(QAbstractItemDelegate *delegate) // 设置为整个视图组件的代理
void setItemDelegateForColumn(int column, QAbstractItemDelegate *delegate) // 为视图组件的某一列设置自定义代理
void setItemDelegateForRow(int row, QAbstractItemDelegate *delegate)  // 为视图组件的某一行设置自定义代理

其中,delegate 是创建的自定义代理类的实例对象。QStyledItemDelegate 是视图组件使用的默认的代理类,自定义代理类需要从 QStyledItemDelegate 类继承,并重新实现 QStyledItemDelegate 中定义的 4 个虚函数,这 4 个函数是由模型/视图系统自动调用的。

函数 createEditor() 可创建用于编辑模型数据的界面组件,称为代理编辑器,例如 QSpinBox 组件,或 QComboBox 组件。其函数原型定义如下:

QWidget *QStyledItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index)

在 QTableView 视图组件上双击一个单元格使其进入编辑状态时,系统就会自动调用 createEditor() 创建代理编辑器,例如创建 QSpinBox 组件,然后将其显示在单元格里。

函数 setEditorData() 的功能是从数据模型获取项的某个角色(一般是 EditRole 角色)的数据,然后将其设置为代理编辑器上显示的数据。其函数原型定义如下:

void QStyledItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index)

完成对当前单元格的编辑,例如输入焦点移到其他单元格时,系统会自动调用函数 setModelData(),其功能是将代理编辑器里的输入数据保存到数据模型的项里。其函数原型定义如下:

void QStyledItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index)

视图组件在界面上显示代理编辑器时,需要调用 updateEditorGeometry() 函数为组件设置合适的大小,例如在一个单元格里显示一个 QSpinBox 代理编辑器时,一般将其设置为单元格的大小。其函数原型定义如下:

void QStyledItemDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index)

五、QFileSystemModel 和 QTreeView

QFileSystemModel 为本机的文件系统提供一个模型,结合使用 QFileSystemModel 和 QTreeView,可以以目录树的形式显示本机的文件系统,如同 Windows 的资源管理器一样。要通过 QFileSystemModel 获得本机的文件系统,需要用 QFileSystemModel 的函数 setRootPath() 设置一个根目录,例如:

QFileSystemModel *model = new QFileSystemModel(this); 
model->setRootPath(QDir::currentPath()); //设置应用程序的当前目录为模型的根目录

QFileSystemModel 提供了一些接口函数,这些接口函数可以设置显示选项,获取目录或文件信息,以及创建或删除文件夹等:

QDir rootDirectory() //以 QDir 类型返回当前根目录
QString rootPath() //以 QString 类型返回当前根目录
QModelIndex index(const QString &path, int column = 0) //返回目录或文件的模型索引
QIcon fileIcon(const QModelIndex &index) //返回项的图标
QFileInfo fileInfo(const QModelIndex &index) //返回项的文件信息
QString fileName(const QModelIndex &index) //返回不含路径的文件名或最后一级文件夹名称
QString filePath(const QModelIndex &index) //返回项的路径或包含路径的文件名
QDateTime lastModified(const QModelIndex &index) //返回项的最后修改日期
bool isDir(const QModelIndex &index) //判断项是不是一个文件夹
qint64 size(const QModelIndex &index) //返回文件的大小(字节数),若是文件夹,返回值为0 
QString type(const QModelIndex &index) //返回项的类型描述文字

QTreeView 是用于显示树状模型的视图组件,其与 QFileSystemModel 模型结合就可以显示本机的文件系统。QTreeView 新定义了两个信号,在一个节点处展开子节点时会触发 expanded() 信号,折叠子节点时会触发 collapsed() 信号。在 QTreeView 组件上点击时,clicked() 信号会传递当前节点的模型索引,通过这个模型索引就可以用 QFileSystemModel 类的接口函数对这个节点表示的文件夹或文件进行各种操作。QTreeView 组件的 clicked()信号只能传递当前节点的模型索引,如果 QTreeView 组件允许多选操作,就需要使 QItemSelectionModel 选择模型,通过选择模型获取所有被选节点的模型索引。

http://www.dtcms.com/a/304487.html

相关文章:

  • Python入门第三课:进阶编程技能: 文件操作与数据持久化
  • 【C++算法】78.BFS解决FloodFill算法_算法简介
  • 量子计算革命:重新定义计算的边界与未来
  • react 的 useTransition 、useDeferredValue
  • ZKmall开源商城架构工具链:Docker、k8s 部署与管理技巧
  • 反射核心:invoke与setAccessible方法详解
  • SpringBoot整合RocketMQ(阿里云ONS)
  • 数据库4.0
  • Linux 文件管理高级操作:复制、移动与查找的深度探索
  • Deep Research(信息检索增强)认识和项目实战
  • 计算器4.0:新增页签功能梳理页面,通过IO流实现在用户本地存储数据
  • 点控云数据洞察智能体:让房地产决策有据可循,让业务增长稳健前行
  • 【LLM】——qwen2.5 VL模型导出到onnx
  • Python中二进制文件操作
  • 快速了解逻辑回归
  • 【华为机试】43. 字符串相乘
  • 【LeetCode 随笔】
  • 【深度学习】独热编码(One-Hot Encoding)
  • 开源 Arkts 鸿蒙应用 开发(十一)证书和包名修改
  • C语言在键盘上输入一个3行3列矩阵的各个元素的值(值为整数),然后输出主对角线元素的积,并在fun()函数中输出。
  • 信号上升时间与带宽的关系
  • Leetcode-3361两个字符串的切换距离
  • FastAPI入门:请求体的字段、嵌套模型、额外数据、额外数据类型
  • Linux系统部署k8s集群
  • 在 Web3 时代通过自我主权合规重塑 KYC/AML
  • Git快速入门,完整的git项目管理工具教程,git入门到精通!
  • 青少年软件编程图形化Scratch等级考试试卷(二级)2025年6月
  • 【EDA】Calma--早期版图绘制工具商
  • python案例:基于python 神经网络cnn和LDA主题分析的旅游景点满意度分析
  • 解决mac下git pull、push需要输入密码