Qt5.9学习笔记3-ModelView结构/文件系统

本文详细解析了Qt中的Model/View架构,涉及数据模型的种类(如QStringListModel、QStandardItemModel),视图组件的应用(如QListView、QTableView),以及它们如何通过代理和信号槽实现数据同步与编辑。重点讲解了QFileSystemModel在文件系统应用中的实例,以及如何设置模型和视图的关联。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Model/View结构

基本概念

用Model接口可以修改数据,在View组件中修改也会自动保存到Model中

所有的View组件,都要关联Model, 

所有的SelectionModel,都需要关联Model

模型/视图结构是Qt中组件显式和数据编辑的一种结构,View视图是界面组件,Model则是为数据和视图提供的一种接口模型。

Model和View交流时,可以使用代理,代理在数据被编辑时,通过模型和数据通信,且提供数据编辑器QLineEdit组件,代理则通过编辑器和View通信完成显示。

Model/View的典型应用是在数据库应用程序中。

一个数据模型可在多个视图组件中显示

Model类的继承关系
QAbstractItemModelQAbstractListModelQStringListModel
QAbstractProxyModelQSortFilterProxyModel

QAbstractTableModel

QSqlQueryModelQSqlTableModel

QSqlRelationalTableModel

QStandardItemModel
QFileSystemModel

主要学习的模型类Model

  • QStringListModel 处理字符串列表数据
  • QStandardItemModel 处理基于数据的类,每个项的数据类型随意
  • QFileSystemModel  处理系统文件数据

关于数据模型的基本信息

  • 数据Model中,最基本的单元仍是项Item
    • 每个item都有一个对应的row和column号,和一个father item
      • List和Table数据格式中,所有的item都有一个 root item 顶层项
      • Tree数据格式中,较为复杂。
  • 项item
    • item role 项的角色数据:一个项可以有多个不同的角色数据,用于不同的场合(不同场合扮演不同角色)
      • role往往是Qt::ItemDataRole枚举类型
        • Qt::DisplayRole 表示item role是组件中显示的字符串,也是标准角色
        • Qt::ToolTipRole 表示item role是鼠标提示的信息
        • Qt::UserROle 表示item role是自定义数据
    • value 项的值:即项存储的数据
  • 数据索引:用QModelIndex类来表示模型索引类,为的是数据的存储和表示的隔离
    • 索引必须提供:row,column,fatherItem三个参数
    • QPersistentModelIndex提供持久指针
    • QModelIndex提供临时指针,来修改数据

主要学习的视图组件View

     视图类组件,不像QListWidget等作为View的便利类,在项中存储数据,而是采用单独的数据类型,并不存储数据。

     便利类适合小型数据,Model/View处理大型数据

     在视图组件显示数据时,调用sertModel()为组件设置一个数据模型,完成Model和View的关联。

继承关系组件针对的数据类型
QAbstractItemViewQListViewQListWidget列表数据
QTableViewQTableWidget表格数据
QTreeViewQTreeWidget树形数据
QColumnVIew多个QListView数据
QHeaderVIew表头

QModelIndex

  • 数据索引:用QModelIndex类来表示模型索引类,为的是数据的存储和表示的隔离
    • QModelIndex提供临时指针,来修改数据
    • QPersistentModelIndex提供持久指针
    • 索引必须提供:row,column,fatherItem三个参数
属性/函数解释

QModelIndex

int column() const

int row() const

column() 返回索引对象存的 列

row()返回索引对象存的 行

QModelIndexList

int count() 

QModelIndex at(int)

count()返回列表包含总个数

at()返回第i个元素,返回类型是QModelIndex对象

就是QList

QFileSystemModel

主要功能:该类提供一个访问主机系统文件的数据模型

配合组件QTreeView,目录树显示文件列表,组件需要关联Model

初始化:应给出一个根目录

关于如何获取当前路径,应参考下一章“文件”

1). QFileSystemModel

这个类提供了一个对本地文件系统的访问的模型

不必思考QFileSystemModel是如何获取系统目录,组件是如何显示这些目录的,这是底层代码实现的.

只需要关联模型和组件即可实现.

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    //define new model
    model = new QFileSystemModel(this);  //在.h中声明的model

    //get progress's rootPath
    model->setRootPath(QDir::currentPath());//QDir参考下一章

    //connect View and Model
    ui->treeView->setModel(model);
}

setRootPath() 

设置模型的根目录

QModelIndex setRootPath(const QString &newPath);

isDir()

判断节点是不是目录, 是目录则true, 是文件则false

bool isDir(const QModelIndex &index) const;

filePath()和fileName()

filePath()返回节点的目录,即绝对路径;

fileName()返回该节点的文件/文件夹名,仅仅是个名字

QString fileName(const QModelIndex &index) const;

QString filePath(const QModelIndex &index) const

type()

返回描述节点类型的文字

 QString type(const QModelIndex &index) const;

size()

节点的文件大小, 文件夹返回0

 qint64 size(const QModelIndex &index) const;

2). QTreeView

该组件可以在ui界面直接拖动,即可以右键直接转为对应的槽函数,

clicked()

该信号将传递点击的节点对象,可以通过该信号完成对节点的设置

[signal] void QAbstractItemView::clicked(const QModelIndex &index);

setModel()

为View组件关联一个Model

[virtual] void QAbstractItemView::setModel(QAbstractItemModel *model);

QStringListModel

主要功能:处理字符串列表

配合组件:QListView,在界面上显示编辑字符串, 组件需要关联Model

1). QListView

主要功能是 返回节点索引对象, 设置节点属性

setModel()

setEditTriggers()设置该item/条/节点是否可编辑,参数是枚举类型QAbstractItemView::SelectedClicked,不可编辑则是QAbstractItemView::NoEditTrrigers

void setEditTriggers(QAbstractItemView::EditTriggers triggers);

ui->listView->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::SelectedClicked);

currentIndex()获取当前项的模型索引index, 也可以通过信号传递进来索引对象,

setCurrentIndex()设置索引对象

QModelIndex QAbstractItemView::currentIndex() const;
[slot] void QAbstractItemView::setCurrentIndex(const QModelIndex &index)

void Widget::on_btnListInsert_clicked()
{//插入一行
    QModelIndex  index;
    index=ui->listView->currentIndex(); //当前 modelIndex
    theModel->insertRow(index.row()); //在当前行的前面插入一行
    theModel->setData(index,"inserted item",Qt::DisplayRole); //设置显示文字
    theModel->setData(index,Qt::AlignRight,Qt::TextAlignmentRole); //设置对齐方式,不起作用
    ui->listView->setCurrentIndex(index); //设置当前选中的行
}

2). QStringListModel

主要功能是:添加、删除、插入节点

同样地,在类声明中,声明一个QStringListModel对象

初始化:不同于QFileSystemModel需要给出根目录, QStringListModel则需要给出StringList

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    theModel = new QStringListModel(this);
    QStringList theStringList;
    theStringList<<"a"<<"b"<<"c"<<"d"<<"e";
    theModel->setStringList(theStringList);
    ui->listView->setModel(theModel);

}

而且关联后,可以发现在ListView中可以进行编辑每个项名称

setStringList():把一个字符串列表对象作为模型的初始内容

void QStringListModel::setStringList(const QStringList &strings);

stringList()方法可以获得当前数据的最新字符串列表

void Widget::on_btnTextImport_clicked()
{// 显示数据模型的StringList
    QStringList tmpList;
    tmpList=theModel->stringList();//获取数据模型的StringList

}

insertRow()在第row行前插入一行, 若row是最后一行则在row后追加一行,但不会创建item,只是个空行

index()返回根据father节点,row,col得到的模型节点的索引类对象

rowCount() 获取模型的总行数

bool insertRow(int row, const QModelIndex &parent = QModelIndex());

QModelIndex index(int row, int column = 0, const QModelIndex &parent = ...) const;

int rowCount();

void Widget::on_btnListInsert_clicked()
{//插入一行
    QModelIndex  index;
    index=ui->listView->currentIndex(); //当前 modelIndex
    theModel->insertRow(index.row()); //在当前行的前面插入一行
    theModel->setData(index,"inserted item",Qt::DisplayRole); //设置显示文字
    theModel->setData(index,Qt::AlignRight,Qt::TextAlignmentRole); //设置对齐方式,不起作用
    ui->listView->setCurrentIndex(index); //设置当前选中的行
}

void Widget::on_btnListAppend_clicked()
{ //添加一行
    theModel->insertRow(theModel->rowCount()); //在尾部插入一空行
    QModelIndex index=theModel->index(theModel->rowCount()-1,0);//获取最后一行
    theModel->setData(index,"new item",Qt::DisplayRole);//设置显示文字
    ui->listView->setCurrentIndex(index); //设置当前选中的行
}

removeRow()删除一行代码,参数是index.row()行号

removeRows()从第row行删除count行

bool removeRow(int row, const QModelIndex &parent = QModelIndex());

void Widget::on_btnListDelete_clicked()
{//删除当前行
    QModelIndex  index;
    index=ui->listView->currentIndex(); //获取当前 modelIndex
    theModel->removeRow(index.row()); //删除当前行
}
void Widget::on_btnListClear_clicked()
{//清除ListView的所有项
    theModel->removeRows(0,theModel->rowCount());
}

setData()设置模型节点的数据,包括自定义数据、显示内容, role为DisplayRole时表示显示角色

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

void Widget::on_btnListAppend_clicked()
{ //添加一行
    theModel->insertRow(theModel->rowCount()); //在尾部插入一空行
    QModelIndex index=theModel->index(theModel->rowCount()-1,0);//获取最后一行
    theModel->setData(index,"new item",Qt::DisplayRole);//设置显示文字
    ui->listView->setCurrentIndex(index); //设置当前选中的行
}

QStandardItemModel

主要功能:以item为基础的标准数据模型,主要是二维数据表格

配合组件:QTableView

配合模型:QStandardItemModel, QItemSelectionModel

表格往往涉及到单元格的添加,删除,插入,修改,索引和行列总数等

每个item都是一个QStandardItem类对象,和QTableView关联后,每个table的单元格对应model中的一个项.

一个表格所用到的类:

  • 视图组件QTableView(需和Model关联)
  • 数据模型QStandardItemModel
    • 每个项是一个QStandardItem
  • 项选择模型QItemSelectionModel(创建时需和Model关联)

这里需要关联的是:

SelectionModel需要关联Model;

View不仅要和SelectionModel关联,也要和Model关联

1). QStandardItemModel

功能:行列的添加,删除和插入; item的添加删除插入等; 模型中item的索引

数据可以是程序生成也可以是文件, 该模型有行表头和列表头,10行里第1行是行表头,因此数据区实际只有9行

初始化Model,和上述两个Model的初始化通过一个StringList初始化Model,

        基本思路:StringList的每一行都是一个String, 把该String按规则split多个string,即一个新的StringList(暂且记为newStringList),把newStringList分配逐个分配给表格一行的每个单元格.

       ⚠️一般StringList的第一行作为表格的表头(行表头),也可以把每一行的首个String作为列表头.

为Model添加Item时,应先创建 QStandardItem对象

void MainWindow::iniModelFromStringList(QStringList& aFileContent)
{ //从一个StringList 获取数据,初始化数据Model
    int rowCnt=aFileContent.count(); //文本行数,第1行是标题
    theModel->setRowCount(rowCnt-1); //实际数据行数

//设置表头
    QString header=aFileContent.at(0);//第1行是表头
//一个或多个空格、TAB等分隔符隔开的字符串, 分解为一个StringList
    QStringList headerList=header.split(QRegExp("\\s+"),QString::SkipEmptyParts);
    theModel->setHorizontalHeaderLabels(headerList); //设置表头文字

//设置表格数据
    int j;
    QStandardItem   *aItem;
    for (int i=1;i<rowCnt;i++)
    {
        QString aLineText=aFileContent.at(i); //获取 数据区 的一行
//一个或多个空格、TAB等分隔符隔开的字符串, 分解为一个StringList
        QStringList tmpList=aLineText.split(QRegExp("\\s+"),QString::SkipEmptyParts);
        for (j=0;j<FixedColumnCount-1;j++) //tmpList的行数等于FixedColumnCount, 固定的
        { //不包含最后一列
            aItem=new QStandardItem(tmpList.at(j));//创建item
            theModel->setItem(i-1,j,aItem); //为模型的某个行列位置设置Item
        }

        aItem=new QStandardItem(headerList.at(j));//最后一列是Checkable,需要设置
        aItem->setCheckable(true); //设置为Checkable
        if (tmpList.at(j)=="0")
            aItem->setCheckState(Qt::Unchecked); //根据数据设置check状态
        else
            aItem->setCheckState(Qt::Checked);
        theModel->setItem(i-1,j,aItem); //为模型的某个行列位置设置Item
    }
}

setItem()  为Model设置item

void QStandardItemModel::setItem(int row, int column, QStandardItem *item);

theModel->setItem(i-1,j,aItem);

QStandardItemModel()创建一个新的模型,指定行列数,和父类往往是this指针

QStandardItemModel(int rows, int columns, QObject *parent = nullptr);

theModel = new QStandardItemModel(2,2,this); //数据模型

rowCount()

columnCount()分别返回模型行列数

horizontalHeaderItem()返回第i行的表头项对象

headerData()获取指定的水平/垂直方向的第section个列/行表头项的自定义数据

setRowCount()设置模型包含的行数

setHorizontalHeaderLabels()设置行表头


removeRow()删除指定行

itemFromIndex()从数据模型中获取数据

QStandardItem *QStandardItemModel::itemFromIndex(const QModelIndex &index) const;


void MainWindow::on_currentChanged(const QModelIndex &current, const QModelIndex &previous)
{ //选择单元格变化时的响应
   Q_UNUSED(previous);

    if (current.isValid()) //当前模型索引有效
    {

        QStandardItem   *aItem;
        aItem=theModel->itemFromIndex(current); //从模型索引获得Item
        this->LabCellText->setText("单元格内容:"+aItem->text()); //显示item的文字内容

    }
}

index()返回模型指定行列的item的索引对象

insertRow()在第row行前插入一个QList对象,row为末尾时则在其后插入

QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;

void insertRow(int row, const QList<QStandardItem *> &items);



void MainWindow::on_actAppend_triggered()
{ //在表格最后添加行
    QList<QStandardItem*>    aItemList; //容器类
    QStandardItem   *aItem;
    for(int i=0;i<FixedColumnCount-1;i++) //不包含最后1列
    {
        aItem=new QStandardItem("0"); //创建Item
        aItemList<<aItem;   //添加到容器
    }
//获取最后一列的表头文字
    QString str=theModel->headerData(theModel->columnCount()-1,Qt::Horizontal,Qt::DisplayRole).toString();
    aItem=new QStandardItem(str); //创建 "测井取样"Item
    aItem->setCheckable(true);
    aItemList<<aItem;   //添加到容器

    theModel->insertRow(theModel->rowCount(),aItemList); //插入一行,需要每个Cell的Item
    QModelIndex curIndex=theModel->index(theModel->rowCount()-1,0);//创建最后一行的ModelIndex
    theSelection->clearSelection();//清空选择项
    theSelection->setCurrentIndex(curIndex,QItemSelectionModel::Select);//设置刚插入的行为当前选择行
}

2). QStandardItem

Model每个项的类型

功能: 每个item的格式、属性等,如对齐,居中,可选性等

QStandardItem()重载了很多函数,可以直接将项数据以参数形式传递,完成QStandardItem类对象的创建及赋值

QStandardItem(const QString &text);

aItem=new QStandardItem(tmpList.at(j));//创建item

setTextAlignment()设置该项左对齐,右对齐时Qt::AlignRight,是枚举量,居中是Qt::AlignHCenter

setCheckable()

void QStandardItem::setCheckable(bool checkable);

aItem->setCheckable(true); 

3). QTableView

setModel()关联一个Model

setSelectionModel()关联一个selectionModel

ui->tableView->setModel(theModel); //设置数据模型
ui->tableView->setSelectionModel(theSelection);//设置选择模型

4). QItemSelectionModel

功能: 该类主要用于跟踪单元格选择状态

QItemSelectionModel()

QItemSelectionModel(QAbstractItemModel *model, QObject *parent);

theModel = new QStandardItemModel(2,FixedColumnCount,this); //数据模型
theSelection = new QItemSelectionModel(theModel);//Item选择模型

currentIndex()获取当前的模型索引号,返回一个QModelIndex对象

QModelIndex QItemSelectionModel::currentIndex() const;

setCurrentIndex()设置该index为当前项,并发射currentChanged()信号

clearSelection()清理选择项

[slot] void clearSelection();
[virtual slot] void setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command);


theSelection->clearSelection();//清空选择项
theSelection->setCurrentIndex(curIndex,QItemSelectionModel::Select);//设置刚插入的行为当前选择行

hasSelection()当该对象包含了任何一个项时,都返回true反之false

selectedIndexes()返回一个选择项的索引列表

bool QModelIndex::isValid() const;

if (current.isValid()) //当前模型索引有效
{
}

信号:

currentChanged()发射当前位置和上次位置

[signal] void QItemSelectionModel::currentChanged(const QModelIndex &current, const QModelIndex &previous);

connect(theSelection,SIGNAL(currentChanged(QModelIndex,QModelIndex)),
            this,SLOT(on_currentChanged(QModelIndex,QModelIndex)));

文件

路径

在文件中,路径是首先要考虑到的内容,主要是路径的 读取问题
Qt为文件和目录操作提供了一些类,利用这些类可以方便地实现一些操作。Qt提供的与文件和目录操作相关的类包括以下几个:


        QCoreApplication:用于提取应用程序路径、程序名等文件信息。
        QFile:除了打开文件操作外,QFile还有复制文件、删除文件等功能。
        QFileInfo:用于提取文件的信息,包括路径、文件名、后缀等。
        QDir:用于提取目录或文件信息,获取一个目录下的文件或目录列表,创建或删除目录和文件,文件重命名等操作。
        QTemporaryDirQTemporaryFile:用于创建临时目录和临时文件。
        QFileSystemWatcher:文件和目录监听类,监听目录下文件的添加、删除等变化,监听文件修改变化。

这些类基本涵盖了文件操作需要的主要功能,有些功能还在某些类里重复出现,例如QFile和QDir都具有删除文件、判断文件是否存在的功能。

简要介绍上述类的部分函数

QCoreApplication的有用函数

QString applicationDirPath();//返回应用程序启动路径
QString applicationFilePath();//返回应用程序的带有目录的完整文件名
QString applicationName();//返回应用程序名称,无路径无后缀
QStringList libraryPaths();//返回动态加载库文件时,应用程序搜索的目录列表
void setOrganizationName(QString &orgName);//为应用程序设置一个机构名
QString organizationName();//返回应用程序的机构名、
void exit();//退出应用程序 

QFile的一些静态函数

bool copy(QString &fileName,QString &newName)         ;//复制文件
bool rename(QString &oldName,QString&newName)        ;//重命名文件
bool remove(QString &fileName)        ;//删除一个文件
bool exists(QString &fileName)        ;//判断文件是否存在
bool setPermissions(QString &fileName,Permissions permissions);//设置文件的权限,权限类型是枚举类QFileDevice::Permission 

QFile的成员函数

void setFileName(QString &name);//设置文件名,文件已打开后不能再调用此函数
bool copy(QString&newName);//当前文件复制为newName表示的文件
bool rename(QString &newName);//将当前文件重命名为newName
bool remove();//删除当前文件
bool exists();//判断当前文件是否存在
bool setPermissions(Permissions permissions);//设置文件权限
Permissions permissions();//返回文件的权限
qint64 size();//返回文件的大小,字节数

QFilelnfo的一些函数

void setFile(QString &file);//设置一个文件名,作为QFileInfo操作的文件
QString absoluteFilePath();//返回带有文件名的绝对文件路径
QString absolutePath();//返回绝对路径,不带文件名
QString fileName();//返回去除路径的文件名
QString filePath();//返回包含路径的文件名
QString path();//返回不含文件名的路径
qint64 size();//返回文件大小,以字节为单位


bool isDir();//判断当前对象是否是一个目录或目录的快捷方式
bool isFile();//判断当前对象是否是一个文件或文件的快捷方式
bool isExecutable();//判断当前文件是否是可执行文件
QDateTime created();//返回文件创建时间
QDateTime lastModified();//返回文件最后一次被修改的时间
QDateTime lastRead();//返回文件最后一次被读取的时间
bool exists();//判断文件是否存在
bool exists(QString &file);//静态函数,判断file表示的文件是否存在

QDir的一些静态函数

QString tempPath();    //返回临时文件目录名称

QString rootPath();    //返回根目录名称

QSting currentPath();    //返回当前目录

bool setCurrent(QString &path); //设置当前目录

QDir的一些成员函数

QString absoluteFilePath(QString &fileName);
//返回当前目录下的一个文件的含绝对路径文件名

QString absolutePath();//返回当前目录的绝对路径
QString canonicalPath();//返回当前目录的标准路径
QString filePath(QString &fileName);//返回目录下一个文件的目录名
QString dirName();//返回最后一级目录的名称
bool exists();//判断当前目录是否存在
QStringList entryList(Filters filters =NoFilter,SortFlags sort=NoSort);
//返回目录下的所有文件名、子目录名等
bool mkdir(QString &dirName);//在当前目录下建一个名称为dirName的子目录
bool rmdir(QString &dirName);//删除指定的目录dirName
bool remove(QString &fileName);//删除当前目录下的文件fileName
bool rename(QString &oldName,QString &newName);//将文件或目录oldName更名为 newName

void setPath(QString &path);//设置QDir对象的当前目录
bool removeRecursively();//删除当前目录及其下面的所有文件

对QDir的成员函数的部分举例

QDir::entryList()重点掌握

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QDir path;

    qDebug()<<"now:"<<path.currentPath()<<endl;
    QString newPath = "C:/Users/sherlock/Pictures/" ;
    QDir::setCurrent(newPath);
    qDebug()<<"new:"<<path.currentPath()<<endl;
    qDebug()<<"all:"<<path.entryList()<<endl;
}


//output
now: "C:/Users/sherlock/Desktop/qt_allSamp/build-untitled1-Desktop_Qt_5_14_2_MSVC2017_64bit-Debug" 

new: "C:/Users/sherlock/Pictures" 

all: (".", "..", "20210201201941.png", "20210201202053.png", "20210201202131.png", "20210201202150.png", "20210201202237.png", "20210214114247.png", "20210214114253.png", "20210214114258.png", "20210214114324.png", "20210214114407.png", "20210214114411.png", "20210214114413.png", "20210214114419.png", "20210214114423.png", "20210214114445.png", "20210214114456.png", "20210214114506.png", "20210214114536.png", "20210214152912.png", "Camera Roll", "IMG_6628.JPG", "IMG_6632.JPG", "Saved Pictures", "test.JPG") 

QTemporaryDir 和QTemporaryFile


1).QTemporaryDir是用于创建、删除临时目录的类,
QTemporaryDir的一些成员函数

void setAutoRemove(bool b);//设置为是否自动删除
QString path();//返回创建的临时目录名称
bool remove();//删除此临时目录及其下面所有文件

在系统临时目录,即QDir:tempPath目录下创建一个临时目录,临时目录名称以QCoreApplication::applicationName()为前缀,后加6个字符。

临时目录可以设置为使用完后自动删除,即临时目录变量删除时,临时目录也删除。
QTemporaryFile 是用于创建临时文件的类,临时文件保存在系统临时目录下。

临时文件以QCoreApplication::applicationName()作为文件名,以“XXXXXX”6个随机数字作为文件后缀。

将QTemporaryFile::setAutoRemove()函数设置为是否自动删除临时文件,

QTemporaryFile::open(函数用于打开临时文件,只有打开临时文件,才实际创建了此文件。


2).QFileSystemWatcher 是对目录和文件进行监听的类。

把某些目录或文件添加到 QFileSystemWatcher 对象的监听列表后,当目录下发生文件新建、删除等操作时会发射 directoryChange()信号,当监听的文件发生修改、重命名等操作时,会发射 fileChanged()信号.

这个类在进行目录或文件监听时起作用。


QFileSystemWatcher的成员函数

bool addPath(QString &path);//添加一个监听的目录或文件
QStringList addPaths(QStringList &paths);//添加需要监听的目录或文件列表
QStringList directories0);//返回监听的目录列表
QStringList files();//返回监听的文件列表
bool removePath(QString &path);//移除监听的目录或文件
QStringList removePaths(QStringList &paths);//移除监听的目录或文件列表

QFileSystemWatcher类有两个信号,分别是:

目录变化信号

void QFileSystemWatcher::directoryChanged(const QString &path);

文件变化信号

void QFileSystemWatcher::fileChanged(const QString &path);

停止监听时,用removePath()移除监听对象,并且用disconnect()解除已关联的信号与槽. 

文件操作

纯文本文件(.txt)

文件操作必须要进行的是:

        在读取文件后,要判断文件是否存在,文件读取是否成功,

        文件使用完毕后,需要关闭文件(按需)

文件处理的方法有两种:

        1.QIODevice类: QFile负责文件的打开,保存关闭等操作,其他的操作交给QIODevice对象

        2.QTextStream类: 其只负责文件的修改,并不负责文件的打开和关闭保存等, 因此需要关联QFile类对象完成上述操作

QFile

一般常借助QFileDialog对话框打开,无需交互时,可以不使用对话框

文件打开一次后,完成write()操作后,不能再次读取, 需要再次读取,需要关闭文件,再次打开

//二次读取
void MainWindow::on_readFile_clicked()
{
    QString currentPath = QDir::currentPath();
    QString title = "open file";
    QString filter = "txt(*.txt);;all(*.*)";
    QString fileName = QFileDialog::getOpenFileName(this,title,currentPath,filter);
    QFile myFile(fileName);
    myFile.open(QIODevice::ReadWrite);

    ui->plainTextEdit->appendPlainText(myFile.readAll());

    QString wtData = ui->plainTextEdit->toPlainText();
    QByteArray arr = wtData.toUtf8();
    myFile.write(arr);
    myFile.close();
    QFile resultFile(fileName);
    resultFile.open(QIODevice::ReadOnly);
    ui->plainTextEdit_cur->appendPlainText(resultFile.readAll());

}

负责文件的打开,保存关闭等

QFile : QFileDevice : QIODevice

QFile()构造函数传递文件地址字符串

QFile::QFile(const QString &name);

exists()判断文件是否存在

bool QFile::exists() const;

if (!aFile.exists()) //文件不存在
    return false;

open()传递参数为打开文件的方式:

        QIODevice::NotOpen 不打开
        QIODevice::ReadOnly 只读
        QIODevice::WriteOnly 只写
        QIODevice::ReadWrite 读写
        QIODevice::Append 末尾追加
        QIODevice::Truncate 覆盖
        QIODevice::Text 文本方式打开

        上述模式都可以 | 多选

bool QFile::open(OpenMode mode);

if (!aFile.open(QIODevice::ReadOnly | QIODevice::Text))
    return false;

close()关闭类对象关联的文件

void QIODevice::close();

 读文件并显示

void MainWindow::on_readFile_clicked()
{
    QString currentPath = QDir::currentPath();
    QString title = "open file";
    QString filter = "txt(*.txt);;all(*.*)";
    QString fileName = QFileDialog::getOpenFileName(this,title,currentPath,filter);
    QFile myFile(fileName);
    myFile.open(QIODevice::ReadWrite);
    ui->plainTextEdit->appendPlainText(myFile.readAll());
}

QIODevice

文件的读取,修改,添加,等

readAll()读取类对象关联的文件的全部内容

QByteArray QIODevice::readAll();

bool MainWindow::openTextByIODevice(const QString &aFileName)
{//用IODevice方式打开文本文件
    QFile   aFile(aFileName);
//    aFile.setFileName(aFileName);

    if (!aFile.exists()) //文件不存在
        return false;

    if (!aFile.open(QIODevice::ReadOnly | QIODevice::Text))
        return false;

    ui->textEditDevice->setPlainText(aFile.readAll());
//    ui->textEditDevice->clear();
//    while (!aFile.atEnd())
//    {
//        QByteArray line = aFile.readLine();//自动添加 \n
//        QString str=QString::fromLocal8Bit(line); //从字节数组转换为字符串
//        str.truncate(str.length()-1); //去除结尾增加的空行
//        ui->textEditDevice->appendPlainText(str);
//    }
    aFile.close();

    ui->tabWidget->setCurrentIndex(0);
    return  true;
}

atEnd()当类对象关联的文件没有内容时返回true

write()写入类对象关联的文件, 具体是覆盖写入还是追加写入应参考文件打开的参数.

        写入文件时应将字符串转换成字节数组,

                转换字节数组的方法有.toUf8()和 .toLocal8bit()

        data是字节数组, maxSize是写入的最大个数

qint64 QIODevice::write(const char *data, qint64 maxSize);

bool MainWindow::saveTextByIODevice(const QString &aFileName)
{ //用IODevice方式保存文本文件
    QFile   aFile(aFileName);
//    aFile.setFileName(aFileName);

    if (!aFile.open(QIODevice::WriteOnly | QIODevice::Text))
        return false;

    QString str=ui->textEditDevice->toPlainText();//整个内容作为字符串

    QByteArray  strBytes=str.toUtf8();//转换为字节数组
//    QByteArray  strBytes=str.toLocal8Bit();

    aFile.write(strBytes,strBytes.length());  //写入文件

    aFile.close();
    ui->tabWidget->setCurrentIndex(0);
    return  true;
}

QTextStream

QFile直接凭借着和QIODevice的继承关系使用成员函数raedAll();

QTextStream需要先创建对象,关联QFile对象,然后利用<< 或者>>传递数据

QTextStream()

        构造函数需要传递一个QIODevice类对象, 使得QIODevice和QTextStream类对象结合起来

readAll()

        函数读取文本全部内容

setAutoDetectUnicode()

        传递参数为true将识别中文字符避免乱码

atEnd()

        没有数据可读取时返回true,与QIODevice::atEnd()不同,前者数据包括了Unicode缓存区,而后者没有

<<

        对于QTextStream无需write()函数,只需要<<流入到对象中即完成内容的写入

>>

        读出

bool MainWindow::openTextByStream(const QString &aFileName)
{ //用 QTextStream打开文本文件
    QFile   aFile(aFileName);

    if (!aFile.exists()) //文件不存在
        return false;

    if (!aFile.open(QIODevice::ReadOnly | QIODevice::Text))
        return false;

    QTextStream aStream(&aFile); //用文本流读取文件
//    aStream.setAutoDetectUnicode(true); //自动检测Unicode,才能正常显示文档内的汉字

    ui->textEditStream->setPlainText(aStream.readAll());

//    ui->textEditStream->clear();//清空
//    while (!aStream.atEnd())
//    {
//        str=aStream.readLine();//读取文件的一行
//        ui->textEditStream->appendPlainText(str); //添加到文本框显示
//    }

    aFile.close();//关闭文件
    ui->tabWidget->setCurrentIndex(1);
    return  true;
}

bool MainWindow::saveTextByStream(const QString &aFileName)
{//用QTextStream保存文本文件
    QFile   aFile(aFileName);

    if (!aFile.open(QIODevice::WriteOnly | QIODevice::Text))
        return false;

    QTextStream aStream(&aFile); //用文本流读取文件
//    aStream.setAutoDetectUnicode(true); //自动检测Unicode,才能正常显示文档内的汉字

    QString str=ui->textEditStream->toPlainText(); //转换为字符串

    aStream<<str; //写入文本流


//    QTextDocument   *doc;       //文本对象
//    QTextBlock      textLine;   //文本中的一段
//    doc=ui->textEditStream->document(); //QPlainTextEdit 的内容保存在一个 QTextDocument 里
//    int cnt=doc->blockCount();//QTextDocument分块保存内容,文本文件就是硬回车符是一个block,

//    QString str;
//    for (int i=0; i<cnt; i++) //扫描所有 blobk
//    {
//         textLine=doc->findBlockByNumber(i);//用blobk编号获取block,就是获取一行
//         str=textLine.text(); //转换为文本,末尾无\n
//         aStream<<str<<"\n";
//    }

    aFile.close();//关闭文件

    return  true;

}

二进制文件

什么是二进制文件,通常来讲除了文本文件之外,其他的以一定格式读写的文件,都是二进制文件.

每种二进制文件都有其具体的格式定义,读写时务必按规则读写.

通常: 写入数据时,并不知道每个字节是如何存储的,只知道写入内容的顺序和对应的数据类型(如int), 读出数据时,只要按照相应的顺序和类型对应读出即可.

二进制文件编码方式的差异,决定了文件格式的差异,在Qt中,往往根据文件编码方式的差异,保存为两种文件格式

  1. .stm文件, 该文件是Qt预定义编码,保存各种类型的文件,
    1. Qt预定义编码: 在写入某个类型数据到文本流时,比如int,string等,使用的书写格式是Qt预定义的格式,书写的过程中利用的是Qt预定义标记符, 同理,在读出该文件时,根据Qt预定义标记符读出
    2. ⚠️ 在写入文件流时,或许产生一些我们不知道的符号,这些符号对原始数据没什么意义,不必在意
  2. .dat文件,标准编码数据文件
    1. 标准编码数据文件:在写入到文件时,完全使用数据的二进制原始内容,每个二进制文件的字节都对应相应的含义,读出时只需要根据含义即可完成数据的转换
    2. 字节序:确定数据的存储字节序是读取数据的基础
      1. 小端字节序:小字节数据 存放在 内存地址较低的位置, x86平台的都是小端字节序(windows系统)
      2. 大端字节序: 小字节数据 存放在 内存地址较高的位置

1⃣️对于stm文件:

        -1-必须制定Qt版本,因为系统内部存储方式等可能不同

        -2-关于>>和<<,QDataStream读出的时候,只是根据>>流向的对象的类型的字节数,来确定的,比如QDataStream对象流入时顺序是有int 和string,和char

        那么读出的时候按照同样的顺序,int string char读出即可

        无需关注内部如何存储的

        -3-❌缺点就是数据内部不透明,很难进行数据交换

2⃣️对于dat文件:

        格式通用,即数据内部透明,有利于数据交换(即数据的每个字节位置存的信息含义是通用的)

        对于dat文件,往往需要知道写入的数据的字节长度,因此writeRawData和writeBytes()传递数据时不仅传递了char*字节指针,还有其长度,因为读取时必须知道该长度

如何保存stm或者dat格式文件?

qt并没有接口选择保存文件的格式类型,

只需要在保存文件时,将后缀改为.stm 或者.dat即可

一般会在QFileDialog中设置保存的后缀,如(*.dat)

对于二进制文件, QFile负责文件的接口, QDataStream负责文件的处理

QFile

和文本文件一致,看上边

QDataStream

QDataStream()将QDataStream和QFile对象关联

Version属性是枚举量,其内容参考Qt文档,主要是记录Qt版本内容

int QDataStream::version() const;

setVersion()设置Version属性,用于标识当前程序所用的Qt版本

void QDataStream::setVersion(int v);

aStream.setVersion(QDataStream::Qt_5_9); //设置版本号,写入和读取的版本号要兼容

1). 对于qt预定义文件.stm

<<采用流的方法将文本内容流入文件中

>>读出 e.g.  aFileStream>>str; 

写入案例:

bool MainWindow::saveDataAsStream(QString &aFileName)
{//将模型数据保存为Qt预定义编码的数据文件
    QFile aFile(aFileName);  //以文件方式读出
    if (!(aFile.open(QIODevice::WriteOnly | QIODevice::Truncate)))
        return false;

    QDataStream aStream(&aFile);
    aStream.setVersion(QDataStream::Qt_5_9); //设置版本号,写入和读取的版本号要兼容

    qint16  rowCount=theModel->rowCount(); //数据模型行数
    qint16  colCount=theModel->columnCount(); //数据模型列数

    aStream<<rowCount; //写入文件流,行数
    aStream<<colCount;//写入文件流,列数

//获取表头文字
    for (int i=0;i<theModel->columnCount();i++)
    {
        QString str=theModel->horizontalHeaderItem(i)->text();//获取表头文字
        aStream<<str; //字符串写入文件流,Qt预定义编码方式
    }


//获取数据区的数据
    for (int i=0;i<theModel->rowCount();i++)
    {
        QStandardItem* aItem=theModel->item(i,0); //测深
        qint16 ceShen=aItem->data(Qt::DisplayRole).toInt();
        aStream<<ceShen;// 写入文件流,qint16

        aItem=theModel->item(i,1); //垂深
        qreal chuiShen=aItem->data(Qt::DisplayRole).toFloat();
        aStream<<chuiShen;//写入文件流, qreal

        aItem=theModel->item(i,2); //方位
        qreal fangWei=aItem->data(Qt::DisplayRole).toFloat();
        aStream<<fangWei;//写入文件流, qreal

        aItem=theModel->item(i,3); //位移
        qreal weiYi=aItem->data(Qt::DisplayRole).toFloat();
        aStream<<weiYi;//写入文件流, qreal

        aItem=theModel->item(i,4); //固井质量
        QString zhiLiang=aItem->data(Qt::DisplayRole).toString();
        aStream<<zhiLiang;// 写入文件流,字符串

        aItem=theModel->item(i,5); //测井
        bool quYang=(aItem->checkState()==Qt::Checked);
        aStream<<quYang;// 写入文件流,bool型
    }
    aFile.close();
    return true;
}

读取案例:

bool MainWindow::openDataAsStream(QString &aFileName)
{ //从Qt预定义流文件读入数据
    QFile aFile(aFileName);  //以文件方式读出
    if (!(aFile.open(QIODevice::ReadOnly)))
        return false;

    QDataStream aStream(&aFile); //用文本流读取文件
    aStream.setVersion(QDataStream::Qt_5_9); //设置流文件版本号

    qint16  rowCount,colCount;
    aStream>>rowCount; //读取行数
    aStream>>colCount; //列数

    this->resetTable(rowCount); //表格复位

    //获取表头文字
    QString str;
    for (int i=0;i<colCount;i++)
        aStream>>str;  //读取表头字符串

    //获取数据区文字,
    qint16  ceShen;
    qreal  chuiShen;
    qreal  fangWei;
    qreal  weiYi;
    QString  zhiLiang;
    bool    quYang;
    QStandardItem   *aItem;
    QModelIndex index;

    for (int i=0;i<rowCount;i++)
    {
        aStream>>ceShen;//读取测深, qint16
        index=theModel->index(i,0);
        aItem=theModel->itemFromIndex(index);
        aItem->setData(ceShen,Qt::DisplayRole);

        aStream>>chuiShen;//垂深,qreal
        index=theModel->index(i,1);
        aItem=theModel->itemFromIndex(index);
        aItem->setData(chuiShen,Qt::DisplayRole);


        aStream>>fangWei;//方位,qreal
        index=theModel->index(i,2);
        aItem=theModel->itemFromIndex(index);
        aItem->setData(fangWei,Qt::DisplayRole);


        aStream>>weiYi;//位移,qreal
        index=theModel->index(i,3);
        aItem=theModel->itemFromIndex(index);
        aItem->setData(weiYi,Qt::DisplayRole);


        aStream>>zhiLiang;//固井质量,QString
        index=theModel->index(i,4);
        aItem=theModel->itemFromIndex(index);
        aItem->setData(zhiLiang,Qt::DisplayRole);

        aStream>>quYang;//bool
        index=theModel->index(i,5);
        aItem=theModel->itemFromIndex(index);
        if (quYang)
            aItem->setCheckState(Qt::Checked);
        else
            aItem->setCheckState(Qt::Unchecked);
    }

    aFile.close();
    return true;
}

可以发现,在二进制文件的写入和读取中,都是采用了按照特定的顺序进行的, 并没有像数组一般可以索引读取 

2). 对于.dat文件

在写入和读取.dat文件时,必须要先设置byteOrder()即字节序

setByteOrder()设置文件的字节序,参数是枚举量

        QDataStream::LittleEndian小端字节序        

        QDataStream::BIgEndian大端字节序

void QDataStream::setByteOrder(QDataStream::ByteOrder bo);

aStream.setByteOrder(QDataStream::LittleEndian);//windows平台

writeRawData() 在保存qint,qreal等类型的数据时使用这个函数,返回的是实际写入的字节数

        务必注意,s是指向字节数据的指针,该函数调用后会向文本从s指向的起点开始把len个字节数据,写入到文本中, 注意*s是字节数据,并不是文本

        在写入时,应在参数列表将字节数据转为指针((char*)&rowCount)
        len是字节长度,因此需要sizeof(qint16)

int QDataStream::writeRawData(const char*s, int len);

qint16  rowCount=theModel->rowCount();
qint16  colCount=theModel->columnCount();

aStream.writeRawData((char *)&rowCount,sizeof(qint16)); //写入文件流
aStream.writeRawData((char *)&colCount,sizeof(qint16));//写入文件流

writeBytes()写入字符串数据到文本时,使用该函数,两者的都传递字节型指针char*, 和对应的长度,char是一个字节,因此只需要将数

        注意要先将字符串转换成字符数组

        数组名就是第一个元素地址

QDataStream& writeBytes(const char*s, uint len);

QBytesArray btArray;
QString str= "abcdefg";
btArray=str.toUtf8(); //转换为字符数组
aStream.writeBytes(btArray,btArray.length()); //写入文件流,长度uint型,然后是字符串内容

在读取前,务必设置byteOrder

readRawData()读取qint, qreal等基本数据时使用该函数

         char*是读出的存储对象,int是读的长度,返回的int是实际读取的字节长度

int QDataStream::readRawData(char* s, int len);

qint16  rowCount,colCount;
aStream.readRawData((char *)&rowCount, sizeof(qint16));

readBytes(),读取字符串时,使用该函数

        由于在写入时字符串是先转换成字节数组再存入的, 因此读取时,读到的内容是字节数组,需要进行转换

QDataStream& QdataStream::readBytes(char*s, uint &len)

    //获取表头文字,但是并不利用
    char *buf;
    uint strLen;  //也就是 quint32
    for (int i=0;i<colCount;i++)
    {
        aStream.readBytes(buf,strLen);//同时读取字符串长度,和字符串内容
        QString str=QString::fromLocal8Bit(buf,strLen); //可处理汉字
    }

案例

void MainWindow::on_saveFile_clicked()
{
    //open file
    QString currentPath = QDir::currentPath();
    QString title = "open file";
    QString filter = "txt(*.txt);;dat(*.dat);;all(*.*)";
    QString fileName = QFileDialog::getOpenFileName(this,title,currentPath,filter);
    QFile myFile(fileName);
    myFile.open(QIODevice::ReadWrite);

    //operate file
    QDataStream myStream(&myFile);
    myStream.setByteOrder(QDataStream::LittleEndian);
    qint16 score = 99;
    QString name ="xjh";
    myStream.writeRawData((char*)&score,sizeof(qint16));
    QByteArray nameArr = name.toUtf8();
    qDebug()<<nameArr<<endl;
    myStream.writeBytes(nameArr,nameArr.length());
    myFile.close();
}

void MainWindow::on_readFile_clicked()
{

    QString currentPath = QDir::currentPath();
    QString title = "open file";
    QString filter = "txt(*.txt);;dat(*.dat);;all(*.*)";
    QString fileName = QFileDialog::getOpenFileName(this,title,currentPath,filter);

    QFile resultFile(fileName);
    resultFile.open(QIODevice::ReadOnly);
    QDataStream resStream(&resultFile);
    resStream.setByteOrder(QDataStream::LittleEndian);
    qint16 resScore;
    resStream.readRawData((char*)&resScore,sizeof(qint16));
    qDebug()<<resScore<<endl;

    char* resNameArr;
    uint resLenth;
    QString resName;
    resStream.readBytes(resNameArr,resLenth);
    resName = QString::fromLocal8Bit(resNameArr,resLenth);

    ui->plainTextEdit_cur->setPlainText(resName);

}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值