QtMessageHandler / QTextStream causes memory usage to go up? - qt

I have installed a simple QtMessageHandler in my application:
void handleMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
QString text = msg;
QString path = QCoreApplication::applicationDirPath() + "/logging/logs.txt";
QFile logFile(path);
logFile.open(QIODevice::WriteOnly | QIODevice::Append);
QTextStream textStream(&logFile);
textStream<< text << endl << flush;
}
int main(int argc, char *argv[])
{
qInstallMessageHandler(handleMessage);
// ...
for (int i = 0; i < 10000000; i++) {
qDebug() << "Some text";
}
// ...
}
Now when I use qDebug to write some logs to the log file (and the application is doing nothing else!), the memory usage is shooting up. When I stop writing logs, it goes back down, but never to it's previous value. For every few thousand messages, the memory goes up a few hundred kilobytes and stays that way.
I think it's the QTextStream that is causing this, because if I comment that part out, then the memory usage just remains low.
I tried moving the myMessageHandler function to a separate class and have the QTextStream as a member variable, instead of creating it every time that myMessageHandler() is called, but then I couldn't get qInstallMessageHandler() to work.
int main(int argc, char *argv[])
{
MyMessageHandler myMessageHandler;
qInstallMessageHandler(myMessageHandler.handleMessage);
}
Which gives me:
cannot convert 'MyMessageHandler::handleMessage' from type 'void (MyMessageHandler::)(QtMsgType, const QMessageLogContext&, const QString&)' to type 'QtMessageHandler {aka void (*)(QtMsgType, const QMessageLogContext&, const QString&)}'qInstallMessageHandler(myMessageHandler.handleMessage);
Anyone have an idea on how to solve this?

Related

Save and load QWebEngineHistory to a QWebEnginePage

I need to save the history of a QWebEnginePage and load it back. Therefore I want to store the history from page A in some structure and set it to page B.
In the documentation I found the following methods:
// Saves the web engine history history into stream.
QDataStream &operator<<(QDataStream &stream, const QWebEngineHistory &history)
// Loads the web engine history from stream into history.
QDataStream &operator>>(QDataStream &stream, QWebEngineHistory &history)
But honestly I don't know how to work with them.
I tried the following:
QWebEnginePage *m_history;
...
...
void setHistory(QWebEngineHistory *history){
QDataStream data;
data << history; //Hoping that the content of data is persistent after deleting of the QWebEnginePage where the history is coming from
data >> m_history;
}
And later on I want to load it back to the page:
m_history >> m_webEnginePage.history(); // Pseudo-Code
I know that the QWebEngineHistory of a QWebEnginePage is const, but then I'm wondering why are there even those two methods from above? Why is there a function that "loads the web engine history into history"?
The only alternative I can think of is storing my history in a QList, but managing this is not nice and could lead to more problems (because of the whole forward/backward button etc).
Thank you very much for your help.
No object can be saved, what is saved is the information associated with the object so you should not create QWebEngineHistory but save and/or load the information.
In the following example, the information is saved in a file when the application is closed and the startup is loaded.
#include <QtWebEngineWidgets>
int main(int argc, char *argv[]) {
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc,argv);
const QString filename = "history.bin";
QWebEngineView view;
view.load(QUrl("https://stackoverflow.com"));
{// load
QFile file(filename);
if(file.open(QFile::ReadOnly)){
qDebug() << "load";
QDataStream ds(&file);
ds >> *(view.page()->history());
}
}
view.resize(640, 480);
view.show();
int ret = app.exec();
{// save
QFile file(filename);
if(file.open(QFile::WriteOnly)){
qDebug() << "save";
QDataStream ds(&file);
ds << *(view.page()->history());
}
}
return ret;
}
In the same way you can save it through QSettings:
#include <QtWebEngineWidgets>
int main(int argc, char *argv[]) {
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc,argv);
QWebEngineView view;
view.load(QUrl("https://stackoverflow.com"));
{// load
QSettings settings;
QByteArray ba = settings.value("page/history").toByteArray();
QDataStream ds(&ba, QIODevice::ReadOnly);
ds >> *(view.page()->history());
}
view.resize(640, 480);
view.show();
int ret = app.exec();
{// save
QSettings settings;
QByteArray ba;
QDataStream ds(&ba, QIODevice::WriteOnly);
ds << *(view.page()->history());
settings.setValue("page/history", ba);
}
return ret;
}

Qt5: compile error while QSharedPointer<const T>::create()

Is it "expected" for QSharedPointer::create() not to work or is it a bug? I get an error:
/usr/include/qt5/QtCore/qsharedpointer_impl.h:439:9: error:
invalid conversion from ‘const void*’ to ‘void*’ [-fpermissive]
new (result.data()) T(std::forward<Args>(arguments)...);
casting from nonconst shared pointer and constructor from raw const pointer work.
I got this for Qt5.7.0 and Qt5.10.0.
Here is a minimal example:
#include <QSharedPointer>
struct A {};
int main(int argc, char *argv[])
{
auto ca = QSharedPointer<const A>::create();
return 0;
}
Here is one file (not minimal) example but with few working cases, 2 not working and a debug. Commented defines are for "not compiling" parts.
#include <QSharedPointer>
#include <QDebug>
#define FROM_PTR
//#define CONST_CREATE
#define FROM_RAW_PTR
#define PERFECT_FORWARD_CREATE
//#define PERFECT_FORWARD_CREATE_CONST
#define BUILTIN_CAST
class A
{
public:
A() = default;
A(int i) : _i{i} {}
void foo() const { qDebug() << "const foo" << _i; }
void foo() { qDebug() << "foo" << ++_i; }
private:
int _i{0};
};
using ASPtr = QSharedPointer<A>;
using ASCPtr = QSharedPointer<const A>;
int main(int argc, char *argv[])
{
Q_UNUSED(argc)
Q_UNUSED(argv)
#ifdef FROM_PTR
qDebug() << "FROM_PTR";
auto a1 = ASPtr::create();
a1->foo();
auto ca1 = static_cast<ASCPtr>(a1);
ca1->foo();
qDebug() << "\n";
#endif // FROM_PTR
#ifdef CONST_CREATE
qDebug() << "CONST_CREATE";
auto ca2 = ASCPtr::create();
ca2->foo();
qDebug() << "\n";
#endif // CONST_CREATE
#ifdef FROM_RAW_PTR
qDebug() << "FROM_RAW_PTR";
auto ca3 = ASCPtr(new const A);
ca3->foo();
qDebug() << "\n";
#endif // FROM_RAW_PTR
#ifdef PERFECT_FORWARD_CREATE
qDebug() << "PERFECT_FORWARD_CREATE";
auto a2 = ASPtr::create(10);
a2->foo();
qDebug() << "\n";
#endif // PERFECT_FORWARD_CREATE
#ifdef PERFECT_FORWARD_CREATE_CONST
qDebug() << "PERFECT_FORWARD_CREATE_CONST";
auto ca4 = ASCPtr::create(20);
ca4->foo();
qDebug() << "\n";
#endif // PERFECT_FORWARD_CREATE
#ifdef BUILTIN_CAST
qDebug() << "BUILTIN_CAST";
QSharedPointer<A> a3 = ASPtr::create();
a3->foo();
auto ca4 = a3.constCast<const A>();
ca4->foo();
qDebug() << "\n";
#endif // BUILTIN_CAST
return 0;
}
That is a known Qt bug (QTBUG-49748). Although it is marked as resolved in Qt 5.6.0, the bug is still present as pointed out in the comments.
Why is this happening?
Look at the implmentation of the class QSharedPointer qsharedpointer_impl.h.
In particular the line:
new (result.data()) T(std::forward<Args>(arguments)...);
uses the result.data() as the new expression placement params. Unfortunately, one can not use a const pointer as a placement param (have a look at this question here on SO for more details).
Hence, there's not much you can do except reporting this to Qt developers via the official bug tracker.
You may have a look at the smart pointers provided by the standard library (e.g. std::shared_ptr) if you are not forced to use Qt ones.
UPDATE
As reported in Qt bug tracker, this bug was fixed in version 5.11 (here is the related commit). Basically, they used std::remove_cv to remove the topmost const from the type specified.

QT QString from QDataStream

I'm working with a buffer and I'm trying to get a string from it, but isnt working...
Example:
*void myFunc(QDataStream& in)
{
quint8 v;
in >> v;
// Ok, I caught v value successfuly
QString s;
in >> s;
// Didnt work :<
}*
The string lenght is stored on 2 first bytes...
Thanks
If the string was not written as a QString, you need to read its length and content separately.
quint8 v;
in >> v;
quint16 length = 0;
in >> length;
// the string is probably utf8 or latin
QByteArray buffer(length, Qt::Uninitialized);
in.readRawData(buffer.data(), length);
QString string(buffer);
You might have to change the endianness of the QDataStream with QDataStream::setByteOrder before reading the 16-bit length.
We should really see the writing code and how you create the QDataStream. I tried with the following sample, and in this case your function works very well:
#include <QCoreApplication>
#include <QDebug>
#include <QDataStream>
#include <QBuffer>
void myFunc(QDataStream& in)
{
quint8 v;
in >> v;
qDebug() << v;
// Ok, I caught v value successfuly
QString s;
in >> s;
qDebug() << s;
// Didnt work :<
}
int main(int argc, char ** argv) {
QCoreApplication a(argc, argv);
QBuffer buffer;
buffer.open(QBuffer::ReadWrite);
// write test data into the buffer
QDataStream out(&buffer);
quint8 ival = 42;
QString sval = "Qt";
out << ival;
out << sval;
// read back data
buffer.seek(0);
myFunc(out);
return a.exec();
}
Output when executed:
$ ./App
42
"Qt"

Serializing QHash to QByteArray

I am trying to serialize a QHash object and store it in a QByteArray (to be sent using QUDPSocket or QTCPSocket).
My current attempt looks like this:
// main.cpp
#include <QtCore/QCoreApplication>
#include <QHash>
#include <QVariant>
#include <QDebug>
int main(int argc, char *argv[])
{
QHash<QString,QVariant> hash;
hash.insert("Key1",1);
hash.insert("Key2","thing2");
QByteArray ba;
QDataStream ds(&ba, QIODevice::WriteOnly);
ds << hash;
qDebug() << ds;
}
When this runs I get this out of qDebug():
QIODevice::read: WriteOnly device
QIODevice::read: WriteOnly device
QIODevice::read: WriteOnly device
QVariant(, )
The documentation says that this should write to the byte array, but obviously that isn't happening here. What am I doing wrong?
Qt 4.7.1 on OS-X
Thanks!
-J
The reason it is failing is because it is trying to read from a write-only stream. The sequence is:
qDebug() << ds;
--> QVariant::QVariant(QDataStream &s)
--> QDataStream& operator>>(QDataStream &s, QVariant &p)
--> void QVariant::load(QDataStream &s)
That last method (and some more downstream) try to read from the data stream to convert its contents into a QVariant for display in qDebug. In other words, your actual code is fine; the debugging check is causing the failure.
You could check the contents of the byte array with something like:
qDebug() << ba.length() << ba.toHex();
You can Implement you program like this code:
QHash<QString,QVariant> options;
options["string"] = "my string";
options["bool"] = true;
QByteArray ar;
//Serializing
QDataStream out(&ar,QIODevice::WriteOnly); // write the data
out << options;
//setting a new value
options["string"] = "new string";
//Deserializing
// read the data serialized from the file
QDataStream in(&ar,QIODevice::ReadOnly);
in >> options;
qDebug() << "value: " << options.value("string");
ref

Save QList<int> to QSettings

I want to save a QList<int> to my QSettings without looping through it.
I know that I could use writeArray() and a loop to save all items or to write the QList to a QByteArray and save this but then it is not human readable in my INI file..
Currently I am using the following to transform my QList<int> to QList<QVariant>:
QList<QVariant> variantList;
//Temp is the QList<int>
for (int i = 0; i < temp.size(); i++)
variantList.append(temp.at(i));
And to save this QList<Variant> to my Settings I use the following code:
QVariant list;
list.setValue(variantList);
//saveSession is my QSettings object
saveSession.setValue("MyList", list);
The QList is correctly saved to my INI file as I can see (comma seperated list of my ints)
But the function crashes on exit.
I already tried to use a pointer to my QSettings object instead but then it crashes on deleting the pointer ..
QSettings::setValue() needs QVariant as a second parameter. To pass QList as QVariant, you have to declare it as a Qt meta type. Here's the code snippet that demonstrates how to register a type as meta type:
#include <QCoreApplication>
#include <QDebug>
#include <QMetaType>
#include <QSettings>
#include <QVariant>
Q_DECLARE_METATYPE(QList<int>)
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qRegisterMetaTypeStreamOperators<QList<int> >("QList<int>");
QList<int> myList;
myList.append(1);
myList.append(2);
myList.append(3);
QSettings settings("Moose Soft", "Facturo-Pro");
settings.setValue("foo", QVariant::fromValue(myList));
QList<int> myList2 = settings.value("foo").value<QList<int> >();
qDebug() << myList2;
return 0;
}
You might have to register QList as a meta-type of its own for it to work. This is a good starting point to read up on meta-types in Qt: http://qt.nokia.com/doc/4.6/qmetatype.html#details .
I was also struggling with this ... and I believe I now have a decent solution.
I hope this saves someone the trouble, it caused me.
#include <QCoreApplication>
#include <QSettings>
#include <QList>
#include <QDataStream>
#include <QVariant>
#include <QVariantList>
#include <QDebug>
#include <deque>
template <class T> static QVariant toVariant(const QList<T> &list)
{
QVariantList variantList;
variantList.reserve(list.size());
for (const auto& v : list)
{
variantList.append(v);
}
return variantList;
}
template <class T> static QList<T> toList(const QVariant &qv)
{
QList <T> dataList;
foreach(QVariant v, qv.value<QVariantList>()) {
dataList << v.value<T>();
}
return dataList;
}
void Gen()
{
QList<QString> data {"hello", "world","how", "are", "you"};
QList<int> ages {10,20,30,40};
QSettings setup("stuff.ini", QSettings::IniFormat);
setup.beginWriteArray("rules");
for (int groups=0;groups<3;groups++)
{
setup.setArrayIndex(groups);
setup.setValue("rule",QString("Rule-%1").arg(groups));
setup.setValue("data", toVariant (data));
setup.setValue("ages", toVariant (ages));
}
setup.endArray();
setup.sync();
}
void Read()
{
QSettings setupR("stuff.ini", QSettings::IniFormat);
int rule_count = setupR.beginReadArray("rules");
for (int groups=0;groups<rule_count;groups++)
{
setupR.setArrayIndex(groups);
QString nameRead = setupR.value("rule").toString();
QList<QString> dataRead = toList<QString>(setupR.value("data"));
qDebug() << "Rule " << groups;
qDebug() << "Rule Read" << nameRead;
qDebug() << "Data Read2" << dataRead;
}
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// Write
Gen();
// Read
Read();
exit(0);
//return a.exec();
}
You should end up with an INI file which looks like this ...
[rules]
1\ages=10, 20, 30, 40
1\data=hello, world, how, are, you
1\rule=Rule-0
2\ages=10, 20, 30, 40
2\data=hello, world, how, are, you
2\rule=Rule-1
3\ages=10, 20, 30, 40
3\data=hello, world, how, are, you
3\rule=Rule-2
size=3
The nice thing here is you can edit this outside of QT (Just be careful) ...

Resources