I use this link to read an image from SQL dataBase (the image saved in dataBase as BLOB) and pass it to qml but I get this error :
QML Image: Error decoding: data:image/png;base64,77+9UE5HDQoaCg==: Unsupported image format
how can I fix this error?
cpp:
void MainWindow::saveToDatabase(QQuickItem *imageObj) {
QSharedPointer<const QQuickItemGrabResult> grabResult =
imageObj->grabToImage();
connect(grabResult.data(), &QQuickItemGrabResult::ready, [=]() {
QImage img(grabResult->image());
QByteArray imageArray;
QBuffer buffer(&imageArray);
buffer.open(QIODevice::WriteOnly);
img.save(&buffer, "PNG");
dbModify->dbinsertRecords(imageArray);
});
}
QString MainWindow::convertToImage(int pos) {
//SelectLastData define as QVariantList in header
QString base64 =
QString::fromUtf8(SelectLastData[pos].toByteArray().toBase64().data());
return QString("data:image/png;base64,") + base64;
}
qml:
onClicked: {
stackView.push("qrc:/imagePage.qml", {"url" : mainWindow.convertToImage(9)})
}
imagePage.qml:
Page {
width: Screen.width
height: Screen.height
visible: true
property alias url: screenshoot.source
Rectangle {
Image {
id: screenshoot
}
}
}
The base64 data in the error message: "77+9UE5HDQoaCg==" corresponds to the bytes ef bf bd 50 4e 47 0d 0a 1a 0a which includes 3 bytes that shouldn't be there for a PNG header (it should start with 89 50 4e 47) and that indicates that the data is truncated, which means the data is read as a null terminated string instead of a binary blob (9th byte of a PNG file is usually a null character).
That is probably because you are passing a char* to QVariant instead of a QByteArray when you initialize the QVariantList.
Basically, when you pass a char* as a parameter to its constructor or setValue, QVariant interprets it as a null terminated UTF-8 string and stores it natively as a QString, then, when you call toByteArray it converts it to UTF-8. The 2 conversions from and to UTF-8 can cause corruption on binary data.
If you create an intermediate QByteArray variable and use it directly to initialize the QVariant, it will be stored as QByteArray and no conversion will occur when you'll call toByteArray.
Related
Qt/C++ program has a function which writes an objects data (_token) to a file as follows:
QFile f( _tokenFile);
if (!f.open( QIODevice::WriteOnly)) {
qDebug() << "Unable to open token file for writing" << f.errorString();
return false;
}
QByteArray tokenBa;
QDataStream ds( &tokenBa, QIODevice::WriteOnly);
ds << _token;
tokenBa = qCompress( tokenBa);
f.write( tokenBa);
f.close();
The _token is an instance of the following struct:
struct Token {
QString accessToken;
QString refreshToken;
QString clientSecret;
QString authCode;
QTime expiryTime;
enum AuthState {
A_Invalid,
A_RequestAuth,
A_Authenticated,
};
AuthState state;
Token() : state( A_Invalid) {}
bool isValid() const {
if (accessToken.isEmpty() ||
refreshToken.isEmpty()) {
return false;
}
return true;
}
void inValidate() {
accessToken.clear();
refreshToken.clear();
clientSecret.clear();
authCode.clear();
expiryTime = QTime();
}
void cleanUp() {
accessToken.clear();
refreshToken.clear();
clientSecret.clear();
authCode.clear();
expiryTime = QTime();
}
};
When the file is saved it has 4 extra bytes at the start which render the file as a invalid zlib file.
0000000 0000 5e01 9c78 904d 4f5d 5082 871c fa9f
0000020 353e 25cd 6975 2c2f d563 4c2c 62b8 cad1
We can see above bytes 5-6 are 9C 78 which is the zlib signature, but the 4 bytes before these are the issue.
To check the compressed data is correct I do the following:
dd if=file.token bs=1 skip=4 | openssl zlib -d
And this produces the expected result (for testing).
The problem is in the application reading this data back into the data object:
QFile f( _tokenFile);
if (!f.exists()) {
qDebug() << "Token file doesn't exist" << f.fileName();
return false;
}
if (!f.open( QIODevice::ReadOnly)) {
qDebug() << "Unable to open token file for reading" << f.errorString();
return false;
}
QByteArray tokenBa = f.readAll();
f.close();
if (tokenBa.isEmpty()) {
qDebug() << "Token file is empty.";
return false;
}
tokenBa = qUncompress( tokenBa);
QDataStream ds( &tokenBa, QIODevice::ReadOnly);
ds >> _token;
This returns null - because of the leading 4 extraneous bytes. I could put some code in to skip these 4 leading bytes, but how do I know it will always be 4 bytes? I'd like to instead have certainly that the files data is all zlib compressed.
My question is how to avoid those bytes being saved in the first place so that on re-read the format is known to be zlib type?
You can't avoid them since they're needed for qUncompress later on:
Note: If you want to use this function to uncompress external data that was compressed using zlib, you first need to prepend a four byte header to the byte array containing the data. The header must contain the expected length (in bytes) of the uncompressed data, expressed as an unsigned, big-endian, 32-bit integer.
I am using the following code from this example: https://doc.qt.io/qt-5/qtserialbus-can-example.html
enum {
MaxPayload = 8,
MaxPayloadFd = 64
};
HexStringValidator::HexStringValidator(QObject *parent) :
QValidator(parent),
m_maxLength(MaxPayload)
{
}
HexStringValidator::HexStringValidator(QObject *parent, uint maxLength) :
QValidator(parent),
m_maxLength(maxLength)
{
}
class HexStringValidator : public QValidator
{
Q_OBJECT
public:
explicit HexStringValidator(QObject *parent = nullptr);
explicit HexStringValidator(QObject *parent, uint maxLength);
QValidator::State validate(QString &input, int &pos) const;
void setMaxLength(int maxLength);
private:
uint m_maxLength = 0;
};
The problem is in this function:
I have a lineedit with this validator and call it with a valid Hex String.
ui->lineEditCANCommand->setValidator(new HexStringValidator(this, 4));
ui->lineEditCANCommand->setText("000003e9");
QValidator::State HexStringValidator::validate(QString &input, int &pos) const
{
const int maxSize = 2 * static_cast<int>(m_maxLength);
const QChar space = QLatin1Char(' ');
QString data = input;
data.remove(space);
if (data.isEmpty())
return Intermediate;
// limit maximum size and forbid trailing spaces
if ((data.size() > maxSize) || (data.size() == maxSize && input.endsWith(space)))
return Invalid;
// check if all input is valid
const QRegularExpression re(QStringLiteral("^[[:xdigit:]]*$"));
if (!re.match(data).hasMatch())
return Invalid;
// insert a space after every two hex nibbles
const QRegularExpression insertSpace(QStringLiteral("(?:[[:xdigit:]]{2} )*[[:xdigit:]]{3}"));
if (insertSpace.match(input).hasMatch()) {
input.insert(input.size() - 1, space);
pos = input.size();
}
return Acceptable;
}
The function is supposed to change this to "00 00 03 e9"
It however runs endless and creates
000003e 9
000003e 9
000003e 9
000003e 9
What is going wrong? Note, this code is from Qt and not mine.
In Qt6 problem should be gone.
There was related to this problem issue and two following commits:
Can example: Fix crash in payload hex editor,
CAN Example: Properly format payload hex string
It is possible to test since Qt 6.2.0.
First, I changed insertSpace regular expression to threeDigits:
QStringLiteral("[[:xdigit:]]{3}")
and insert space if 3 digits were added sequentially:
input.insert(match.capturedEnd() - 1, space);
That solves crash problem (infinite recursion).
Then I added auxilliary condition isEvenHex and formatter formatHexData with oneDigitAndSpace and threeDigits regular expressions that removes all extra spaces and add space after every two hex nibbles. I call them respectively on text change and sendButton click:
connect(m_ui->payloadEdit, &QLineEdit::textChanged, frameIdOrPayloadChanged);
connect(m_ui->sendButton, &QPushButton::clicked, [this]() {
...
m_ui->payloadEdit->setText(formatHexData(data));
...
}
Trying to solve your task I added:
QValidator::State HexStringValidator::validate(QString &input, int &pos) const
{
...
QString data = input;
const QRegularExpression twoSpaces(QStringLiteral("([\\s]{2})"));
if (twoSpaces.match(data).hasMatch())
return Invalid;
data.remove(space);
...
But there is one problem: if you are editing string in payload editor you may remove digits and two spaces can occure. The validator won't allow you remove characters between spaces. As a compromise I would introduce
threeSpaces(QStringLiteral("([\\s]{3})"));
validator instead of twoSpaces. With that one you may enter two spaces sequentially but still can edit string. I may ask QtSerialBus developers to include that change too.
I'm creating a socket-based program to send a screenshot from one user to another user. I need to convert a screenshot to a byte array before sending. After I convert my screenshot to a QByteArray I insert 4 bytes to the beginning of the array to mark that it is a picture (it is the number 20 to tell me it is a picture and not text or something else).
After I send the byte array via a socket to other user, when it is received I read the first 4 bytes to know what it is. Since it was a picture I then convert it from a QByteArray to QPixmap to show it on a label. I use secondPixmap.loadFromData(byteArray,"JPEG") to load it but it not load any picture.
This is a sample of my code:
void MainWindow::shootScreen()
{
originalPixmap = QPixmap(); // clear image for low memory situations
// on embedded devices.
originalPixmap = QGuiApplication::primaryScreen()->grabWindow(0);
scaledPixmap = originalPixmap.scaled(500, 500);
QByteArray bArray;
QBuffer buffer(&bArray);
buffer.open(QIODevice::WriteOnly);
originalPixmap.save(&buffer,"JPEG",5);
qDebug() << bArray.size() << "diz0";
byteArray= QByteArray();
QDataStream ds(&byteArray,QIODevice::ReadWrite);
int32_t c = 20;
ds << c;
ds<<bArray;
}
void MainWindow::updateScreenshotLabel()
{
this->ui->label->setPixmap(secondPixmap.scaled(this->ui->label->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
}
void MainWindow::on_pushButton_clicked()
{
shootScreen();
}
void MainWindow::on_pushButton_2_clicked()
{
secondPixmap = QPixmap();
QDataStream ds(&byteArray,QIODevice::ReadOnly);
qint32 code;
ds>>code;
secondPixmap.loadFromData(byteArray,"JPEG");
updateScreenshotLabel();
}
Your MainWindow::on_pushButton_2_clicked implementation looks odd. You have...
QDataStream ds(&byteArray,QIODevice::ReadOnly);
which creates a read-only QDataStream that will read it's input data from byteArray. But later you have...
secondPixmap.loadFromData(byteArray,"JPEG");
which attempts to read the QPixmap directly from the same QByteArray -- bypassing the QDataStream completely.
You can also make use of the QPixmap static members that read from/write to a QDataStream. So I think you're looking for something like...
QDataStream ds(&byteArray,QIODevice::ReadOnly);
qint32 code;
ds >> code;
if (code == 20)
ds >> secondPixmap;
And likewise for your MainWindow::shootScreen implementation. You could reduce your code a fair bit by making use of QDataStream & operator<<(QDataStream &stream, const QPixmap &pixmap).
I have ordinrary text file with settings, which is generated by java application. Inside this file, there is a key db.url, which has value db.URL=jdbc\:mysql\://192.168.0.101\:3306/dbuser. I parse this file with QSettings class in QSettings::Native mode, everything is ok, but this db.URL gets messed up if I read it via value() method. Whatever I do (if I transform it into QString or QUrl), I get same result: jdbcmysql//192.,168.0.1013306/user. Why this key gets messed up?? I am using Qt 5.4 on Kubuntu 14.10 with kernel Linux desktop001 3.16.0-30-generic #40-Ubuntu SMP Mon Jan 12 22:06:37 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux. Here is a simple method that wrongly reads value of key:
QString UePOSSesttings::ueReadDbUrl() const
{
// QVariant dbUrl=this->value(UeDefaults::UeDbKeys::KEY_DB_URL);
return this->value(UeDefaults::UeDbKeys::KEY_DB_URL).toString();
}
and constants:
#ifndef UEDEFAULTS
#define UEDEFAULTS
#include <QString>
namespace UeDefaults
{
namespace UeDbKeys
{
static const QString KEY_DB_DRIVER="db.driver";
static const QString KEY_DB_PASSWORD="db.password";
static const QString KEY_DB_URL="db.URL";
static const QString KEY_DB_DRIVER_LIB="db.driverlib";
static const QString KEY_DB_ENGINE="db.engine";
static const QString KEY_DB_USER="db.user";
}
}
#endif // UEDEFAULTS
QSettings clears up the string from unsupported escape sequences, in this case \:. Remove \ slashes before reading the value or don't use QSettings for parsing unsupported file formats.
Perhaps not the most optimal solution but you could processes the settings file to escape all \: before reading it with QSettings.
QFile oldSettings("settings.txt");
oldSettings.open(QIODevice::ReadOnly);
QString data = QString::fromAscii(oldSettings.readAll().constData());
oldSettings.close();
data.replace("\\:", "\\\\:");
QFile newSettings("/tmp/settings.txt");
newSettings.open(QIODevice::WriteOnly);
newSettings.write(data.toAscii(), data.size());
newSettings.close();
I did it:
void UePOSSesttings::ueParseData(const QString& filename)
{
QFile settingsFile(filename);
QString data;
settingsFile.open(QIODevice::ReadOnly);
data=QString::fromLatin1(settingsFile.readAll().constData());
data.replace("\\:",
":");
this->ueSetParsedData(data);
qDebug() << this->ueParsedData();
settingsFile.close();
}
and now I get this url:
db.URL=jdbc:mysql://192.168.0.101:3306/dbuser
which is ok!
I receive a byte array which contains PNG file from network. I need to set this to a pixmap and set it as a texture to my QGlWidget. As I run the program below, pixmap is in debug mode and does not contain anything. However, bytes contains the whole byte array received from network.
void MainWindow::dataFromServer(QByteArray bytes)
{
// QByteArray bytes;
QBuffer buffer(&bytes);
QPixmap pixmap;
// pixmap = QPixmap::grabWidget(this);
buffer.open(QIODevice::WriteOnly);
pixmap.save(&buffer, "PNG"); // writes pixmap into bytes in PNG format
emit sendPixmapToWidget(pixmap);
}
and here I set the pixmap to texture:
void GlWidget::pixmapCatchFromForm(QPixmap pixmap)
{
deleteTexture(texture);
// image->loadFromData(bytes, "PNG");
texture = bindTexture(pixmap);
qDebug() << texture; // returns 1
updateGL();
}
QPixmap::save(..) saves the QPixmap's contents to the buffer, surely you want to use QPixmap::loadFromData(..) to do the opposite?