I am saving the QAbstract tree model to a dat file
void saveTreeStructureToFile(const QModelIndexList &indexes , const std::string stdstrFilePath)
{
QMimeData *mimeData = new QMimeData;
QByteArray data; //a kind of RAW format for datas
QDataStream stream(&data, QIODevice::WriteOnly);
QList<TreeItem *> nodes;
foreach(const QModelIndex &index, indexes) {
TreeItem *node = getItem(index);
if (!nodes.contains(node))
nodes << node;
}
stream << nodes.count();
foreach(TreeItem *node, nodes) {
buildTree(node, stream);
}
mimeData->setData(s_treeNodeMimeType, data);
std::string st = stdstrFilePath.substr(0, stdstrFilePath.size() - 3);
st.append("dat");
const QString path = st.c_str();
QFile file(path);
file.open(QIODevice::WriteOnly);
QDataStream out(&file);
out << *mimeData;
}
How can i prepend the size of total bytes to be written to the start of dat file.
You can retrieve the size of the data and append it to your data stream at the beginning, like:
out << data.size() << *mimeData;
Related
I have been trying to serialize my tree structure and restore it back with not much of success.
Mimedata function is able to store the tree structure in the correct hierarchy.
How can i save the mime data to a file and load it back to QDataStream ?
QMimeData *TreeModel::mimeData(const QModelIndexList &indexes) const
{
QMimeData *mimeData = new QMimeData;
QByteArray data;
QDataStream stream(&data, QIODevice::WriteOnly);
QList<TreeItem *> nodes;
foreach(const QModelIndex &index, indexes) {
TreeItem *node = getItem(index);
if (!nodes.contains(node))
nodes << node;
}
stream << QCoreApplication::applicationPid();
stream << nodes.count();
foreach(TreeItem *node, nodes) {
buildTree(node, stream);
}
mimeData->setData(s_treeNodeMimeType, data);
return mimeData;
}
You must implement the stream operators between QDataStream and QMimeData saving the formats and data associated with each format and the number of formats.
#include <QtWidgets>
QDataStream &operator<<(QDataStream &stream, const QMimeData &data)
{
const QStringList formats = data.formats();
stream << static_cast<qint32>(formats.size());
for(const QString& format : formats)
stream << format << data.data(format);
return stream;
}
QDataStream &operator>>(QDataStream &stream, QMimeData &data)
{
data.clear();
qint32 size;
QString format;
QByteArray formatData;
stream >> size;
while(size-->0){
stream >> format >> formatData;
data.setData(format,formatData);
}
return stream;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
const QString text = "Text";
const QString html = R"(<!DOCTYPE html>
<html>
<body>
<h1>My First Heading</h1>
<p>My first paragraph.</p>
</body>
</html>
)";
const QList<QUrl> urls = {
{"https://stackoverflow.com/questions/58073079/how-to-save-mimedata-to-a-file"},
{"https://doc.qt.io/qt-5/"}
};
const QString mimeType = "custom/custom-app";
const QByteArray data("stackoverflow");
const QString path = "datastream.dat";
{
QMimeData mimedata;
mimedata.setText(text);
mimedata.setHtml(html);
mimedata.setUrls(urls);
mimedata.setData(mimeType, data);
QFile file(path);
file.open(QIODevice::WriteOnly);
QDataStream out(&file);
out << mimedata;
}
{
QMimeData mimedata;
QFile file(path);
file.open(QIODevice::ReadOnly);
QDataStream out(&file);
out >> mimedata;
Q_ASSERT(mimedata.text() == text);
Q_ASSERT(mimedata.html() == html);
Q_ASSERT(mimedata.urls() == urls);
Q_ASSERT(mimedata.data(mimeType) == data);
}
return 0;
}
I'm trying to upload some files to a server by using QHttpMultiPart. The following version file is just an example. For some reason, Qt will automatically add boundary into the files. However, what I truly uploaded are .tar.gz files and they will be considered damaged if such boundary were added.
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QNetworkAccessManager *netManager = new QNetworkAccessManager();
HTTPFirmwareUpgrader upgrader(netManager,"http://mytest.com");
upgrader.upgradeCIU();
return app.exec();
}
void HTTPFirmwareUpgrader::upgradeCIU()
{
QString filename = "version";
QString filePath = QString("C:/Users/User/Desktop/HTTP/%1").arg(filename);
qDebug() << uploadFirmware(filename, filePath);
}
bool HTTPFirmwareUpgrader::uploadFirmware(const QString &filename, const QString &filePath)
{
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart myPart;
QFile *file = new QFile(filePath);
if(!file->exists())
qWarning() << "File DOES NOT exists";
file->open(QIODevice::ReadOnly);
myPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("form-data"));
myPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"version\"; filename=\"version\""));
myPart.setBodyDevice(file);
file->setParent(multiPart);
multiPart->append(ciu);
QByteArray resp = this->post(createUploadRequest(QString("filename=%1").arg(filename)),
multiPart, file);
qDebug() << "Upload Filrmware " << resp;
return resp != "";
}
QByteArray HTTPFirmwareUpgrader::post(QUrl url, QHttpMultiPart *multiPart, QFile *file)
{
QNetworkRequest request;
QEventLoop loop;
request.setUrl(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("multipart/form-data"));
QNetworkReply *reply = m_manager->post(request, multiPart);
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
multiPart->setParent(reply);
reply->deleteLater();
return reply->readAll();
}
This is the original "version" file:
enter image description here
And this is the "version" file that transferred:
enter image description here
And if I do not set header for the request, the size of the transferred file will be 0.
Does anyone have any idea what should I do?
Problem solved!!!
Turns out I use the wrong way to upload the file. What I need to use is QIODevice instead of QMultipart.
Recently I programmed to do file transmission with Qt. Thought it worked now, I'm still curious about what happened. Please help me find out the reason. Many thanks.
Why the size of head is bigger than the sum of sizeof(qin32), sizeof(qint32) and length of file name?(I guess it is the reason of function - setVersion())
QFileInfo info(file_to_send.fileName());
QByteArray head;
QDataStream out(&head, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_0);
out << qint32(file_to_send.size() + info.fileName().length() + sizeof(qint32)*2)
<< qint32(info.fileName().length())
<< info.fileName();
tcpClient.write(head);
You have made it to complicated. Pattern is like that:
QFileInfo info(file_to_send.fileName());
QByteArray head;
QDataStream out(&head, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_0);
out << qint32(0)
<< info.fileName(); // << YouCanAddMoreStuffHere;
out.device()->seek(0);
out << qint32(out.length());
tcpClient.write(head);
and read code:
void SomeClass::slotReadClient() { // slot connected to readyRead signal of QTcpSocket
QTcpSocket *tcpSocket = (QTcpSocket*)sender();
QDataStream clientReadStream(tcpSocket);
while(true) {
if (!next_block_size) {
if (tcpSocket->bytesAvailable() < sizeof(qint32)) { // are size data available
break;
}
clientReadStream >> next_block_size;
}
if (tcpSocket->bytesAvailable() < next_block_size) {
break;
}
QString fileName;
clientReadStream >> fileName; // >> YouCanAddMoreStuffHere; // same as above
next_block_size = 0;
}
}
info.filename() writes out its own length
if you don't want that then you can do
QFileInfo info(file_to_send.fileName());
QByteArray head;
QDataStream out(&head, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_0);
QByteArray filename = info.fileName().toLatin1();
out << qint32(file_to_send.size() + filename .length() + sizeof(qint32)*2);
<< qint32(filename .length())
out.writeRawData(fileName.constData(), filename.length());
tcpClient.write(head);
using writeRawData which bypasses any built in encoding
is there any way to store exact cv::Mat format data in sqlite3 using Qt.. as i will be using the same cv::Mat format in future..
i tried converting image to unsigned char* and them storing it.. but this didn't worked for me.. any other technique ??
You can serialize cv::Mat to QByteArray (see kde/libkface):
QByteArray mat2ByteArray(const cv::Mat &image)
{
QByteArray byteArray;
QDataStream stream( &byteArray, QIODevice::WriteOnly );
stream << image.type();
stream << image.rows;
stream << image.cols;
const size_t data_size = image.cols * image.rows * image.elemSize();
QByteArray data = QByteArray::fromRawData( (const char*)image.ptr(), data_size );
stream << data;
return byteArray;
}
Then store to DB.
To convert from QByteArray after reading from DB:
cv::Mat byteArray2Mat(const QByteArray & byteArray)
{
QDataStream stream(byteArray);
int matType, rows, cols;
QByteArray data;
stream >> matType;
stream >> rows;
stream >> cols;
stream >> data;
cv::Mat mat( rows, cols, matType, (void*)data.data() );
return mat.clone();
}
It works for me.
I have a QList list. I want to insert it on the database. I didn't find any serializer method after some googling. If there any method / idea to serialize the list data for database?
How about using QStringList instead of QList<QString> -
QStringList numberList_; // instead of QList<QString>, use this
QString myString1 = "Hello";
QString myString2 = "World";
numberList_ << myString1;
numberList_ << myString2;
QByteArray byteArray;
QBuffer buffer(&byteArray);
QDataStream out(&buffer);
out << numberList_;
Probably QList<QString> should also work in place of QStringList. If it doesn't, well, you can convert it pretty easily to QStringList.
QDataStream, QBuffer,
QByteArray and QStringList reference.
Here is another option that is a bit more succinct:
QString serialize(QStringList stringList)
{
QByteArray byteArray;
QDataStream out(&byteArray, QIODevice::WriteOnly);
out << stringList;
return QString(byteArray.toBase64());
}
QStringList deserialize(QString serializedStringList)
{
QStringList result;
QByteArray byteArray = QByteArray::fromBase64(serializedStringList.toUtf8());
QDataStream in(&byteArray, QIODevice::ReadOnly);
in >> result;
return result;
}