最近在空闲时想做一些东西,之前一直想做一个自己的日志记录工具, 然后需要增加文字、图片,可以播放本地音乐等一些功能。
目前实现了文字、图片的编辑和储存(数据储存,之后的打开可以不依赖本地图片数据);
现在将一些代码和思路想法记录下,分享给需要的小伙伴们
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);
二、文字和图片数据的保存方式
最开始做的时候,以为将文字和图片从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; }
(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; }
今天写到这里,之后在继续补充。
原文:https://www.cnblogs.com/warmSnowFY/p/10797172.html