Qt6.0开发 第五章 模型-视图结构

第五章 模型-视图结构


模型-视图结构概述

模型-视图结构是一种将数据存储和界面分离的编程方法.模型存储数据,视图组件显示模型中的数据,在视图组件里修改的数据会被自动保存到模型里.

GUI程序的主要功能是可由用户在界面上编辑和修改数据,典型的如数据库应用程序.在数据库应用程序中,界面上的数据来源于数据库,用户在界面上修改数据,修改后的数据又保存到数据库.

模型-视图的基本结构包括以下几个部分:

  • 源数据(data):原始数据,如数据库的一个数据表或SQL查询结果等
  • 视图(view):也称视图组件,是界面组件,视图从模型获得数据然后将其显示在界面上.
  • 模型(model):也称数据模型,与源数据通信,并为视图组件提供数据接口.它从源数据提取需要的数据,用于视图组件进行显示和编辑.
  • 代理(delegate):为视图与模型之间交互操作提供临时的编辑器.模型向视图提供数据一般是单向的,一般仅用于显示.当需要在视图上编辑数据时,代理会为编辑数据提供一个编辑器.

模型

所有基于项(item)的模型类都是基于QAbstractItemModel类的,这个类定义了试图组件和代理存取数据的接口.

模型只是在内存中临时存储数据,模型的数据来源可以是其他类,文件,数据库或任何数据源.

模型类继承关系:

  • QAbstractItemModel:抽象模型类父类
    • QFileSystemModel:用于表示计算机上文件系统的模型类
    • QAbstractListModel:抽象列表类
      • QStringListModel:用于表示字符串列表数据的模型类
    • QStandardItemModel:标准的基于项的模型类,每个项都为QStandardItem对象
    • QAbstractTableModel:抽象表类
      • QSqlQueryModel:用于表示数据库SQL查询结果的模型类
        • QSqlTableModel:用于表示数据库的一个数据表的模型类

视图

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

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

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

视图组件的数据来源于模型,视图组件不存储数据.便利类则为组件的每个节点或单元格创建一个项,用项存储数据.

因此,便利类缺乏对大型数据源进行灵活处理的能力,只适用于小型数据的显示和编辑,而视图组件则会根据模型的数据内容自动显示.

代理

代理能够在视图组件上为编辑数据提供临时的编辑器.代理负责从模型获取相应的数据,然后将其显示在编辑器里,修改数据又将编辑器里的数据保存到模型中.

QAbstractItemDelegate是所有代理类的基类,作为抽象类,它不能直接用于创建对象.它有两个子类,即QItemDelegate和QStyledItemDelegate,这两个类基本相同,而QStyledItemDelegate能使用Qt样式表定义的当前样式绘制代理组件.

对于一些特殊的数据编辑需求,则可以从QStyledItemDelegate中继承.

一些基本概念

在模型-视图结构中,模型为视图组件和代理提供存取数据的标准接口.

QAbstractItemModel是所有模型类的基类,不管底层的数据结构是如何组织数据的,QAbstractItemModel的子类都以表格的层次结构展示数据,视图组件按照这种规则规则来存取模型中的数据,但是展示给用户的形式不一样.

模型的三种常见展示形式:

  • 列表模型(list model)
  • 表格模型(table model)
  • 树状模型(tree model)

不管模型的表现形式是怎样的,模型中存储数据的基本单元都是项(item),每个项有一个行号和一个列号,还有一个父项(parent item).三个模型都有一个隐藏的根项(item).

为了确保数据的展示与数据存取方式分离,模型中引入了模型索引(model index)的概念.通过模型索引,视图组件和代理都通过模型索引来获取数据.

QModelIndex是表示模型索引的类.模型索引提供访问数据的临时指针,用于通过模型提取或修改数据.

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

模型的基本形式是用行和列定义的表格数据,但这并不意味着底层的数据是用二维数据存储的,是用行与列只是为了组件之间交互方便.一个模型索引包含行号与列号.

在创建模型索引的函数中需要传递行号,列号和父项的模型索引.

当模型为列表或表格结构时,是用行号、列号访问数据比较直观,所有项的父项就是顶层项.

当模型为树状结构时情况比较复杂,一个节点有父节点,其他可以是其他节点的父节点,在构造节点的模型索引时,需要指定正确的行号,列号和父节点.

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

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

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

可以为一个项设置不同角色的数据,角色参数role用枚举类型Qt::ItemDataRole的枚举类型表示.枚举类型Qt::ItemDataRole常用的一些枚举值及其含义如表所示,表中的橘色数据类型用于相应数据的数据的类型:

  • bool QAbstractItemModel::setData(const QModelIndex &index,const QVariant &value,int role=Qt::EditRole)
枚举类型Qt::ItemDataRole常用的一些枚举值
枚举值 角色数据类型 含义
Qt::DisplayRole QString 界面上显示的字符串
Qt::DecorationRole QIcon,QColor 在界面上期装饰作用的数据
Qt::EditRole QString 界面上适合在编辑器中显示的数据
Qt::ToolTipRole QString 项的toolTip
Qt::StatusTipRole QString 项的statusTip
Qt::FontRole QFont 项的字体
Qt::TextAlignmentRole Qt::Alignment 项的对齐方式
Qt::BackgroundRole QBrush 项的背景色
Qt::ForegroundRole QBrush 项的前景色
Qt::CheckStateRole Qt::CheckState 项的复选状态
Qt::UserRole QVariant 自定义的用户数据

在获取一个项的数据时也需要指定角色,以获取不同角色的数据.QAbstractItemModel定义了函数data()可以返回一个项的不同角色的数据.

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

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

QAbstractItemModel类

QAbstractItemModel是所有模型类的字节或间接父类,它定义了模型的通用接口函数,作为抽象类不能直接用来创建对象实例.

由于QAbstractItemModel是抽象类,所以它的很多函数都是虚函数.

下面给出一些关于QAbstractItemModel的接口函数:

  • int rowCount(const QModelIndex &parent=QModelIndex())//返回行数
  • int columnCount(const QModelIndex &parent=QModelIndex())//返回列数
  • 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())//移除多列
  • bool moveRow(const QModelIndex &sourceParent,int sourceRow,const QModelIndex &destinationParent,int destinationChild)//移动行
  • bool moveColumn(const QModelIndex &sourceParent,int sourceColumn,const QModelIndex &destinationParent,int destinationChild)//移动列
  • void sort(int column,Qt::SortOrder order=Qt::AscendingOrder)//排序项
  • bool setData(const QModelIndex &index,const QVariant &value,int role=Qt::EditRole)//设置数据
  • QVariant data(const QModelIndex &index,int role=Qt::EditRole)//返回数据
  • bool clearItemData(const QModelIndex &index)//清楚项的数据

QAbstractItemView类

QAbstractView类是所有视图组件类的父类,它定义了视图组件类共有的一些接口.

下面是其常用的一些函数:

  • void setModel(QAbstractItemModel *model)//设置数据模型
  • QAbstractItemModel *model()//返回关联模型的数据对象指针
  • void setSelectionModel(QItemSelectionModel *selectionModel)//设置选择模型
  • QItemSelectionModel *selectionModel()//返回选择模型的对象指针
  • QModelIndex currentIndex()//返回当前项的模型索引
  • void setCurrentIndex(const QModelIndex &index)//设置模型索引为index的项为当前项
  • void selectAll()//选择视图中的所有项
  • void clearSelection()//清除所有选择

下面是一些常用的属性:

  • editTriggers属性:表示视图组件是否可以编辑数据以及进入编辑状态的方式等.
    • 常用函数有:
      • void setEditTriggers(QAbstractItemView::EditTriggers triggers)//
      • QAbstractItemView::EditTriggers editTriggers()
    • QAbstractItemView::EditTriggers的值有:
      • NoEditTriggers:不允许编辑
      • CurrentChanged:当前项变化时进入编辑状态
      • DoubleClicked:点击一个已选择的项时进入编辑状态
      • SelectedClicked:点击一个已选择的项时进入编辑状态
      • EditKeyPressed:当平台的编辑按键被按下时进入编辑状态
      • AnyKeyPressed:任何键被按下时进入编辑状态
      • AllEditTriggers:发生以上任何动作时进入编辑状态
  • alternatingRowColors属性:这个属性设置各行是否需要交替使用不同背景色.
  • selectionMode属性:这个属性表示在视图组件上选择项的操作模式,该属性值一般为SelectionMode类型
    • QAbstractItemView::SelectionMode的值有:
      • SingleSelection:单选
      • ContiguousSelection:连续选择,如按住shift
      • ExtendedSelection:扩展选择,如按住ctrl
      • MultiSelection:多选
      • NoSelection:不允许选择
  • selectionBehavior属性:这个属性表示点击鼠标时操作的行为,属性值为SelectionBehavior类型.
    • QAbstractItemView::SelectionBehavior的值有:
      • SelectItems:选择单个项
      • SelectRows:选择行,点击单个单元格选择对应行
      • SelectColumns:选择列,点击单个单元格选择对应列

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组件的数据模型.

QStringListModel内部存储了一个字符串列表,通过字符串列表内容自动显示在关联容器上.

QStringListModel类

QStringListModel类有两种参数形式的构造函数,定义如下:

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

作为字符串列表数据的模型,QStringListModel对象内部有一个字符串列表,对模型数据的修改就是对QStringListModel对象内部字符串的修改.

QStringListModel继承于QAbstractItemModel,新增加的函数只有两个:

  • void setStringList(const QStringList &strings)//设置字符串列表
  • QStringList stringList()//返回模型内部的字符串列表

下面给出一个使用例子用于方便理解:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    m_list<<"北京"<<"上海"<<"成都"<<"长沙"<<"广州";
    m_lisModel= new QStringListModel(m_list,this);
    m_lisModel->setStringList(m_list);
    ui->ltvView1->setModel(m_lisModel);
    ui->ltvView1->setEditTriggers(QAbstractItemView::DoubleClicked|
                                  QAbstractItemView::SelectedClicked);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_btnReliveList_clicked()
{
    m_lisModel->setStringList(m_list);
}


void MainWindow::on_btnClearList_clicked()
{
    m_lisModel->removeRows(0,m_lisModel->rowCount());
}


void MainWindow::on_chkEditable_clicked(bool checked)
{
    if(checked)
        ui->ltvView1->setEditTriggers(QAbstractItemView::DoubleClicked|
                                      QAbstractItemView::SelectedClicked);
    else
        ui->ltvView1->setEditTriggers(QAbstractItemView::NoEditTriggers);
}


void MainWindow::on_btnAddItem_clicked()
{
    m_lisModel->insertRow(m_lisModel->rowCount());
    QModelIndex index= m_lisModel->index(m_lisModel->rowCount()-1);
    m_lisModel->setData(index,QString("new item %1").arg(m_lisModel->rowCount()),Qt::DisplayRole);
    ui->ltvView1->setCurrentIndex(index);
}


void MainWindow::on_btnInsertItem_clicked()
{
    QModelIndex index= ui->ltvView1->currentIndex();
    m_lisModel->insertRow(index.row());
    m_lisModel->setData(index,QString("new item %1").arg(index.row()),Qt::DisplayRole);
    ui->ltvView1->setCurrentIndex(index);
}


void MainWindow::on_btnDeleteItem_clicked()
{
    QModelIndex index= ui->ltvView1->currentIndex();
    m_lisModel->removeRow(index.row());
}


void MainWindow::on_btnUpper_clicked()
{
    QModelIndex index;
    int curRow= ui->ltvView1->currentIndex().row();
    m_lisModel->moveRow(index,curRow,index,curRow-1);
}


void MainWindow::on_btnLower_clicked()
{
    QModelIndex index;
    int curRow= ui->ltvView1->currentIndex().row();
    m_lisModel->moveRow(index,curRow,index,curRow+2);
}


void MainWindow::on_btnSort_clicked(bool checked)
{
    if(checked)
        m_lisModel->sort(Qt::AscendingOrder);
    else
        m_lisModel->sort(Qt::DescendingOrder);
}


void MainWindow::on_btnClearPlt_clicked()
{
    ui->pltEdit->clear();
}


void MainWindow::on_btnShowSL_clicked()
{
    QStringList tempList= m_lisModel->stringList();
    ui->pltEdit->clear();
    for(auto iter:tempList)
        ui->pltEdit->appendPlainText(iter);
}

QStanardItemModel和QTableView

QStandardItemModel是基于项的模型类,每个项是一个QStandardItem对象,可以存储各种数据.QStandardItemModel通常与QTableView组成模型-视图结构,实现二维数据的管理.

QTableView类

QTableView继承自QAbstractItemView类,其新定义的属性主要用于控制显示效果,在UI可视化设计时就可以设置QTableView组件的各种属性.

QTableView组件有水平表头和垂直表头,都是QHeaderView对象,可以设置和返回表头对象.

相关函数定义如下:

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

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

QStandardItemModel类

QStandardItemModel是以项为基本数据单元的模型类,每个项是一个QStandardItem对象.

项可以存储各种角色的数据,如文字,字体,对齐方式,图标,复选状态等.

如果以多行多列的二维数组形式存储项,就是表格模型;如果表格模型只有一列,就是列表模型;如果存储项时为项指定父项,就构成树状模型.

如果一个QStandardItemModel对象是表格模型,将它设置为QTableView组件的数据模型后,视图组件就用表格的形式显示模型的数据,并且根据每个单元格的项定义各种角色数据控制显示效果.

QStandardItemModel的接口主要有:

  • 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 appednColumn(cosnt 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)
  • 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)
  • void clear()

QStandardItemModel新定义了一个信号,在任何一个项的数据变化时该信号就会发射:

  • void itemChanged(QStandardItem *item)

QStandardItem类

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

QStandardItem类没有父类,它存储项的各种特性内容与设置各种特性.

QStandardItem的常用接口函数
读取函数 设置函数 设置函数的功能
text() setText() 设置项的显示文字
toolTip() setToolTip() 设置toolTip文字
statusTip() setStatusTip() 设置statusTip文字
icon() setIcon() 设置项的图标
font() setFont() 设置文字的字体
textAlignment() setTextAlignment() 设置文字的对齐方式
foreground() setForeground() 设置项的前景色
background() setBackground() 设置项的背景色
isEnabled() setEnabled() 设置项是否能够交互
isEditable() setEditable() 设置项是否可以被编辑
isSelectable() setSelectable() 设置项是否可以被选择
isCheckable() setCheckable() 设置项是否可以复选
checkState() setCheckable() 设置项的复选状态
isAutoTristate() setAutoTristate() 设置是否自动改变三种复选状态
isUserTristate() setUserTristate() 设置是否由用户决定三种复选状态
flags() setFlags() 设置项的标志
row() -- 返回自身父项的所有子项的行号
column() -- 返回自身父项的所有子项的列号

此外,还有一些函数很重要:

  • void QStandardItem::setFlags(Qt::ItemFlags flags)//设置标志
  • void QStandardItem::setData(const QVariant &value,int role=Qt::UserRole+1)//设置自定义数据
  • QVariant QStandardItem::data(int role=Qt::UserRole+1)//返回用户自定义数据
  • void QStandardItem::clearData()//清除用户自定义数据

QStandard管理子项的函数有:

  • void appendRow(const QList<QStandardItem *>&items)
  • void appendRow(QStandardItem *item)
  • void appednColumn(cosnt 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)
  • void insertColumns(int column,int count)
  • void removeColumn(int column)
  • void removeColumns(int column,int count)
  • int rowCount()
  • int columnCount()
  • bool hasChildren()
  • QStandardItem *child(int row,int column=0)

如果采用树状模型,那么QStandardItemModel管理所有的顶层项,而各个一级节点的字节子节点则通过QStandardItem类来管理.

QItemSelectionModel类

一个视图组件需要设置一个数据模型,还可以设置一个选择模型.QItemSelectionModel是选择模型类,它的功能是跟踪视图组件上的选择操作,给出选择范围.

为了设置视图模型,需要用到函数setModell()为模型设置数据模型,函数定义如下:

  • void QItemSelecitonModel::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行所有被选择项的模型索引表
  • void clear()//清除选择模型
  • void clearCurrentIndex()//清除当前索引
  • void clearSelection()//清除所有选择

QItemSelectionModel有几个信号:

  • void currentChanged(const QModelIndex &current,const QModelIndex &previous)//当前项改变
  • void selectionChanged(const QItemSelection &selected,const QItemSelection &deselected)
    //选择多个单元格或取消选择一些单元格

下面给出一个综合使用上述知识的例子:
mainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QLabel>
#include <QStandardItemModel>
#include <QItemSelectionModel>
#include <QFileDialog>
#include <QString>

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
    QLabel *labCurFile,*labCellPos,*labCellText;
    const int fixedColum=6;
    QStandardItemModel *m_model;
    QItemSelectionModel *m_selection;

    void iniModelData(QStringList &fileContent);
private slots:
    void do_currentChanged(const QModelIndex&current,const QModelIndex&previous);
    void on_actOpen_triggered();
    void on_actAddRow_triggered();
    void on_actInsertRow_triggered();
    void on_actDeleteRow_triggered();
    void on_actModelData_triggered();
    void on_actAlignLeft_triggered();
    void on_actAlignCenter_triggered();
    void on_actAlignRight_triggered();
    void on_actFontBold_triggered(bool checked);
};
#endif // MAINWINDOW_H

mainWindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    labCurFile=new QLabel("当前文件",this);
    labCurFile->setMinimumWidth(200);
    labCellPos=new QLabel("当前单元格",this);
    labCellPos->setMinimumWidth(200);
    labCellText=new QLabel("单元格内容",this);
    labCellText->setMinimumWidth(200);

    ui->statusbar->addWidget(labCurFile);
    ui->statusbar->addWidget(labCellPos);
    ui->statusbar->addWidget(labCellText);

    m_model= new QStandardItemModel(2,fixedColum,this);
    m_selection= new QItemSelectionModel(m_model,this);
    ui->tableView->setModel(m_model);
    ui->tableView->setSelectionModel(m_selection);
    ui->tableView->setSelectionMode(QAbstractItemView::ExtendedSelection);
    ui->tableView->setSelectionBehavior(QAbstractItemView::SelectItems);

    connect(m_selection,SIGNAL(currentChanged(QModelIndex,QModelIndex)),this,SLOT(do_currentChanged(QModelIndex,QModelIndex)));
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::iniModelData(QStringList &fileContent)
{
    int rowCount=fileContent.size();
    m_model->setRowCount(rowCount-1);

    QString header=fileContent.at(0);
    QStringList headerList=header.split(QRegularExpression(R"(\s+)"),Qt::SkipEmptyParts);
    m_model->setHorizontalHeaderLabels(headerList);
    for(int i=1;i<rowCount;i++){
        QString lineText=fileContent.at(i);
        QStringList tempList=lineText.split(QRegularExpression(R"(\s+)"),Qt::SkipEmptyParts);

        QStandardItem *item= new QStandardItem(tempList.at(0));
        item->setCheckable(true);
        item->setBackground(QBrush(Qt::yellow));
        if(tempList.at(0)=="0")
            item->setCheckState(Qt::Unchecked);
        else
            item->setCheckState(Qt::Checked);
        m_model->setItem(i-1,0,item);
        for(int j=1;j<=5;j++){
            item=new QStandardItem(tempList.at(j));
            m_model->setItem(i-1,j,item);
        }
    }

}

void MainWindow::do_currentChanged(const QModelIndex &current, const QModelIndex &previous)
{
    if(current.isValid()){
        labCellPos->setText(QString::asprintf("当前单元格:%d行,%d列",current.row(),current.column()));
        QStandardItem *tempItem= m_model->itemFromIndex(current);
        labCellText->setText(QString("单元格内容:%1").arg(tempItem->text()));
        ui->actFontBold->setChecked(tempItem->font().bold());
    }
}

void MainWindow::on_actOpen_triggered()
{
    QString curPath= QCoreApplication::applicationDirPath();
    QString fileName= QFileDialog::getOpenFileName(this,"打开一个文件",curPath,"数据文件(*.txt);所有文件(*.*)");
    if(fileName.isEmpty())
        return;
    QFile fileObject(fileName);
    if(!fileObject.open(QIODevice::ReadOnly|QIODevice::Text))
        return;

    QStringList fileContent;
    ui->plainTextEdit->clear();
    QTextStream tempStream(&fileObject);
    while(!tempStream.atEnd()){
        QString str=tempStream.readLine();
        ui->plainTextEdit->appendPlainText(str);
        fileContent.append(str);
    }
    fileObject.close();
    labCurFile->setText("当前文件:"+fileName);
    ui->actAddRow->setEnabled(true);
    ui->actInsertRow->setEnabled(true);
    ui->actDeleteRow->setEnabled(true);

    iniModelData(fileContent);
}


void MainWindow::on_actAddRow_triggered()
{
    QList<QStandardItem*>itemList;
    QString str=m_model->headerData(0,Qt::Horizontal).toString()+QString::number(m_model->rowCount());
    QStandardItem *item=new QStandardItem(str);
    item->setColumnCount(6);
    item->setCheckable(true);
    item->setCheckState(Qt::Checked);
    item->setBackground(Qt::yellow);
    itemList<<item;
    for(int i=0;i<m_model->columnCount()-1;i++){
        item=new QStandardItem("0");
        itemList<<item;
    }
    m_model->appendRow(itemList);
    m_selection->clearSelection();
    m_selection->setCurrentIndex(m_model->index(m_model->rowCount()-1,0),QItemSelectionModel::Select);
}



void MainWindow::on_actInsertRow_triggered()
{
    QModelIndex index= ui->tableView->currentIndex();
    QList<QStandardItem*>itemList;
    QString str=m_model->headerData(0,Qt::Horizontal).toString()+QString::number(m_model->rowCount());
    QStandardItem *item=new QStandardItem(str);
    item->setColumnCount(6);
    item->setCheckable(true);
    item->setCheckState(Qt::Checked);
    item->setBackground(Qt::yellow);
    itemList<<item;
    for(int i=0;i<m_model->columnCount()-1;i++){
        item=new QStandardItem("0");
        itemList<<item;
    }
    m_model->insertRow(index.row(),itemList);
    m_selection->clearSelection();
    m_selection->setCurrentIndex(m_model->index(index.row(),0),QItemSelectionModel::Select);
}


void MainWindow::on_actDeleteRow_triggered()
{
    QModelIndex index= ui->tableView->currentIndex();
    m_model->removeRow(index.row());
}


void MainWindow::on_actModelData_triggered()
{
    ui->plainTextEdit->clear();
    QStandardItem *item;
    QString str="";

    for(int i=0;i<m_model->columnCount();i++){
        item=m_model->horizontalHeaderItem(i);
        str+=item->text();
        str+='\t';
    }
    ui->plainTextEdit->appendPlainText(str);
    for(int i=0;i<m_model->rowCount();i++){
        str.clear();
        item=m_model->item(i,0);
        if(item->checkState()==Qt::Checked)
            str+="是 ";
        else
            str+="否 ";
        for(int j=0;j<m_model->columnCount();j++){
            item=m_model->item(i,j);
            str+=item->text();
            str+='\t';
        }
        ui->plainTextEdit->appendPlainText(str);
    }
}


void MainWindow::on_actAlignLeft_triggered()
{
    QStandardItem *item;
    QModelIndexList indexList;

    if(!m_selection->hasSelection())
        return;
    indexList=m_selection->selectedIndexes();

    for(auto index:indexList){
        item= m_model->item(index.row(),index.column());
        item->setTextAlignment(Qt::AlignLeft|Qt::AlignVCenter);
        m_model->setItem(index.row(),index.column(),item);
    }
}


void MainWindow::on_actAlignCenter_triggered()
{
    QStandardItem *item;
    QModelIndexList indexList;

    if(!m_selection->hasSelection())
        return;
    indexList=m_selection->selectedIndexes();

    for(auto index:indexList){
        item= m_model->item(index.row(),index.column());
        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
        m_model->setItem(index.row(),index.column(),item);
    }
}


void MainWindow::on_actAlignRight_triggered()
{
    QStandardItem *item;
    QModelIndexList indexList;

    if(!m_selection->hasSelection())
        return;
    indexList=m_selection->selectedIndexes();

    for(auto index:indexList){
        item= m_model->item(index.row(),index.column());
        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
        m_model->setItem(index.row(),index.column(),item);
    }
}



void MainWindow::on_actFontBold_triggered(bool checked)
{
    QStandardItem *item;
    QModelIndexList indexList;
    QFont font;

    if(!m_selection->hasSelection())
        return;
    indexList=m_selection->selectedIndexes();

    for(auto index:indexList){
        item= m_model->item(index.row(),index.column());
        font=item->font();
        font.setBold(checked);
        item->setFont(font);
        m_model->setItem(index.row(),index.column(),item);
    }
}

自定义代理

在模型-视图结构中,代理的作用就是在视图组件进入编辑状态编辑某个项时,提供一个临时的编辑器用于数据编辑,编辑完成后再把数据提交给数据模型.

QTableView等视图组件的代理默认提供QLineEdit编辑框,在这个编辑框里可以输入任何数据,所以比较通用.

但是某些情况,我们也希望根据数据类型不同使用不同的编辑器,若要使用这样的功能,就需要使用自定义代理.

若要替换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个虚函数.

  • QWidget*QStyledItemDelegate::createEditor(QWidget*parent,const QStyleOptionViewItem &option,const QModelIndex &index)//用于创建代理编辑器
  • void QStyledItemDelegate::setEditorData(QWidget *editor,const QModelIndex &index)
    //用于从数据模型获取项的某个角色(一般为EditRole),然后将其设置为代理编辑器上显示的数据
  • void QStyledItemDelegate::setModelData(QWidget*editor,QAbstractItemModel *model,const QModelIndex &index)//完成对当前单元格的编辑
  • void QStyledItemDelegate::updateEditorGeometry(QWidget *editor,const QStyleOptionViewItem &option,const QModelIndex &index)//为临时组件设置合适的大小

下面提供一个使用自定义代理的例子,改编自上面QStandardItemModel综合使用例子:

tcombobox_delegate.h

#ifndef TCOMBOBOXDELEGATE_H
#define TCOMBOBOXDELEGATE_H

#include <QObject>
#include <QStyledItemDelegate>
#include <QComboBox>

class TComboBoxDelegate : public QStyledItemDelegate
{
    Q_OBJECT
public:
    explicit TComboBoxDelegate(QObject *parent = nullptr);

    // QAbstractItemDelegate interface
public:
    void setItems(QStringList items,bool editable);
    virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
    virtual void setEditorData(QWidget *editor, const QModelIndex &index) const override;
    virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
    virtual void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
private:
    QStringList m_itemList;
    bool m_editable;
};

#endif // TCOMBOBOXDELEGATE_H

tfloat_spinbox_delegate.h

#ifndef TFLOATSPINBOXDELEGATE_H
#define TFLOATSPINBOXDELEGATE_H

#include <QObject>
#include <QStyledItemDelegate>
#include <QDoubleSpinBox>

class TFloatSpinBoxDelegate : public QStyledItemDelegate
{
    Q_OBJECT
public:
    explicit TFloatSpinBoxDelegate(QObject *parent = nullptr);

    // QAbstractItemDelegate interface
public:
    virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
    virtual void setEditorData(QWidget *editor, const QModelIndex &index) const override;
    virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
    virtual void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
};

#endif // TFLOATSPINBOXDELEGATE_H

tspinbox_delegate.h

#ifndef TSPINBOXDELEGATE_H
#define TSPINBOXDELEGATE_H

#include <QObject>
#include <QSpinBox>
#include <QQmlEngine>
#include <QStyledItemDelegate>

class TSpinBoxDelegate : public QStyledItemDelegate
{
    QML_ELEMENT
public:
    explicit TSpinBoxDelegate(QObject *parent=nullptr);

    // QAbstractItemDelegate interface
public:
    virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
    virtual void setEditorData(QWidget *editor, const QModelIndex &index) const override;
    virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
    virtual void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
};
#endif // TSPINBOXDELEGATE_H

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QLabel>
#include <QStandardItemModel>
#include <QItemSelectionModel>
#include <QFileDialog>
#include <QString>
#include <QStyledItemDelegate>

#include "tspinbox_delegate.h"
#include "tfloat_spinbox_delegate.h"
#include "tcombobox_delegate.h"

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
    QLabel *labCurFile,*labCellPos,*labCellText;
    const int fixedColum=6;
    QStandardItemModel *m_model;
    QItemSelectionModel *m_selection;

    TSpinBoxDelegate* intSpinDelegate;
    TFloatSpinBoxDelegate* doubleSpinDelegate;
    TComboBoxDelegate* comboBoxDelegate;

    void iniModelData(QStringList &fileContent);
private slots:
    void do_currentChanged(const QModelIndex&current,const QModelIndex&previous);
    void on_actOpen_triggered();
    void on_actAddRow_triggered();
    void on_actInsertRow_triggered();
    void on_actDeleteRow_triggered();
    void on_actModelData_triggered();
    void on_actAlignLeft_triggered();
    void on_actAlignCenter_triggered();
    void on_actAlignRight_triggered();
    void on_actFontBold_triggered(bool checked);
};
#endif // MAINWINDOW_H

tcombobox_delegate.cpp

#include "tcombobox_delegate.h"

TComboBoxDelegate::TComboBoxDelegate(QObject *parent)
    : QStyledItemDelegate{parent}
{

}

void TComboBoxDelegate::setItems(QStringList items, bool editable)
{
    m_itemList=items;
    m_editable=editable;
}

QWidget *TComboBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    Q_UNUSED(option);
    Q_UNUSED(index);

    QComboBox *editor=new QComboBox(parent);
    editor->setEditable(m_editable);
    for(auto iter:m_itemList)
        editor->addItem(iter);
    return editor;
}

void TComboBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
    QComboBox *comboBox=dynamic_cast<QComboBox*>(editor);
    QString str=index.model()->data(index,Qt::EditRole).toString();
    comboBox->setCurrentText(str);
}

void TComboBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
    QComboBox *comboBox=dynamic_cast<QComboBox*>(editor);
    QString str=comboBox->currentText();
    model->setData(index,str,Qt::EditRole);
}

void TComboBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    Q_UNUSED(index);
    editor->setGeometry(option.rect);
}

tfloat_spinbox_delegate.cpp

#include "tfloat_spinbox_delegate.h"

TFloatSpinBoxDelegate::TFloatSpinBoxDelegate(QObject *parent)
    : QStyledItemDelegate{parent}
{

}

QWidget *TFloatSpinBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    Q_UNUSED(option);
    Q_UNUSED(index);
    QDoubleSpinBox *editor=new QDoubleSpinBox(parent);
    editor->setFrame(false);
    editor->setMinimum(0);
    editor->setMaximum(50000);
    return editor;
}

void TFloatSpinBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
    QDoubleSpinBox *spinBox=dynamic_cast<QDoubleSpinBox*>(editor);
    double value=index.model()->data(index,Qt::EditRole).toDouble();
    spinBox->setValue(value);
}

void TFloatSpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
    QDoubleSpinBox *spinBox=dynamic_cast<QDoubleSpinBox*>(editor);
    double value=spinBox->value();
    model->setData(index,value);
}

void TFloatSpinBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    Q_UNUSED(index);
    editor->setGeometry(option.rect);
}

tspinbox_delegate.cpp

#include "tspinbox_delegate.h"

TSpinBoxDelegate::TSpinBoxDelegate(QObject *parent)
    :QStyledItemDelegate(parent)
{

}

QWidget *TSpinBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    Q_UNUSED(option);
    Q_UNUSED(index);
    QSpinBox *editor=new QSpinBox(parent);
    editor->setFrame(false);
    editor->setMinimum(0);
    editor->setMaximum(50000);
    return editor;
}

void TSpinBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
    QSpinBox *spinBox=dynamic_cast<QSpinBox*>(editor);
    int value=index.model()->data(index,Qt::EditRole).toInt();
    spinBox->setValue(value);
}

void TSpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
    QSpinBox *spinBox=dynamic_cast<QSpinBox*>(editor);
    int value=spinBox->value();
    model->setData(index,value);
}

void TSpinBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    Q_UNUSED(index);
    editor->setGeometry(option.rect);
}

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    labCurFile=new QLabel("当前文件",this);
    labCurFile->setMinimumWidth(200);
    labCellPos=new QLabel("当前单元格",this);
    labCellPos->setMinimumWidth(200);
    labCellText=new QLabel("单元格内容",this);
    labCellText->setMinimumWidth(200);

    ui->statusbar->addWidget(labCurFile);
    ui->statusbar->addWidget(labCellPos);
    ui->statusbar->addWidget(labCellText);

    m_model= new QStandardItemModel(2,fixedColum,this);
    m_selection= new QItemSelectionModel(m_model,this);
    ui->tableView->setModel(m_model);
    ui->tableView->setSelectionModel(m_selection);
    ui->tableView->setSelectionMode(QAbstractItemView::ExtendedSelection);
    ui->tableView->setSelectionBehavior(QAbstractItemView::SelectItems);

    intSpinDelegate= new TSpinBoxDelegate;
    ui->tableView->setItemDelegateForColumn(1,intSpinDelegate);

    doubleSpinDelegate= new TFloatSpinBoxDelegate;
    ui->tableView->setItemDelegateForColumn(3,doubleSpinDelegate);
    ui->tableView->setItemDelegateForColumn(4,doubleSpinDelegate);
    ui->tableView->setItemDelegateForColumn(5,doubleSpinDelegate);

    comboBoxDelegate= new TComboBoxDelegate;
    QStringList strList;
    strList<<"名字1"<<"名字2"<<"名字3";
    comboBoxDelegate->setItems(strList,true);
    ui->tableView->setItemDelegateForColumn(0,comboBoxDelegate);

    connect(m_selection,SIGNAL(currentChanged(QModelIndex,QModelIndex)),this,SLOT(do_currentChanged(QModelIndex,QModelIndex)));
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::iniModelData(QStringList &fileContent)
{
    int rowCount=fileContent.size();
    m_model->setRowCount(rowCount-1);

    QString header=fileContent.at(0);
    QStringList headerList=header.split(QRegularExpression(R"(\s+)"),Qt::SkipEmptyParts);
    m_model->setHorizontalHeaderLabels(headerList);
    for(int i=1;i<rowCount;i++){
        QString lineText=fileContent.at(i);
        QStringList tempList=lineText.split(QRegularExpression(R"(\s+)"),Qt::SkipEmptyParts);

        QStandardItem *item= new QStandardItem(tempList.at(0));
        item->setCheckable(true);
        item->setBackground(QBrush(Qt::yellow));
        if(tempList.at(0)=="0")
            item->setCheckState(Qt::Unchecked);
        else
            item->setCheckState(Qt::Checked);
        m_model->setItem(i-1,0,item);
        for(int j=1;j<=5;j++){
            item=new QStandardItem(tempList.at(j));
            m_model->setItem(i-1,j,item);
        }
    }

}

void MainWindow::do_currentChanged(const QModelIndex &current, const QModelIndex &previous)
{
    if(current.isValid()){
        labCellPos->setText(QString::asprintf("当前单元格:%d行,%d列",current.row(),current.column()));
        QStandardItem *tempItem= m_model->itemFromIndex(current);
        labCellText->setText(QString("单元格内容:%1").arg(tempItem->text()));
        ui->actFontBold->setChecked(tempItem->font().bold());
    }
}

void MainWindow::on_actOpen_triggered()
{
    QString curPath= QCoreApplication::applicationDirPath();
    QString fileName= QFileDialog::getOpenFileName(this,"打开一个文件",curPath,"数据文件(*.txt);所有文件(*.*)");
    if(fileName.isEmpty())
        return;
    QFile fileObject(fileName);
    if(!fileObject.open(QIODevice::ReadOnly|QIODevice::Text))
        return;

    QStringList fileContent;
    ui->plainTextEdit->clear();
    QTextStream tempStream(&fileObject);
    while(!tempStream.atEnd()){
        QString str=tempStream.readLine();
        ui->plainTextEdit->appendPlainText(str);
        fileContent.append(str);
    }
    fileObject.close();
    labCurFile->setText("当前文件:"+fileName);
    ui->actAddRow->setEnabled(true);
    ui->actInsertRow->setEnabled(true);
    ui->actDeleteRow->setEnabled(true);

    iniModelData(fileContent);
}


void MainWindow::on_actAddRow_triggered()
{
    QList<QStandardItem*>itemList;
    QString str=m_model->headerData(0,Qt::Horizontal).toString()+QString::number(m_model->rowCount());
    QStandardItem *item=new QStandardItem(str);
    item->setColumnCount(6);
    item->setCheckable(true);
    item->setCheckState(Qt::Checked);
    item->setBackground(Qt::yellow);
    itemList<<item;
    for(int i=0;i<m_model->columnCount()-1;i++){
        item=new QStandardItem("0");
        itemList<<item;
    }
    m_model->appendRow(itemList);
    m_selection->clearSelection();
    m_selection->setCurrentIndex(m_model->index(m_model->rowCount()-1,0),QItemSelectionModel::Select);
}



void MainWindow::on_actInsertRow_triggered()
{
    QModelIndex index= ui->tableView->currentIndex();
    QList<QStandardItem*>itemList;
    QString str=m_model->headerData(0,Qt::Horizontal).toString()+QString::number(m_model->rowCount());
    QStandardItem *item=new QStandardItem(str);
    item->setColumnCount(6);
    item->setCheckable(true);
    item->setCheckState(Qt::Checked);
    item->setBackground(Qt::yellow);
    itemList<<item;
    for(int i=0;i<m_model->columnCount()-1;i++){
        item=new QStandardItem("0");
        itemList<<item;
    }
    m_model->insertRow(index.row(),itemList);
    m_selection->clearSelection();
    m_selection->setCurrentIndex(m_model->index(index.row(),0),QItemSelectionModel::Select);
}


void MainWindow::on_actDeleteRow_triggered()
{
    QModelIndex index= ui->tableView->currentIndex();
    m_model->removeRow(index.row());
}


void MainWindow::on_actModelData_triggered()
{
    ui->plainTextEdit->clear();
    QStandardItem *item;
    QString str="";

    for(int i=0;i<m_model->columnCount();i++){
        item=m_model->horizontalHeaderItem(i);
        str+=item->text();
        str+='\t';
    }
    ui->plainTextEdit->appendPlainText(str);
    for(int i=0;i<m_model->rowCount();i++){
        str.clear();
        item=m_model->item(i,0);
        if(item->checkState()==Qt::Checked)
            str+="是 ";
        else
            str+="否 ";
        for(int j=0;j<m_model->columnCount();j++){
            item=m_model->item(i,j);
            str+=item->text();
            str+='\t';
        }
        ui->plainTextEdit->appendPlainText(str);
    }
}


void MainWindow::on_actAlignLeft_triggered()
{
    QStandardItem *item;
    QModelIndexList indexList;

    if(!m_selection->hasSelection())
        return;
    indexList=m_selection->selectedIndexes();

    for(auto index:indexList){
        item= m_model->item(index.row(),index.column());
        item->setTextAlignment(Qt::AlignLeft|Qt::AlignVCenter);
        m_model->setItem(index.row(),index.column(),item);
    }
}


void MainWindow::on_actAlignCenter_triggered()
{
    QStandardItem *item;
    QModelIndexList indexList;

    if(!m_selection->hasSelection())
        return;
    indexList=m_selection->selectedIndexes();

    for(auto index:indexList){
        item= m_model->item(index.row(),index.column());
        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
        m_model->setItem(index.row(),index.column(),item);
    }
}


void MainWindow::on_actAlignRight_triggered()
{
    QStandardItem *item;
    QModelIndexList indexList;

    if(!m_selection->hasSelection())
        return;
    indexList=m_selection->selectedIndexes();

    for(auto index:indexList){
        item= m_model->item(index.row(),index.column());
        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
        m_model->setItem(index.row(),index.column(),item);
    }
}



void MainWindow::on_actFontBold_triggered(bool checked)
{
    QStandardItem *item;
    QModelIndexList indexList;
    QFont font;

    if(!m_selection->hasSelection())
        return;
    indexList=m_selection->selectedIndexes();

    for(auto index:indexList){
        item= m_model->item(index.row(),index.column());
        font=item->font();
        font.setBold(checked);
        item->setFont(font);
        m_model->setItem(index.row(),index.column(),item);
    }
}

QFileSystemModel和QTreeView

QFileSystemModel为文件系统提供一个模型,综合使用QFileSystemModel和QTreeView可以以目录树的形式显示本机的文件系统,如同Windows的资源管理器一样.

QFileSystemModel类

要通过QFileSystemModel获得本机的文件系统,需要用QFileSystemModel的函数setRootPath()设置一个根目录.如果需要使用条件过滤,则还需要设置过滤器.

QFileSystemModel类的主要成员函数如下:

  • QModelIndex setRootPath(const QString &newPath)//设置根目录
  • QDir rootDirectory()//以QDir类返回目录
  • QString rootPath()//以QString类返回目录
  • QModelIndex index(const QString &path,int column=0)//返回目录或文件的模型索引
  • void setFilter(QDir::Filters filters)//设置模型数据项过滤器
  • void setNameFilters(const QStringList &filters)//设置文件名过滤器
  • void setNameFilterDisables(bool enable)//设置文件名过滤器
  • void setOption(QFileSystemModel::Option option,bool on=true)//设置模型选项
  • 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)//返回文件的大小字节数
  • QString type(const QModelIndex &index)//返回项的类型描述文字
  • QModelIndex mkdir(const QModelIndex &parent,const QString &name)//创建文件夹
  • bool rmdir(const QModelIndex &index)//删除文件夹
  • bool remove(const QModelIndex &index)//删除文件

相关的一些内容:

  • QDir的枚举值:
    • QDir::AllDirs:列出所有目录
    • QDir::Files:列出文件
    • QDir::Drives:列出驱动器
    • QDir::NoDotAndDotDot:不列出目录下的'.'和'..'特殊项
    • QDir::Hidden:列出隐藏的文件
    • QDir::System:列出系统文件
  • QFileSystemModel::Option的枚举值:
    • QFileSystemModel::Option::DontWatchForChanges:不监视文件系统的变化
    • QFileSystemModel::Option::DontResolveSymlinks:不解析文件系统的符号连接项
    • QFileSystemModel::Option::DontUseCustomDirectoryIcons:不使用自定义的目录图标

QTreeView类

QTreeView类是用于显示树状模型的视图组件,其与QFileSystemModel模型结合就可以显示本机的文件系统.

如果QTreeView组件允许多选操作,就需要使用QItemSelectionModel选择模型.

关于文件系统有关内容,后面会进一步深入,因而暂时不用太过在意.

下面是一个综合使用的样例:

mainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QFileSystemModel>
#include <QFileDialog>

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_actSetRootDir_triggered();

    void on_radOnlyDirAndFile_clicked();

    void on_radOnlyDir_clicked();

    void on_chkFliterFile_clicked(bool checked);

    void on_btnApplicate_clicked();

    void on_treeView_clicked(const QModelIndex &index);

private:
    Ui::MainWindow *ui;
    QFileSystemModel *m_model;
};
#endif // MAINWINDOW_H

mainWindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ui->radOnlyDirAndFile->setChecked(true);
    m_model=new QFileSystemModel(this);
    ui->treeView->setModel(m_model);
    ui->listView->setModel(m_model);
    ui->tableView->setModel(m_model);
    m_model->setRootPath(QDir::currentPath());
    //ui->treeView->setRootIndex(m_model->index(QDir::currentPath()));
    connect(ui->treeView,SIGNAL(clicked(QModelIndex)),ui->listView,SLOT(setRootIndex(QModelIndex)));
    connect(ui->treeView,SIGNAL(clicked(QModelIndex)),ui->tableView,SLOT(setRootIndex(QModelIndex)));
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_actSetRootDir_triggered()
{
    QString dir=QFileDialog::getExistingDirectory(this,"选择目录",QDir::currentPath());

    if(dir.isEmpty())
        return;
    ui->treeView->setRootIndex(m_model->index(dir));
    ui->listView->setRootIndex(m_model->index(dir));
    ui->tableView->setRootIndex(m_model->index(dir));
}


void MainWindow::on_radOnlyDirAndFile_clicked()
{
    ui->gbFilter->setEnabled(true);
    m_model->setFilter(QDir::AllDirs|QDir::Files|QDir::NoDotAndDotDot);
}


void MainWindow::on_radOnlyDir_clicked()
{
    ui->gbFilter->setEnabled(false);
    m_model->setFilter(QDir::AllDirs|QDir::NoDotAndDotDot);
}


void MainWindow::on_chkFliterFile_clicked(bool checked)
{
    ui->comFileType->setEnabled(checked);
    ui->btnApplicate->setEnabled(checked);
    m_model->setNameFilterDisables(!checked);
}


void MainWindow::on_btnApplicate_clicked()
{
    QStringList filter= ui->comFileType->currentText().trimmed().split(';',Qt::SkipEmptyParts);
    m_model->setNameFilters(filter);
}


void MainWindow::on_treeView_clicked(const QModelIndex &index)
{
    ui->lbFileName->setText(m_model->fileName(index));
    ui->lbDirName->setText(m_model->filePath(index));
    ui->lbFileType->setText(m_model->type(index));

    ui->chkIsDir->setChecked(m_model->isDir(index));

    int sz=m_model->size(index)/1024;
    if(sz<1024)
        ui->lbFileSize->setText(QString("%1 KB").arg(sz));
    else
        ui->lbFileSize->setText(QString::asprintf("%.1f MB",sz/1024.0));
}


posted @ 2024-03-24 23:14  Mesonoxian  阅读(12)  评论(0编辑  收藏  举报