How to split qbyteArray containing jpg images or iterate about it - c++

I am working on an app with c++,Qt and Qt Creator where amongst others the user can choose multiple images (.jpg files), display it and store it in a sqlite database as blob
QStringList pfad = QFileDialog::getOpenFileNames(this, tr("Bilddateien auswählen"),
QDir::homePath(), tr("Bilddateien(*.png *.jpg *.bmp"));
QByteArray Foto::foto_daten(const QStringList dateiPfad)
{
//QStringList const dateiPfad = foto_get();
QByteArray fotoDaten;
for (int i = 0; i < dateiPfad.size(); i++)
{
QString dateiName = dateiPfad[i];
QFile file(dateiName);
file.open(QIODevice::ReadOnly);
QByteArray byteArray = file.readAll();
fotoDaten.append(byteArray + '$');
}
return fotoDaten;
}
(Here "pfad" is the variable passed to method "foto_daten" as argument;
return value is used for storage in the database as blob datatype and I added '$' as a separator)
So long no problem.
I can also go the opposite way: Get the blob value out of the database via Select statemant, convert it to a QByteArray object and display it to a TableView.
QByteArray fotoByte;
fotoByte = query_combo.value("kon_fotoDaten").toByteArray();
if (!fotoByte.isNull())
{
QPixmap outPixmap = QPixmap();
outPixmap.loadFromData( fotoByte );
int w = ui->labelFotoZeigen->width();
int h = 300;
ui->labelFotoZeigen->setPixmap(outPixmap);
ui->labelFotoZeigen->setPixmap(outPixmap.scaled(w, h, Qt::KeepAspectRatio));
}
My problem is:
If I stored maybe 3 images as one blob in the database (via append method to a QByteArray object)
I get only the first image back of the stored three to display.
I proved if really three images are stored via size() method and yes I get different sizes storing only one or three images.
I tried QList split method but no success, I got tens of splits. I tried also several seperators, the same.
Is there a possibility to get each single image back? Split with a special seperator or a iterate method on QByteArray object?

The separator is ambiguous, as it is not guaranteed to not be present in the data. I suggest you use QDataStream to serialize QByteArray's. You can serialize and deserialize without ambiguity most Qt types, including QByteArray's.

Related

How do I properly store raw byte data in XML and recover it?

Using C++ and Qt, I need to store some raw byte data (an unsigned char array) in a QDomElement (XML node), and then recover it later so that I can compare it to the raw data that is written directly to a different binary file. During testing, I noticed my solution works ~85% of the time, but comparing the recovered data and the raw data read from file seems to fail occasionally. The code snippets below illustrate the Qt methods I am currently using. I have very little knowledge of different character encodings and what I need to look out for in that regard, so I am assuming my mistake has something to do with that.
Storing the raw data in XML:
QDomElement myElement;
unsigned char rawData[ DATA_LEN ];
foo( rawData ); // upon return, rawData now contains the data I want to store in XML
QByteArray dataByteArray( reinterpret_cast< char * >( rawData ) );
QString dataStr( dataByteArray.toBase64() );
QByteArray excluded = " /():|+,.=[]_^{}";
myElement.setAttribute( "Data", QUrl::toPercentEncoding( dataStr, excluded ) );
Recovering the data from XML and comparing to raw data read from binary file (the memcmp() occasionally fails):
unsigned char recoveredData[ DATA_LEN ];
QString dataStr = QUrl::fromPercentEncoding( stringFromXmlNode.toUtf8() );
QByteArray dataByteArray = QByteArray::fromBase64( dataStr.toAscii() );
memcpy( recoveredData, reinterpret_cast< unsigned char * >( dataByteArray.data() ), DATA_LEN );
unsigned char dataFromFile[ DATA_LEN ];
fread( dataFromFile, 1, DATA_LEN, filePtr );
if( 0 != memcmp( dataFromFile, recoveredData, DATA_LEN ) )
{
return false;
}
I am restricted to Qt 4.8, so please refrain from any Qt5-specific solutions if possible, thanks!
You state the bytes are random, so they can contain 0 bytes. Byte value 0 is string terminator in C-style strings. This line in your code initializes QByteArray from such string:
QByteArray dataByteArray( reinterpret_cast< char * >( rawData ) );
Solution is to also pass length of rawData and use this constructor.
You want to use an XML CDATA section.
Look at QDomCDATASection
https://doc.qt.io/archives/qt-4.8/qdomcdatasection.html
Why don't you use QDataStream, which can define the byte order (important if exchanging data across different platforms) and the versioning?
From the Qt 4.8 documentation page:
A data stream cooperates closely with a QIODevice. A QIODevice represents an input/output medium one can read data from and write data to. The QFile class is an example of an I/O device.
Example (write binary data to a stream):
QFile file("file.dat");
file.open(QIODevice::WriteOnly);
QDataStream out(&file); // we will serialize the data into the file
out << QString("the answer is"); // serialize a string
out << (qint32)42; // serialize an integer
Example (read binary data from a stream):
QFile file("file.dat");
file.open(QIODevice::ReadOnly);
QDataStream in(&file); // read the data serialized from the file
QString str;
qint32 a;
in >> str >> a; // extract "the answer is" and 42
Each item written to the stream is written in a predefined binary
format that varies depending on the item's type. Supported Qt types
include QBrush, QColor, QDateTime, QFont, QPixmap, QString, QVariant
and many others. For the complete list of all Qt types supporting data
streaming see Serializing Qt Data Types.
You can read/write your XML Data with QDataStream and import them in the QDomDocument structure with the QDomDocument function setContent() and toByteArray().

QJsonDocument::fromRawData(const char *data, int size) data has to be aligned to a 4 byte boundary

void test()
{
QFile f("..\\data\\NAVHistory2.txt");
if (!f.open(QFile::ReadOnly))
{
return;
}
QByteArray data = f.readAll();
int iLeft = data.indexOf('[');
int iRight = data.lastIndexOf(']');
QJsonDocument::fromRawData(data.data() + iLeft, iRight - iLeft + 1);// got error
}
I want to cut a part of QByteArray and send it to a QJsonDocument. The simplest way is to use QByteArray::mid and create a new copy of QByteArray. And QJsonDocument::fromJson(QByteArray) works well.
However, it only needs to cut a small part of data away. So to create a new QBytedata would lost performance. There is a better way QJsonDocument::fromRawData(char*). But I got an error:
QJsonDocument::fromRawData: data has to have 4 byte alignment
I looked up the Qt document for this. It says data has to be aligned to a 4 byte boundary.
Qt source
My application is a x64 project, so the char* is a 8-byte boundary. How do I get through it?
I see two options:
Just take the copy. Quick and easy.
If you don't need anything else from data, just use data.remove(0, iLeft) to make your JSON snippet start at the beginning of the QByteArray (which will be aligned to at least 4 bytes).
According to the Qt document:
It assumes data contains a binary encoded JSON document.
The data should be a binary encoded. My document is just a normal text. So it doesn't work. I didn't noticed that before.
It seems I have to use QJsonDocument::fromJson.

Comparing international strings

What I am trying to do is comparin 2 QStrings that have special characters (French)
first I recieved from server as json data saved in txtInfo
txtInfo = "Présenter";
When I am having condition like this it's not gonna work(its not gonna set state.)
if (txtInfo == "Présenter"){
m_appState = 8;
m_appStateString = AppStatesArray[m_appState];
}
else {
m_appState = -1;
m_appStateString = "UNKNOWN";
}
What I am missing? What if I would like to compare not French but Chinese?
Thank you very much
Since Qt 5 QString's operator== performs fromUtf8 conversion on the character array being compared to it. But if your source file (.cpp) isn't using utf8 you need to build your own QString.
Depending on your source file's (.cpp) encoding:
Utf8:
QString compared = QString::fromUtf8("Présenter");
if (txtInfo == QString::fromUtf8("Présenter")){
local 8-bit:
QString compared = QString::fromLocal8Bit("Présenter");
if (txtInfo == QString::fromUtf8("Présenter")){
For 100% correctness, don't forget to normalize your strings:
txtInfo = txtInfo.normalized(QString::NormalizationForm_D);
QString compared = /* the correct form for you */;
if (txtInfo == compared.normalized(QString::NormalizationForm_D)){

QString numbers stay the same in serial communication

I have trouble with reading serial data from an Arduino, and sending it to the UI. I found it to be a hassle to split all serial data into 3 seperate variables, and therefore used the left and mid to get the specific numbers. The problem now is that the numbers I get stays the same, eventhough I can see on an LCD display they change.
This is a section of the code:
void Dolle::serialReceived(){
ba = serial->readAll();
serialBuffer += QString::fromStdString(ba.toStdString());
QString bufferSplit = serialBuffer;
QString hum = bufferSplit.left(2);
QString temp = bufferSplit.mid(2, 2);
QString gas = bufferSplit.mid(4, 4);
if((hum.size()==2) && (temp.size()==2) && (gas.size()==4)){
ui->humLabel->setText("Humidity: "+hum+" %");
ui->tempLabel->setText("Temperature: "+temp+(char(176))+ "C");
ui->gasLabel->setText("Gas level: "+gas);
qDebug() << hum << temp << gas;
}
}
There are several problems:
You're appending to serialBuffer, but you never empty it out. The QIODevice already maintains an internal, resizable circular buffer for you, so a second one is not necessary.
You're going from binary representation to a QString via a std::string. That is completely unnecessary.
You're not explicit about what character encoding is used in the binary data.
The UI is updated repeatedly, instead of at most once per readyRead signal.
You're using magic constants. You should UTF-8 encode your source instead, or use a named QChar constant.
You're manually building strings using string operators, this impedes internationalization and maintainability.
You're using spaces as a mechanism to align the display in your UI. Perhaps you should design your UI in a different manner, so that such hacks won't be necessary.
I presume that your strings are separated somehow - perhaps each is in a separate line? In any case, you should keep reading the complete delimited strings from the device as long as they are available. The QIODevice::readLine method makes it easy in case of line-delimited data:
void Dolle::serialReceived() {
QString validLine;
while (serial->canReadLine()) {
auto binLine = serial->readLine();
auto line = QString::fromLatin1(binLine);
if (line.length() < 8)
continue;
validLine = line;
}
if (validLine.isEmpty()) return;
auto hum = line.left(2);
auto temp = line.mid(2, 2);
auto gas = line.mid(4, 4);
ui->humLabel->setText(QStringLiteral("Humidity: %1%").arg(hum));
ui->tempLabel->setText(QStringLiteral("Temperature: %1°C").arg(temp));
ui->gasLabel->setText(QStringLiteral("Gas level: %1").arg(gas));
}
Suppose that instead of line-separated data, your data arrives in fixed-size chunks. You'd process them in a similar fashion:
void Dolle::serialReceived() {
QString validPacket;
while (serial->bytesAvailable() >= 8) {
auto bin = serial->read(8);
auto packet = QString::fromLatin1(bin);
if (packet.length() < 8)
continue;
validPacket = packet;
}
...
}

Create a QByteArray from a QImage and vice-versa

I need to encode an already-created QImage into a QByteArray to send to a socket, and decode it on the another side.
On the server side, I'm trying to do something like:
// The vector is mandatory. The class that creates the image line expects it.
QVector<unsigned char> msg;
QImage line(create_image_line(msg));
QByteArray ba((char*)line.bits(), line.numBytes());
for (int i = 0; i < ba.size(); ++i) {
msg.append(ba[i]);
}
send_msg(msg);
The create_image_line does something like:
... handle the msg and get the image properties...
QImage img(pixels_, 1, QImage::Format_RGB32);
... set the values ...
return(img);
And on the client side:
receive_msg(msg);
QByteArray ba;
for (int i = 0; i < msg.size(); ++i) {
ba.append(msg[i]);
}
QImage line(LINE_WIDTH, 1, QImage::Format_RGB32);
line.fromData(ba);
Something goes wrong, and the image is shown with a lot of noise (I know that the problem is located in this conversion, due another successful tests).
I'd like to know where is the problem.
Rgds.
QImage::fromData does not retain the format, it tries to probe for a file header. It's also a static function, so it does not modify line (which stays uninitialized), it returns an image (which you discard). And it's concent of format is things like PNG or JPG, not pixel formats like the constructor.
So to do the way you have it now, you need to loop through line.bits again copying in the pixels.
However, QDataStream can serialize most of the Qt value types, including QImage. If you're in control of both ends and open to changing the protocol, this is probably a much simpler and robust solution.