1. 程式人生 > >個人向word文檔編輯器(一)

個人向word文檔編輯器(一)

嘗試 每一個 bsp flush ide nodelist ada 接口 filename

最近在空閑時想做一些東西,之前一直想做一個自己的日誌記錄工具, 然後需要增加文字、圖片,可以播放本地音樂等一些功能。

目前實現了文字、圖片的編輯和儲存(數據儲存,之後的打開可以不依賴本地圖片數據);

現在將一些代碼和思路想法記錄下,分享給需要的小夥伴們

1、目前的主要界面如下

技術分享圖片

上面是菜單欄,工具欄暫時還沒有添加。

下面是由一個QTableView和QTextEdit組成的主體部分。之後應該會添加一個音樂播放器的控件在下面

插入一段布局的代碼

setStretch是用來控制控件在布局內的拉伸大小的

另外這裏不能直接對MainWindow調用this.setLayout()

技術分享圖片
    hboxLayout->setStretch(0, 1);
    hboxLayout->setStretch(2, 2.5);
    //this->setLayout(hboxLayout);不能直接set,顯示不出來並且提示錯誤
    //Attempting to set QLayout "" on MainWindow "", which already has a layout

    QWidget *newWidget = new QWidget(this);
    newWidget->setLayout(hboxLayout);
    
this->setCentralWidget(newWidget);
View Code

二、文字和圖片數據的保存方式

最開始做的時候,以為將文字和圖片從QTextEdit通過toPlainText()接口讀出來然後保存到一個文件,顯示的時候直接readAll()然後丟給控件顯示就完了。

後來發現還是太天真。文字數據和圖片數據是兩種數據,並且QTextEdit是按照富文本格式去處理數據的,通過QTextDocument 的toHtml獲取到控件的數據,發現是按照HTML格式編輯的,其中圖片已這樣的格式保存

<img width="400" height="274" src="C:/Users/Default/AppData/myAppImg/11/IMG_000000011.jpg"/>

也就是顯示的時候通過src路徑去加載,如果刪除本地的圖片就顯示不了。

所以定下的方案是,將輸出數據和其中加載的圖片數據組合,保存到自定義數據中,在打開的時候根據規則進行解析,還原成HTML格式文字數據和對應的圖片數據。

首先對輸出的控件數據中圖片路徑修改為指向定義的“處理空間”路徑,這個處理空間,就是在打開文件時生成的圖片備份文件和文字備份文件,這樣在控件加載文字數據時,會加載備份的圖片,就可以顯示了。

對文字數據和圖片數據,進行了格式編碼。以下是自己定的格式說明 ^-^

數據項

格式定義

長度

TAG值

數據格式

說明

文件頭

文件頭

4

B

標識一個自定義的文件類型。

clog

文件長度

4

B

整個數據條目的長度

0x00 0x00 0x00 0x24

文字編碼

文件保存路徑

1

0xC1

B

標識文件數據解壓後的文件路徑

文件數據

1

0XC2

B

文件的數據流數據

數據編碼

圖片保存路徑

1

0xD1

B

圖片格式

jpg、png、bmp

圖片數據

1

0xD2

B

Base64格式圖片數據

備用

1

0xE1

B

其所有數據都按照TLV(標簽-長度-值)據組合,其中長度域有單獨設置。

(1)對原文件數據的修改,主要是圖片的路徑修改

技術分享圖片
void MainWindow::saveFile(const QString &ht, int row)
{
    QTextDocument *textDocument = m_textEdit->document();
    QDomDocument dcreateDoc;
    if(!ht.isEmpty()){
        if(!dcreateDoc.setContent(ht)){
            qDebug() << "str html set Content error";
        }
    }else{
        if(!dcreateDoc.setContent(textDocument->toHtml())){
            qDebug() << "document html set Content error";
        }
    }

    //這個地方有想過直接修改獲取QString字符串,但是我修改之後嘗試獲取新的字符串是沒有被改過的,所以這裏用了一個文件來保存在讀取
    //最外層節點
    QDomElement roots = dcreateDoc.documentElement();
    QDomNodeList list = roots.childNodes();
    //analyImg會更改圖片的路徑指向備份路徑
    QByteArray imgBuff = analyImg(list);

    QString path = qApp->applicationDirPath() + "/copy.htm";
    QFile file(path);
    if(!file.open(QIODevice::ReadWrite)){
        qDebug() << "open copy file error";
    }
    QTextStream out(&file);
    dcreateDoc.save(out,4);
    file.close();//調用file.flush()不能刷新

    file.open(QIODevice::ReadOnly);
    QByteArray html = file.readAll();
    file.close();
    file.remove();

    //組文件和圖片數據
    QString fil;
    if(row != tag){
        fil = m_fileHand->getFileName(row);
    }else{
        fil = m_fileHand->getFileName(tag);
    }

    fil = fil.mid(0, fil.indexOf("."));
    QString newFileName = BACKSAVE_PATH + tr("/%1/%2").arg(fil).arg(m_fileHand->getFileName(tag));
    QByteArray all,all1;
    //增加文件標識域 "clog"
    all.append(tr("clog").toLatin1());
    //儲存數據域
    all1.append(tlvByte(0xC1, newFileName.toLatin1()));
    all1.append(tlvByte(0xC2, html));
    all1.append(imgBuff);
    //增加數據長度域
    QString len = QString("%1").arg(all1.length(), 8, 16, QChar(0));
    all.append(QByteArray::fromHex(len.toLatin1()));
    all.append(all1);
    //串->clog+數據長度域+數據域
    if(!m_fileHand->save(all, row)){
        QMessageBox::warning(NULL, "warning",
                             "save file falt.", QMessageBox::Ok);
    }
}

QByteArray MainWindow::analyImg(QDomNodeList &list)
{
    //要保證傳入的list不為空
    if(list.isEmpty()){
        return 0;
    }
    QString imgpath;
    QByteArray img;//儲存所有的圖片的信息
    for(int i = 0;i < list.length(); i++){
        if(list.at(i).toElement().tagName() == "img"){
            //獲取src指向的圖片路徑
            imgpath = list.at(i).toElement().attributeNode("src").value();
            QFileInfo file(imgpath);
            QString path = m_fileHand->getFileName(tag);
            path = path.mid(0, path.indexOf("."));
            QString newFile = BACKSAVE_PATH +tr("/%1/%2").arg(path).arg(file.fileName());

            //替換圖片路徑
            list.at(i).toElement().setAttribute("src",newFile);
            //qDebug() << "替換之後路徑:" <<  list.at(i).toElement().attributeNode("src").value();
            //tlvByte是用來組tlv格式的數據處理函數
            img.append(tlvByte(0xD1,newFile.toLatin1()));
            //用readAll讀到的圖片數據
            QByteArray imgByte = imgDataByte(imgpath);
            img.append(tlvByte(0xD2,imgByte));
        }
        QDomNodeList it = list.at(i).childNodes();
        if(it.length() >= 1){
            //回調,一層一層處理完所有圖片數據
            img += analyImg(it);
        }else{
            continue;
        }
    }
    return img;
}    
View Code

(2)最後獲得的數據串,需要經過壓縮在進行寫入,本次用到的是Qt自帶的Zlib庫

(3)通過readAll讀出文件數據,需要解出各標簽數據在寫入各個文件。

技術分享圖片
QByteArray CFileHandle::analyByte(const QByteArray &byte)
{
    if(byte.isEmpty()){
        return "";
    }
    QByteArray analy;
    QString str;
    analy = byte.mid(0,4);
    if(!("clog" == str.prepend(analy))){
        qDebug() << "it‘s not app Standard file";
        return "";
    }

    analy = byte.mid(4,4);

    int byteLen = analy.toHex().toInt(NULL, 16);
    analy = byte.mid(8);//取所有文件數據
    //長度不一樣也進行解析
    if(analy.length() != byteLen)
        qDebug() << "file byte len error";

    QString filepath = analyTLV(analy);//返回文本文件路徑

    QFile file(filepath);
    if(file.exists()){
        if(file.open(QIODevice::ReadOnly)){
            analy = file.readAll();
        }
        file.close();
    }

    return analy;
}

//解出文件路徑返回,並將所有標簽數據解出寫入對應文件中
QString CFileHandle::analyTLV(const QByteArray &tlvByte)
{
    QString file;
    QString str;
    QList<QString> listStr;
    QList<QByteArray> listByte;

    QByteArray byteT;
    QByteArray byteL;
    QByteArray byteV;
    QByteArray Llen;
    int len = 0;
    int length = 0;

    for(int i = 0; i < tlvByte.length(); ){
        //T
        byteT = tlvByte.mid(i,1);
        i += 1;

        byteL = tlvByte.mid(i, 1);
        //如果L的最高位為1,則剩下的位值表示長度
        if((byteL.at(0) & 0x80) == 0x80){
            len = byteL.at(0) & 0x7f;
            i += 1;
            Llen = tlvByte.mid(i, len);
            //數據長度
            length = Llen.toHex().toInt(NULL, 16);
            i += len;
        }else{
            length = byteL.at(0) & 0xff;
            i += 1;

        }

        //V 數據
        byteV = tlvByte.mid(i, length);//數據
        str = "";

        i += length;//循環i不用自增

        //每一個C1 D1後都跟著C2 D2,確保數據連貫,則兩個list可以對應下標
        if((byteT.at(0) & 0xff) == 0xC1){
            file = file.prepend(byteV);
            listStr.append(str.prepend(byteV));
        }else if((byteT.at(0) & 0xff) == 0xD1){
            listStr.append(str.prepend(byteV));
        }else if((byteT.at(0) & 0xff) == 0xC2
                 || (byteT.at(0) & 0xff) == 0xD2){
            listByte.append(byteV);
        }else
            qDebug() << "analy tlv error, byteT is:" << byteT.toHex()
                     << " byteV is:" << byteV;
    }

    //將所有文件創建
    QString filepath;
    for(int j = 0; j < listStr.length(); j++){
        filepath = listStr.at(j);       
        //創建文件名文件夾,防止同名文件導致的加載錯誤
        QString fp = filepath.mid(0, filepath.lastIndexOf("/"));
        QFileInfo fl(fp);
        if(!fl.exists()){
            QDir dir;
            dir.mkpath(fp);
        }

        QFile file(filepath);
        if(!file.exists()){//文件存在不創建
            //qDebug() << tr("j is %1").arg(j) << " filepath:" << filepath;
            if(file.open(QIODevice::WriteOnly)){
                file.write(listByte.at(j));
                file.close();
            }else{
                qDebug() << "create file error, file path:" <<
                            filepath;
            }
        }
    }

    if(file.isEmpty()){
        qDebug() << "dont‘t have file path,please check!";
        return "";
    }

    return file;
}
View Code

今天寫到這裏,之後在繼續補充。

個人向word文檔編輯器(一)