Model/View结构
基本概念
用Model接口可以修改数据,在View组件中修改也会自动保存到Model中
所有的View组件,都要关联Model,
所有的SelectionModel,都需要关联Model
模型/视图结构是Qt中组件显式和数据编辑的一种结构,View视图是界面组件,Model则是为数据和视图提供的一种接口模型。
Model和View交流时,可以使用代理,代理在数据被编辑时,通过模型和数据通信,且提供数据编辑器QLineEdit组件,代理则通过编辑器和View通信完成显示。
Model/View的典型应用是在数据库应用程序中。
一个数据模型可在多个视图组件中显示
Model类的继承关系 | ||||
QAbstractItemModel | QAbstractListModel | QStringListModel | ||
QAbstractProxyModel | QSortFilterProxyModel | |||
QAbstractTableModel | QSqlQueryModel | QSqlTableModel |
QSqlRelationalTableModel | |
QStandardItemModel | ||||
QFileSystemModel |
主要学习的模型类Model
- QStringListModel 处理字符串列表数据
- QStandardItemModel 处理基于项数据的类,每个项的数据类型随意
- QFileSystemModel 处理系统文件数据
关于数据模型的基本信息
- 数据Model中,最基本的单元仍是项Item
- 每个item都有一个对应的row和column号,和一个father item
- List和Table数据格式中,所有的item都有一个 root item 顶层项
- Tree数据格式中,较为复杂。
- 每个item都有一个对应的row和column号,和一个father item
- 项item
- item role 项的角色数据:一个项可以有多个不同的角色数据,用于不同的场合(不同场合扮演不同角色)
- role往往是Qt::ItemDataRole枚举类型
- Qt::DisplayRole 表示item role是组件中显示的字符串,也是标准角色
- Qt::ToolTipRole 表示item role是鼠标提示的信息
- Qt::UserROle 表示item role是自定义数据
- role往往是Qt::ItemDataRole枚举类型
- value 项的值:即项存储的数据
- item role 项的角色数据:一个项可以有多个不同的角色数据,用于不同的场合(不同场合扮演不同角色)
- 数据索引:用QModelIndex类来表示模型索引类,为的是数据的存储和表示的隔离
- 索引必须提供:row,column,fatherItem三个参数
- QPersistentModelIndex提供持久指针
- QModelIndex提供临时指针,来修改数据
主要学习的视图组件View
视图类组件,不像QListWidget等作为View的便利类,在项中存储数据,而是采用单独的数据类型,并不存储数据。
便利类适合小型数据,Model/View处理大型数据
在视图组件显示数据时,调用sertModel()为组件设置一个数据模型,完成Model和View的关联。
继承关系 | 组件针对的数据类型 | ||
QAbstractItemView | QListView | QListWidget | 列表数据 |
QTableView | QTableWidget | 表格数据 | |
QTreeView | QTreeWidget | 树形数据 | |
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 ¤t, 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 ¤t, const QModelIndex &previous);
connect(theSelection,SIGNAL(currentChanged(QModelIndex,QModelIndex)),
this,SLOT(on_currentChanged(QModelIndex,QModelIndex)));
文件
路径
在文件中,路径是首先要考虑到的内容,主要是路径的 读取问题
Qt为文件和目录操作提供了一些类,利用这些类可以方便地实现一些操作。Qt提供的与文件和目录操作相关的类包括以下几个:
QCoreApplication:用于提取应用程序路径、程序名等文件信息。
QFile:除了打开文件操作外,QFile还有复制文件、删除文件等功能。
QFileInfo:用于提取文件的信息,包括路径、文件名、后缀等。
QDir:用于提取目录或文件信息,获取一个目录下的文件或目录列表,创建或删除目录和文件,文件重命名等操作。
QTemporaryDir 和QTemporaryFile:用于创建临时目录和临时文件。
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中,往往根据文件编码方式的差异,保存为两种文件格式
- .stm文件, 该文件是Qt预定义编码,保存各种类型的文件,
- Qt预定义编码: 在写入某个类型数据到文本流时,比如int,string等,使用的书写格式是Qt预定义的格式,书写的过程中利用的是Qt预定义标记符, 同理,在读出该文件时,根据Qt预定义标记符读出
- ⚠️ 在写入文件流时,或许产生一些我们不知道的符号,这些符号对原始数据没什么意义,不必在意
- .dat文件,标准编码数据文件
- 标准编码数据文件:在写入到文件时,完全使用数据的二进制原始内容,每个二进制文件的字节都对应相应的含义,读出时只需要根据含义即可完成数据的转换
- 字节序:确定数据的存储字节序是读取数据的基础
- 小端字节序:小字节数据 存放在 内存地址较低的位置, x86平台的都是小端字节序(windows系统)
- 大端字节序: 小字节数据 存放在 内存地址较高的位置
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);
}