個人向word文檔編輯器(一)
最近在空閑時想做一些東西,之前一直想做一個自己的日誌記錄工具, 然後需要增加文字、圖片,可以播放本地音樂等一些功能。
目前實現了文字、圖片的編輯和儲存(數據儲存,之後的打開可以不依賴本地圖片數據);
現在將一些代碼和思路想法記錄下,分享給需要的小夥伴們
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);View Codethis->setCentralWidget(newWidget);
二、文字和圖片數據的保存方式
最開始做的時候,以為將文字和圖片從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文檔編輯器(一)