天天看點

《QT5.9 c++ 開發指南》第7章 二進制檔案讀寫

       本章有3個自定義元件的,這裡就不詳細去細講,後期會有一章全面的講解,由于時間問題,例子裡面中文是對每個代碼的解析,希望簡單去記錄裡面的實作過程:

老規矩,先上頁面布局,後期需要源碼的同學們,留言我會添加進取:

《QT5.9 c++ 開發指南》第7章 二進制檔案讀寫
《QT5.9 c++ 開發指南》第7章 二進制檔案讀寫

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

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

#include    "qwintspindelegate.h"
#include    "qwfloatspindelegate.h"
#include    "qwcomboboxdelegate.h"


#define     FixedColumnCount    6       //檔案固定6行

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT
private:

//用于狀态欄的資訊顯示
//    QLabel  *LabCurFile;  //目前檔案
    QLabel  *LabCellPos;    //目前單元格行列号
    QLabel  *LabCellText;   //目前單元格内容

    QWIntSpinDelegate    intSpinDelegate; //整型數
    QWFloatSpinDelegate  floatSpinDelegate; //浮點數
    QWComboBoxDelegate   comboBoxDelegate; //清單選擇

//    QString fCurFile;//目前檔案名

    QStandardItemModel  *theModel;//資料模型
    QItemSelectionModel *theSelection;//Item選擇模型

//    void    iniModelFromStringList(QStringList&);//從StringList初始化資料模型

    void    resetTable(int aRowCount);  //表格複位,設定行數
    bool    saveDataAsStream(QString& aFileName);//将資料儲存為資料流檔案
    bool    openDataAsStream(QString& aFileName);//讀取資料流檔案

    bool    saveBinaryFile(QString& aFileName);//儲存為二進制檔案
    bool    openBinaryFile(QString& aFileName);//打開二進制檔案

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void on_currentChanged(const QModelIndex &current, const QModelIndex &previous);

    void on_actOpen_triggered(); //打開檔案

    void on_actAppend_triggered(); //添加行

    void on_actInsert_triggered();//插入行

    void on_actDelete_triggered();//删除行

    void on_actSave_triggered();//儲存檔案

    void on_actAlignCenter_triggered(); //居中對齊

    void on_actFontBold_triggered(bool checked); //字型粗體設定

    void on_actAlignLeft_triggered(); //左對齊

    void on_actAlignRight_triggered(); //右對齊

    void on_actTabReset_triggered(); //表格複位

    void on_actSaveBin_triggered(); //儲存為自編碼二進制檔案

    void on_actOpenBin_triggered(); //打開自編碼二進制檔案

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H
           

mainwindow.cpp

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

#include    <QFileDialog>
#include    <QDataStream>
#include    <QMessageBox>
//#include    "qw.h"

void MainWindow::resetTable(int aRowCount)
{ //表格複位,先删除所有行,再設定新的行數,表頭不變
//    QStringList     headerList;
//    headerList<<"測深(m)"<<"垂深(m)"<<"方位(°)"<<"總位移(m)"<<"固井品質"<<"測井取樣";
//    theModel->setHorizontalHeaderLabels(headerList); //設定表頭文字

    theModel->removeRows(0,theModel->rowCount()); //删除所有行
    theModel->setRowCount(aRowCount);//設定新的行數

//    QStandardItem   *aItem;
//    QModelIndex     index;
    QString str=theModel->headerData(theModel->columnCount()-1,
                     Qt::Horizontal,Qt::DisplayRole).toString();

    for (int i=0;i<theModel->rowCount();i++)
    { //設定最後一列
        QModelIndex index=theModel->index(i,FixedColumnCount-1); //擷取模型索引
        QStandardItem* aItem=theModel->itemFromIndex(index); //擷取item
        aItem->setCheckable(true);
        aItem->setData(str,Qt::DisplayRole);
        aItem->setEditable(false); //不可編輯
    }
}

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預定義編碼方式
    }


//擷取資料區的資料
//    qint16  ceShen;
//    qreal  chuiShen;
//    qreal  fangWei;
//    qreal  weiYi;
//    QString  zhiLiang;
//    bool    quYang;
//    QStandardItem   *aItem;
    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;
}

bool MainWindow::saveBinaryFile(QString &aFileName)
{ //儲存為純二進制檔案
    QFile aFile(aFileName);  //以檔案方式讀出
    if (!(aFile.open(QIODevice::WriteOnly)))
        return false;

    QDataStream aStream(&aFile); //用文本流讀取檔案
//    aStream.setVersion(QDataStream::Qt_5_9); //無需設定資料流的版本
    aStream.setByteOrder(QDataStream::LittleEndian);//windows平台
//    aStream.setByteOrder(QDataStream::BigEndian);//QDataStream::LittleEndian

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

    aStream.writeRawData((char *)&rowCount,sizeof(qint16)); //寫入檔案流
    aStream.writeRawData((char *)&colCount,sizeof(qint16));//寫入檔案流


//擷取表頭文字
//    QString str;
    QByteArray  btArray;
    QStandardItem   *aItem;
    for (int i=0;i<theModel->columnCount();i++)
    {
        aItem=theModel->horizontalHeaderItem(i); //擷取表頭item
        QString str=aItem->text(); //擷取表頭文字
        btArray=str.toUtf8(); //轉換為字元數組
        aStream.writeBytes(btArray,btArray.length()); //寫入檔案流,長度uint型,然後是字元串内容
    }

//擷取資料區文字,
//    qint16  ceShen;
//    qreal  chuiShen;
//    qreal  fangWei;
//    qreal  weiYi;
//    QString  zhiLiang;
//    bool    quYang;
    qint8   yes=1,no=0; //分别代表邏輯值 true和false
    for (int i=0;i<theModel->rowCount();i++)
    {
        aItem=theModel->item(i,0); //測深
        qint16 ceShen=aItem->data(Qt::DisplayRole).toInt();//qint16類型
        aStream.writeRawData((char *)&ceShen,sizeof(qint16));//寫入檔案流

        aItem=theModel->item(i,1); //垂深
        qreal chuiShen=aItem->data(Qt::DisplayRole).toFloat();//qreal 類型
        aStream.writeRawData((char *)&chuiShen,sizeof(qreal));//寫入檔案流

        aItem=theModel->item(i,2); //方位
        qreal fangWei=aItem->data(Qt::DisplayRole).toFloat();
        aStream.writeRawData((char *)&fangWei,sizeof(qreal));

        aItem=theModel->item(i,3); //位移
        qreal weiYi=aItem->data(Qt::DisplayRole).toFloat();
        aStream.writeRawData((char *)&weiYi,sizeof(qreal));

        aItem=theModel->item(i,4); //固井品質
        QString zhiLiang=aItem->data(Qt::DisplayRole).toString();
        btArray=zhiLiang.toUtf8();
        aStream.writeBytes(btArray,btArray.length()); //寫入長度,uint,然後是字元串
//        aStream.writeRawData(btArray,btArray.length());//對于字元串,應使用writeBytes()函數

        aItem=theModel->item(i,5); //測井取樣
        bool quYang=(aItem->checkState()==Qt::Checked); //true or false
        if (quYang)
            aStream.writeRawData((char *)&yes,sizeof(qint8));
        else
            aStream.writeRawData((char *)&no,sizeof(qint8));
    }

    aFile.close();
    return true;
}

bool MainWindow::openBinaryFile(QString &aFileName)
{//打開二進制檔案
    QFile aFile(aFileName);  //以檔案方式讀出
    if (!(aFile.open(QIODevice::ReadOnly)))
        return false;

    QDataStream aStream(&aFile); //用文本流讀取檔案
//    aStream.setVersion(QDataStream::Qt_5_9); //設定資料流的版本
    aStream.setByteOrder(QDataStream::LittleEndian);
//    aStream.setByteOrder(QDataStream::BigEndian);

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

    this->resetTable(rowCount);


    //擷取表頭文字,但是并不利用
//    QByteArray  btArray;
    char *buf;
    uint strLen;  //也就是 quint32
//    QString str;
    for (int i=0;i<colCount;i++)
    {
        aStream.readBytes(buf,strLen);//同時讀取字元串長度,和字元串内容
        QString str=QString::fromLocal8Bit(buf,strLen); //可處理漢字
//        btArray.setRawData(buf,strLen);
//        str=QString::fromLatin1(btArray);
    }

//擷取資料區資料
    QStandardItem   *aItem;

    qint16  ceShen;
    qreal  chuiShen;
    qreal  fangWei;
    qreal  weiYi;
    QString  zhiLiang;
    qint8   quYang; //分别代表邏輯值 true和false
    QModelIndex index;

    for (int i=0;i<rowCount;i++)
    {
        aStream.readRawData((char *)&ceShen, sizeof(qint16)); //測深
        index=theModel->index(i,0);
        aItem=theModel->itemFromIndex(index);
        aItem->setData(ceShen,Qt::DisplayRole);

        aStream.readRawData((char *)&chuiShen, sizeof(qreal)); //垂深
        index=theModel->index(i,1);
        aItem=theModel->itemFromIndex(index);
        aItem->setData(chuiShen,Qt::DisplayRole);

        aStream.readRawData((char *)&fangWei, sizeof(qreal)); //方位
        index=theModel->index(i,2);
        aItem=theModel->itemFromIndex(index);
        aItem->setData(fangWei,Qt::DisplayRole);

        aStream.readRawData((char *)&weiYi, sizeof(qreal)); //位移
        index=theModel->index(i,3);
        aItem=theModel->itemFromIndex(index);
        aItem->setData(weiYi,Qt::DisplayRole);

        aStream.readBytes(buf,strLen);//固井品質
        zhiLiang=QString::fromLocal8Bit(buf,strLen);
//        btArray.setRawData(buf,strLen);
//        zhiLiang=QString::fromLatin1(btArray);
        index=theModel->index(i,4);
        aItem=theModel->itemFromIndex(index);
        aItem->setData(zhiLiang,Qt::DisplayRole);

        aStream.readRawData((char *)&quYang, sizeof(qint8)); //測井取樣
        index=theModel->index(i,5);
        aItem=theModel->itemFromIndex(index);
        if (quYang==1)
            aItem->setCheckState(Qt::Checked);
        else
            aItem->setCheckState(Qt::Unchecked);
    }

    aFile.close();
    return true;
}


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

    theModel = new QStandardItemModel(5,FixedColumnCount,this); //建立資料模型
    QStringList     headerList;
    headerList<<"Depth"<<"Measured Depth"<<"Direction"<<"Offset"<<"Quality"<<"Sampled";
    theModel->setHorizontalHeaderLabels(headerList); //設定表頭文字


    theSelection = new QItemSelectionModel(theModel);//Item選擇模型
    connect(theSelection,SIGNAL(currentChanged(QModelIndex,QModelIndex)),
            this,SLOT(on_currentChanged(QModelIndex,QModelIndex)));

    //為tableView設定資料模型
    ui->tableView->setModel(theModel); //設定資料模型
    ui->tableView->setSelectionModel(theSelection);//設定選擇模型

//為各列設定自定義代理元件
    ui->tableView->setItemDelegateForColumn(0,&intSpinDelegate);  //測深,整數
    ui->tableView->setItemDelegateForColumn(1,&floatSpinDelegate);  //浮點數
    ui->tableView->setItemDelegateForColumn(2,&floatSpinDelegate); //浮點數
    ui->tableView->setItemDelegateForColumn(3,&floatSpinDelegate); //浮點數
    ui->tableView->setItemDelegateForColumn(4,&comboBoxDelegate); //Combbox選擇型


    resetTable(5); //表格複位

    setCentralWidget(ui->tabWidget); //

//建立狀态欄元件
//    LabCurFile = new QLabel("目前檔案:",this);
//    LabCurFile->setMinimumWidth(300);

    LabCellPos = new QLabel("目前單元格:",this);
    LabCellPos->setMinimumWidth(180);
    LabCellPos->setAlignment(Qt::AlignHCenter);

    LabCellText = new QLabel("單元格内容:",this);
    LabCellText->setMinimumWidth(200);

//    ui->statusBar->addWidget(LabCurFile);
    ui->statusBar->addWidget(LabCellPos);
    ui->statusBar->addWidget(LabCellText);
//    ui->statusBar->addWidget(LabSelectionRange);
}

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

void MainWindow::on_currentChanged(const QModelIndex &current, const QModelIndex &previous)
{
    if (current.isValid())
    {
        LabCellPos->setText(QString::asprintf("目前單元格:%d行,%d列",
                                  current.row(),current.column()));
        QStandardItem   *aItem;
        aItem=theModel->itemFromIndex(current); //從模型索引獲得Item
        this->LabCellText->setText("單元格内容:"+aItem->text());

        QFont   font=aItem->font();
        ui->actFontBold->setChecked(font.bold());
    }
}

void MainWindow::on_actOpen_triggered()
{
    QString curPath=QDir::currentPath();
//調用打開檔案對話框打開一個檔案
    QString aFileName=QFileDialog::getOpenFileName(this,tr("打開一個檔案"),curPath,
                 "流資料檔案(*.stm)");

    if (aFileName.isEmpty())
        return; //

    if  (openDataAsStream(aFileName)) //儲存為流資料檔案
         QMessageBox::information(this,"提示消息","檔案已經打開!");
}

void MainWindow::on_actAppend_triggered()
{ //添加行
    QList<QStandardItem*>    aItemList; //容器類
    QStandardItem   *aItem;
    QString str;
    for(int i=0;i<FixedColumnCount-2;i++)
    {
        aItem=new QStandardItem("0"); //建立Item
        aItemList<<aItem;   //添加到容器
    }
    aItem=new QStandardItem("優"); //建立Item
    aItemList<<aItem;   //添加到容器

    str=theModel->headerData(theModel->columnCount()-1,Qt::Horizontal,Qt::DisplayRole).toString();
    aItem=new QStandardItem(str); //建立Item
    aItem->setCheckable(true);
    aItem->setEditable(false);
    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);
}

void MainWindow::on_actInsert_triggered()
{//插入行
    QList<QStandardItem*>    aItemList;  //QStandardItem的容器類
    QStandardItem   *aItem;
    QString str;
    for(int i=0;i<FixedColumnCount-2;i++)
    {
        aItem=new QStandardItem("0"); //建立一個QStandardItem
        aItemList<<aItem;//添加到容器類
    }
    aItem=new QStandardItem("優"); //建立一個QStandardItem
    aItemList<<aItem;//添加到容器類

    str=theModel->headerData(theModel->columnCount()-1,Qt::Horizontal,Qt::DisplayRole).toString();
    aItem=new QStandardItem(str); //建立Item
    aItem->setCheckable(true);
    aItem->setEditable(false);
    aItemList<<aItem;//添加到容器類
//    theModel->insertRow(ui->tableView->currentIndex().row(),aItemList);
    QModelIndex curIndex=theSelection->currentIndex();
    theModel->insertRow(curIndex.row(),aItemList);
    theSelection->clearSelection();
    theSelection->setCurrentIndex(curIndex,QItemSelectionModel::Select);
}

void MainWindow::on_actDelete_triggered()
{ //删除行
    QModelIndex curIndex=theSelection->currentIndex();
    if (curIndex.row()==theModel->rowCount()-1)//(curIndex.isValid())
        theModel->removeRow(curIndex.row());
    else
    {
        theModel->removeRow(curIndex.row());
        theSelection->setCurrentIndex(curIndex,QItemSelectionModel::Select);
    }
}


void MainWindow::on_actSave_triggered()
{ //以Qt預定義編碼儲存資料檔案
    QString curPath=QDir::currentPath();
    QString aFileName=QFileDialog::getSaveFileName(this,tr("選擇儲存檔案"),curPath,
                 "Qt預定義編碼資料檔案(*.stm)");

    if (aFileName.isEmpty())
        return; //

   if  (saveDataAsStream(aFileName)) //儲存為流資料檔案
       QMessageBox::information(this,"提示消息","檔案已經成功儲存!");
}

void MainWindow::on_actAlignCenter_triggered()
{
    if (!theSelection->hasSelection())
        return;

    QModelIndexList selectedIndix=theSelection->selectedIndexes();

    QModelIndex aIndex;
    QStandardItem   *aItem;

    for (int i=0;i<selectedIndix.count();i++)
    {
        aIndex=selectedIndix.at(i);
        aItem=theModel->itemFromIndex(aIndex);
        aItem->setTextAlignment(Qt::AlignHCenter);
    }
}

void MainWindow::on_actFontBold_triggered(bool checked)
{
    if (!theSelection->hasSelection())
        return;

    QModelIndexList selectedIndix=theSelection->selectedIndexes();

    QModelIndex aIndex;
    QStandardItem   *aItem;
    QFont   font;

    for (int i=0;i<selectedIndix.count();i++)
    {
        aIndex=selectedIndix.at(i);
        aItem=theModel->itemFromIndex(aIndex);
        font=aItem->font();
        font.setBold(checked);
        aItem->setFont(font);
    }

}

void MainWindow::on_actAlignLeft_triggered()
{
    if (!theSelection->hasSelection())
        return;

    QModelIndexList selectedIndix=theSelection->selectedIndexes();

    QModelIndex aIndex;
    QStandardItem   *aItem;

    for (int i=0;i<selectedIndix.count();i++)
    {
        aIndex=selectedIndix.at(i);
        aItem=theModel->itemFromIndex(aIndex);
        aItem->setTextAlignment(Qt::AlignLeft);
    }
}

void MainWindow::on_actAlignRight_triggered()
{
    if (!theSelection->hasSelection())
        return;

    QModelIndexList selectedIndix=theSelection->selectedIndexes();

    QModelIndex aIndex;
    QStandardItem   *aItem;

    for (int i=0;i<selectedIndix.count();i++)
    {
        aIndex=selectedIndix.at(i);
        aItem=theModel->itemFromIndex(aIndex);
        aItem->setTextAlignment(Qt::AlignRight);
    }
}

void MainWindow::on_actTabReset_triggered()
{//表格複位
    resetTable(10);
}

void MainWindow::on_actSaveBin_triggered()
{//儲存二進制檔案
    QString curPath=QDir::currentPath();
    //調用打開檔案對話框選擇一個檔案
    QString aFileName=QFileDialog::getSaveFileName(this,tr("選擇儲存檔案"),curPath,
                                                   "二進制資料檔案(*.dat)");
    if (aFileName.isEmpty())
        return; //

    if  (saveBinaryFile(aFileName)) //儲存為流資料檔案
        QMessageBox::information(this,"提示消息","檔案已經成功儲存!");
}

void MainWindow::on_actOpenBin_triggered()
{//打開二進制檔案
    QString curPath=QDir::currentPath();//系統目前目錄
    QString aFileName=QFileDialog::getOpenFileName(this,tr("打開一個檔案"),curPath,
                                                   "二進制資料檔案(*.dat)");
    if (aFileName.isEmpty())
        return; //

    if  (openBinaryFile(aFileName)) //儲存為流資料檔案
        QMessageBox::information(this,"提示消息","檔案已經打開!");
}
           

對于剩下的3個頭檔案,我會上傳項目上,需要的請留言。

繼續閱讀