Fetch text from a site as QString
-
QNetworkAccessManager *manager = new QNetworkAccessManager(this); connect(manager, &QNetworkAccessManager::finished, this, &MyClass::replyFinished); manager->get(QNetworkRequest(QUrl( "https://...file"))); // Class public slot function: void replyFinished(QNetworkReply *reply) { if (reply->error() == QNetworkReply::NoError) { QByteArray data = reply->readAll(); QFile file(<file>); if (file.open(QIODevice::WriteOnly)) { QTextStream out(&file); out << data; file.close(); doSomething(<file>); } } reply->deleteLater(); }
Thanks, this is working. Is it safe now?
-
When are you creating your manager object ? Based only on your code, it seems you will be creating it many times though you only need one instance during the lifetime of your application.
-
In the function there is no more code:
void MyClass::downloadFile() { QNetworkAccessManager *manager = new QNetworkAccessManager(this); connect(manager, &QNetworkAccessManager::finished, this, &MyClass::replyFinished); manager->get(QNetworkRequest(QUrl( "https://...file"))); }
Should I make a manager instance as private member of MyClass?
-
@realroot said in Fetch text from a site as QString:
In the function there is no more code:
It is not a question of whether this function has more code. It is a question of whether
MyClass::replyFinished()
completes all processing of the reply/downloading the file. Which I imagine it does.Should I make a manager instance as private member of MyClass?
Yes. And do not allocate it more than once! You could alternatively allocate
manager
within the class instead of withnew
, i.e.QNetworkAccessManager manager;
as a member variable would work. And don't forget you need to callreply->deleteLater()
inMyClass::replyFinished(QNetworkReply *reply)
. As per https://doc.qt.io/qt-6/qnetworkaccessmanager.html#detailsNote: After the request has finished, it is the responsibility of the user to delete the QNetworkReply object at an appropriate time. Do not directly delete it inside the slot connected to finished(). You can use the deleteLater() function.
-
In addition to what @JonB wrote, the fact that you pass a parent to your manager object only ensures that it will get destroyed when the parent gets destroyed.
What you currently have is a variant of memory leak since you create new instances of QNetworkAccessManager every time you call that function and they will only get destroyed when your MyClass instance will as well.
-
I see thanks.
If I declare it asQNetworkAccessManager manager;
I have errors so I made it like this:private: QNetworkAccessManager* m_manager = new QNetworkAccessManager(this); void MyClass::downloadFile() { connect(m_manager, &QNetworkAccessManager::finished, this, &MyClass::replyFinished); manager->get(QNetworkRequest(QUrl( "https://...file"))); } void MyClass::replyFinished(QNetworkReply *reply) { if (reply->error() == QNetworkReply::NoError) { QByteArray data = reply->readAll(); QFile file(<file>); if (file.open(QIODevice::WriteOnly)) { QTextStream out(&file); out << data; file.close(); doSomething(<file>); } } reply->deleteLater(); }
-
@realroot said in Fetch text from a site as QString:
I see thanks.
If I declare it asQNetworkAccessManager manager;
I have errors so I made it like this:private: QNetworkAccessManager* m_manager = new QNetworkAccessManager(this); void MyClass::downloadFile() { connect(m_manager, &QNetworkAccessManager::finished, this, &MyClass::replyFinished);
Move that connect to the constructor of your class. Otherwise each time you call
downloadFile
you will create a new connection which means that the slot will be called an additional time. -
Then it should be so I believe:
class MyClass : public QAbstractListModel { Q_OBJECT public: MyClass(QObject *parent = nullptr) : QAbstractListModel(parent) { connect(m_manager, &QNetworkAccessManager::finished, this, &MyClass::downloadFinished); } private: QNetworkAccessManager* m_manager = new QNetworkAccessManager(this); void MyClass::downloadFile() { m_manager->get(QNetworkRequest(QUrl( "https://...file"))); }
It's working at least.
-
@realroot
It looks reasonable. Although yourQNetworkAccessManager* m_manager = new QNetworkAccessManager(this);
will work personally I would do the
new
in theMyClass
constructor, to the line above where you have moved theconnect()
like @SGaist said. But maybe that's just me. In any case I believe your code is now acceptable. -
-
C++ now allows to initialize class variable at declaration spot. It's easy and nice for base types however, it can make things harder to read for complex type.
Next, unless you have complex logic associated, you should use your class initializer list and after that the constructor. This will help the compiler optimize some things.
-
@Pl45m4 Functions (
downloadFile
andreplyFinished
) are not in the header. I did not specify that but I did add MyClass scope e.g.void MyClass::downloadFile()
.@SGaist So:
MyClass(QObject *parent = nullptr) : QAbstractListModel(parent), m_manager(new QNetworkAccessManager(this)) { connect(m_manager, &QNetworkAccessManager::finished, this, &MyClass::downloadFinished); }
-
@realroot said in Fetch text from a site as QString:
@Pl45m4 Functions ( downloadFile and replyFinished) are not in the header. I did not specify that but I did add MyClass scope e.g. void MyClass::downloadFile().
Don't know what this means, but might be okay.