I have a function and connection between a slot and a signal.
Check the code below:
void NetworkAccessManager::sendPOST(QString url)
{
QNetworkCookieJar *cookieJar = new QNetworkCookieJar(manager_);
manager_->setCookieJar(cookieJar);
QNetworkRequest request(url);
QByteArray postData;
postData.append("j_username=admin&");
postData.append("j_password=admin");
manager_->post(request, postData);
connect(manager_, SIGNAL(finished(QNetworkReply *)), this, SLOT(replyFinishedSlot(QNetworkReply *)));
}
void NetworkAccessManager::replyFinishedSlot(QNetworkReply *reply)
{
...
cookie = "...";
}
Code above owned by class NetworkAccessManager. Variable cookie is public and I need to change its value in replyFinishedSlot.
I try to use the function sendPOST() in constructer of another class and it works, but slot doing nothing and cookie varaible is empty. What do I do wrong?
Here's the code inside another class:
NetworkAccessManager *manager = new NetworkAccessManager();
manager->sendPOST("http://example.com");
qDebug() << "cookies: " << manager->cookies;
I guess that slot may not work because I never emit signal finished(), but I am not sure where should I emit this because my code shouldn't work with the user interface.
I have found a couple of problem solutions.
First one
I am saving the cookie in a file. Here's the code:
void NetworkAccessManager::replyFinishedSlot(QNetworkReply *reply)
{
...
QFile cookieFile("cookie.txt");
if (cookieFile.open(QIODevice::WriteOnly))
{
cookieFile.write(cookies.toUtf8());
cookieFile.close();
}
...
}
Here's the constructer of class where I use the function
NetworkAccessManager *manager = new NetworkAccessManager();
manager->sendPOST("http://example.com");
QString cookies = "";
QFile cookieFile("cookie.txt");
if (cookieFile.exists() && cookieFile.open(QIODevice::ReadOnly))
{
cookies = cookieFile.readAll();
cookieFile.close();
}
QMap<QString, QString> elements;
elements.insert("cookies", cookies);
Second one
I use a signal to send the info to QML (I transferred my NetworkAccessManager class to WebViewModel).
.h
class WebViewModel : public QObject
{
Q_OBJECT
public:
explicit WebViewModel(QObject *parent = nullptr);
~WebViewModel();
void sendPOST(QString url);
private slots:
void replyFinishedSlot(QNetworkReply *);
private:
QNetworkAccessManager *manager_;
signals:
void finished(QNetworkReply *);
void cookieReady(QString thisCookie);
};
.cpp
WebViewModel::WebViewModel(QObject* parent)
: QObject(parent)
{
manager_ = new QNetworkAccessManager(this);
sendPOST("http://example.com");
}
WebViewModel::~WebViewModel()
{
delete manager_;
}
void WebViewModel::sendPOST(QString url)
{
...
connect(manager_, SIGNAL(finished(QNetworkReply *)), this, SLOT(replyFinishedSlot(QNetworkReply *)));
...
}
void WebViewModel::replyFinishedSlot(QNetworkReply *reply)
{
if(reply->error())
{
...
}
else
{
...
QString cookieString = cookie[0].name() + "=" + cookie[0].value() + "; domain=" + cookie[0].domain() + "; path=" + cookie[0].path();
emit cookieReady(cookieString);//the signal sends my variable to .qml
...
}
}
.qml
Connections {
target: WebViewModel
onCookieReady: {
testCookies = thisCookie
//thisCookie variable wasn't declared in the .qml file, thisCookie variable
//is accessible by signal void cookieReady(QString thisCookie) from .h C++
//and MUST have exactly the same name in .qml file as in .h file
}
}
Related
I connect two signals to same slot. like this:
check = new QCheckBox();
connect(check, SIGNAL(clicked()), this, SLOT(MySlot()));
connect(check, SIGNAL(toggled(bool)),this,SLOT(MySlot()));
I don't want to define an other slot. In MySlot is it possible to recognize which signal callbacks the slot?
How can I do this?
You might be able to use the QMetaObject/QMetaMethod data associated with the sender to get what you want (untested)...
void MyClass::MySlot ()
{
auto index = senderSignalIndex();
if (index == sender()->indexOfSignal("clicked()")) {
/*
* Got here as the result of a clicked() signal.
*/
} else if (index == sender()->indexOfSignal("toggled(bool)")) {
/*
* Got here as the result of a toggled(bool) signal.
*/
}
}
Rather than that, however, if you're using Qt5 I would suggest making use of the new signal/slot syntax along with lambdas...
check = new QCheckBox();
connect(check, &QCheckBox::clicked,
[this]()
{
MySlot(false);
});
connect(check, &QCheckBox::toggled,
[this](bool toggled)
{
MySlot(true, toggled);
});
Along with a change to the signature of MySlot...
/**
* #param from_toggled_signal If true this call was triggered by a
* QCheckBox::toggled signal, otherwise it's
* the result of a QCheckBox::clicked signal.
*
* #param toggle_value If from_toggled_signal is true then this was the
* value passed to QCheckBox::toggled, otherwise unused.
*/
void MyClass::MySlot (bool from_toggled_signal, bool toggle_value = false)
{
.
.
.
}
New slots can be defined on the fly using lambdas :)
class MyClass : public QWidget {
QSomeLayout m_layout{this};
QCheckBox m_check;
enum Signal { Clicked, Toggled };
Q_SLOT void mySlot(Signal);
public:
MyClass( ... ) : ... {
m_layout.addWidget(&m_check);
connect(&m_check, &QCheckBox::clicked, this, [this]{ mySlot(Clicked); });
connect(&m_check, &QCheckBox::toggled, this, [this]{ mySlot(Toggled); });
}
};
You can also add your own context to the signal if that helps. For instance I had a service that downloaded user avatars for multiple windows. I needed the window to only load the user it was interested in something so I would pass in the user's id as the context. Something like:
void UserService::downloadAvatar(const QString& url, const int context = 0) {
...// Make the http request, on finished:
emit onAvatarDownloaded(context, responseBody);
}
I am going to use QNetworkAccessManager to make requests to HTTP server by my mobile app to the server. The question is, how do you link custom data to each request ? I tried to subclass QNetworkReply but I found out that I have to implement virtual methods close() and isSequential() but I don't know what those should return so I am afraid I am going to break network request functionality.
For example, when my app does the log in procedure, it has to store the email address of the account:
class MyApp : public QObject
{
Q_OBJECT
private:
QNetworkRequest request;
QNetworkReply *reply;
QNetworkAccessManager *manager;
...
}
void MyApp::do_log_in(QString email, QString password) {
QString s;
someobject.email=email; // <-- I have to store email address before sending request to server, but where do I store it?
s.append("http://myapp.com/do-auth.php?email=");
s.append(QUrl::toPercentEncoding(email));
s.append("&password=");
s.append(QUrl::toPercentEncoding(password));
connect(manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(login_finished(QNetworkReply*)));
request.setUrl(QUrl(s));
manager->get(request);
}
void MyApp::login_finished(QNetworkReply *rep) {
DepservReply *reply;
QString email;
....
email= ...... // <-- I need to get the email address from QNetworkReply object somehow
///my code here handling server reply
....
}
So, how do I implement storage and retrieval of email in my case, what classes should I subclass and what methods should I re-implement ?
You can leverage the dynamic property system available in each QObject and hold the data in the reply:
// https://github.com/KubaO/stackoverflown/tree/master/questions/network-reply-tracking-40707025
#include <QtNetwork>
class MyCtl : public QObject
{
Q_OBJECT
QNetworkAccessManager manager{this};
// ...
void reply_finished(QNetworkReply *reply);
public:
MyCtl(QObject *parent = nullptr);
void do_log_in(const QString &email, const QString &password);
};
static const char kAuthGetSalt[] = "req_auth-get-salt";
static const char kDoAuth[] = "req_do-auth";
static const char kEmail[] = "req_email";
static const char kPassword[] = "req_password";
static const auto authGetSaltUrl = QStringLiteral("https://myapp.com/auth-get-salt.php?email=%1");
static const auto doAuthUrl = QStringLiteral("https://myapp.com/do-auth.php?email=%1&passwordHash=%2");
MyCtl::MyCtl(QObject *parent) : QObject{parent}
{
connect(&manager, &QNetworkAccessManager::finished, this, &MyCtl::reply_finished);
}
void MyCtl::do_log_in(const QString &email, const QString &password) {
auto url = authGetSaltUrl.arg(email);
auto reply = manager.get(QNetworkRequest{url});
reply->setProperty(kAuthGetSalt, true);
reply->setProperty(kEmail, email);
reply->setProperty(kPassword, password);
}
void MyCtl::reply_finished(QNetworkReply *reply) {
if (!reply->property(kAuthGetSalt).isNull()) {
reply->deleteLater(); // let's not leak the reply
if (reply->error() == QNetworkReply::NoError) {
auto salt = reply->readAll();
auto email = reply->property(kEmail).toString();
auto password = reply->property(kPassword).toString();
Q_ASSERT(!password.isEmpty() && !email.isEmpty());
QCryptographicHash hasher{QCryptographicHash::Sha1};
hasher.addData(salt); // the server must hash the same way
hasher.addData("----");
hasher.addData(password.toUtf8());
auto hash = hasher.result().toBase64(QByteArray::Base64UrlEncoding);
auto url = doAuthUrl.arg(email).arg(QString::fromLatin1(hash));
auto reply = manager.get(QNetworkRequest{url});
reply->setProperty(kDoAuth, true);
reply->setProperty(kEmail, email);
}
}
else if (!reply->property(kDoAuth).isNull()) {
if (reply->error() == QNetworkReply::NoError) {
auto email = reply->property(kEmail).toString();
// ...
}
}
}
Use a constant for a property name to avoid typos by letting the compiler check that you're using a valid identifier.
The example above rectifies the following critical safety issues in your code:
Sending security credentials over a clear connection: use https://, not http://.
Sending a password in cleartext: instead, send a salted hash of it. Your server should generate a random salt for each account when the accounts are created. Existing accounts can be left unsalted, but they should acquire a salt as soon as the user changes the password.
Also note that a QString to QUrl conversion will automatically percent-encode the string, so doing it explicitly is unnecessary.
In this case email is part of the request's URL so you could just extract it from there (the QNetworkReply has access to the QNetworkRequest it is handling, see QNetworkReply::request()).
You an also store more or less any kind of data as a dynamic property because QNetworkReply is a QObject derived class, see QObject::setProperty().
You can subclass QNAM to get more control of it.
network.h
class QNAMNetwork : public QNetworkAccessManager
{
Q_OBJECT
public:
explicit QNAMNetwork(QObject *parent = 0);
~QNAMNetwork();
inline void insertUserValue(const QString & key, const QString & value){this->m_user_values.insert(key,value);}
inline QString getUserValue(const QString & key){return this->m_user_values.value(key);}
signals:
void requestFinished(ExNetwork *, QNetworkReply *);
private slots:
void _sslErrors(QNetworkReply *, const QList<QSslError> &);
void _finished(QNetworkReply *);
private:
QMap<QString, QString> m_user_values;
};
network.cpp
QNAMNetwork::QNAMNetwork(QObject *parent):QNetworkAccessManager(parent)
{
connect(this, &QNAMNetwork::sslErrors, this, &QNAMNetwork::_sslErrors);
connect(this, &QNAMNetwork::finished, this, &QNAMNetwork::_finished);
}
QNAMNetwork::~QNAMNetwork()
{
//qDebug() << __FUNCTION__ << QString::number((long)this,16);
}
void QNAMNetwork::_sslErrors(QNetworkReply * reply, const QList<QSslError> & errors)
{
reply->ignoreSslErrors(errors);
}
void QNAMNetwork::_finished(QNetworkReply * reply)
{
emit requestFinished(this, reply);
}
usecase:
QNAMNetwork * network = new QNAMNetwork(this);
network->insertUserValue("email","yourmail#mail.com");
connect(network, SIGNAL(requestFinished(QNAMNetwork*,QNetworkReply*)), this, SLOT(requestFinished(QNAMNetwork*,QNetworkReply*)));
QNetworkRequest req(QUrl::fromUserInput(query)); //get url
network->get(req);
...
void YourClass::requestFinished(QNAMNetwork * net, QNetworkReply * rep)
{
QString email = net->getUserValue("email");
net->deleteLater();
rep->deleteLater();
}
Implementation:
void Test::addProcessToList(const QString &command, const QString &id, const BasicInfo &basicInfo) {
QProcess *console = new QProcess();
QSignalMapper* signalMapper = new QSignalMapper (this) ;
connect (console, SIGNAL(readyRead()), signalMapper, SLOT(map())) ;
connect (console, SIGNAL(finished(int)), signalMapper, SLOT(processFinished(int))) ;
signalMapper->setMapping (console, id) ;
connect (signalMapper, SIGNAL(mapped(int)), this, SLOT(pidOut(QString))) ;
console->start(command);
}
void Test::registerProcess(QString id) {
QProcess *console = qobject_cast<QProcess*>(QObject::sender());
QByteArray processOutput = console->readAll();
int mainPID = parsePID(processOutput);
BasicInfo basicInfo;
qDebug() << "Registering id: " + id + " mainPID: " + mainPID;
if(mainPID != 0) {
Main::getInstance()->addProcessToList(mainPID, packageId, basicInfo);
} else {
qWarning() << "pidOut Error fetching mainPID";
}
}
void Test::processFinished(int exitCode) {
QProcess *console = qobject_cast<QProcess*>(QObject::sender());
QByteArray processOutput = console->readAll() + QString("Finished with code %1").arg(exitCode).toLatin1();
qDebug() << " processFinished: " + processOutput;
}
prototypes:
private
void addProcessToList(const QString &command, const QString &id, const BasicInfo &basicInfo);
private slots:
void registerProcess(QString);
void processFinished(int);
I get this errors when I call connect, which tells me I'm doing it wrong:
"QObject::connect: Incompatible sender/receiver arguments
QSignalMapper::mapped(int) --> Test::registerProcess(QString)"
I'm not understanding where I'm suppose to specify my parameter (QString id) so that registerProcess will receive it when it's called? I'm assuming I'm doing this part wrong, cut from above:
signalMapper->setMapping (console, id) ;
connect (signalMapper, SIGNAL(mapped(int)), this, SLOT(pidOut(QString))) ;
QSignalMapper can emit either mapped(const QString & text) or mapped(int i) signals. The type is defined by setMapping(QObject * sender, int id) or setMapping(QObject * sender, const QString & text).
That led to confusion probably by autocompletion in
connect (signalMapper, SIGNAL(mapped(int)), this, SLOT(pidOut(QString)));
The types of signal and slot must be the same for connection.
You set string mapping (QString &id), so the signal in the connection should be QString:
connect (signalMapper, SIGNAL(mapped(QString)), this, SLOT(pidOut(QString)));
Update
After deeper review of the code flow I suspect that you wanted to connect mapper to registerProcess() slot instead of pidOut(). In that slot you can have as an argument QString id that was passed to signalMapper in setMapping() call. That is the purpose of using QSignalMapper.
However, beside that id it is not possible to extract console pointer, since in that case sender() is signalMapper object. If it is the case, QSignalMapper cannot help you here. You should use direct connection of console and this on readReady (of course with slot of this with void argument as readReady()). To get the string id in that slot it is possible to use simple QMap<QProces*, QString> map stored as a Test class member.
// addProcessToList(...):
map[console] = id;
//registerProcess():
QString id = map[console];
//processFinished(...):
map.remove(console);
By the way, it is not needed to created a new instance of QSignalMapper for each map item.
I'm making a program that uses lots of timer and, at intervals of 4 seconds, does an online post to a php script.
I'm coding in QtCreator 5.1. I use classes just like the ones below.
The one below just populates a task list, but throughout the course of 8 to 12 hours, the memory that the program takes up just keep rising and rising gradually.
What am I doing wrong while using this class?
I have to be able to keep posting online like I already am, about every 4 to 8 seconds.
Here's a simple class for taking care of one of my processes:
Header file: tasklistprocess.h
#ifndef TASKLISTPROCESS_H
#define TASKLISTPROCESS_H
#include <QThread>
#include <QtCore>
#include <QNetworkRequest>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QListWidget>
#include <QTabWidget>
#include "globalhelper.h"
#include "securityinfo.h"
class TaskListProcess : public QThread
{
Q_OBJECT
public:
explicit TaskListProcess(QListWidget *obj_main, QTabWidget *tabs_main, QString user, QObject *parent = 0);
signals:
void upTaskStorage(int key,QHash<QString,QString> item);
private:
GlobalHelper gh;
Securityinfo sci;
QNetworkAccessManager *nam;
QNetworkRequest request;
QByteArray data;
// this is the disposable params for reusage through out the class
QUrlQuery params;
QString post_data;
QString user_name;
QTimer *tasklist_tmr;
bool get_task_list;
QListWidget *obj;
QTabWidget *tabs;
private slots:
void setTaskList();
void replyFinished(QNetworkReply *reply);
void sendPost(QString file_name, QUrlQuery params);
};
#endif // TASKLISTPROCESS_H`
Source file: tasklistprocess.cpp
#include "tasklistprocess.h"
TaskListProcess::TaskListProcess(QListWidget *obj_main, QTabWidget *tabs_main, QString user, QObject *parent) :
QThread(parent)
{
user_name = user;
get_task_list = false;
obj = obj_main;
tabs = tabs_main;
tasklist_tmr = new QTimer(this);
connect(this,SIGNAL(started()),this,SLOT(setTaskList()));
connect(tasklist_tmr,SIGNAL(timeout()),this,SLOT(setTaskList()));
nam = new QNetworkAccessManager(this);
request.setHeader(QNetworkRequest::ContentTypeHeader,"application/x-www-form-urlencoded");
request.setRawHeader( "User-Agent" , "Mozilla Firefox" );
// here we connect up the data stream and data reply signals
connect(nam, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
}
void TaskListProcess::setTaskList()
{
qDebug() << "Your task list was set";
bool in = false;
if(!(tasklist_tmr->isActive()))
{
tasklist_tmr->start(10000);
in = true;
}
if(!(get_task_list))
{
params.clear();
params.addQueryItem("user_name", user_name);
params.addQueryItem("logged_in", "1");
sendPost("getTaskList.php",params);
get_task_list = true;
}
else
{
if(post_data.contains("|*|"))
{
//here i retrieve a piece of information from a php script which is stored in a custom string format
// here we clear the list for the new data to be put in
if(obj->count()>0)
{
obj->clear();
}
int key = 0;
foreach(QString inner_task,post_data.split("|*|"))
{
QHash<QString,QString> task_cont;
//qDebug() << " ";
if(inner_task.contains("*,*"))
{
foreach(QString task_val,inner_task.split("*,*"))
{
if(task_val.contains("*=*"))
{
QStringList key_pairs = task_val.split("*=*");
task_cont.insert(key_pairs[0],key_pairs[1]);
if(key_pairs[0] == "tt")
{
QString val_in;
if(key_pairs[1].length()>10)
{
// this sets the title to the shortened version
// if the string length is too long
val_in = key_pairs[1].left(10) + "....";
}
else
{
val_in = key_pairs[1];
}
obj->addItem("Task :" + QString::fromUtf8(key_pairs[1].toStdString().c_str()));
}
}
}
}
//task_storage.insert(key,task_cont);
emit upTaskStorage(key,task_cont);
key ++;
}
}
get_task_list = false;
}
// here we're checking to see if they are looking at the task tab so it doesn't keep changing
// back and forth between the tabs
bool change = true;
if(tabs->currentIndex() != 0)
{
change = false;
}
if(change)
{
tabs->setCurrentIndex(0);
}else if(in)
{
tabs->setCurrentIndex(0);
}
}
void TaskListProcess::replyFinished(QNetworkReply *reply)
{
if (reply->error() != QNetworkReply::NoError) {
qDebug() << "Error in" << reply->url() << ":" << reply->errorString();
return;
}
QString data = reply->readAll().trimmed();
post_data = data;
if(get_task_list)
{
setTaskList();
}
}
void TaskListProcess::sendPost(QString file_name, QUrlQuery params)
{
post_data = "";
QUrl url(sci.getHost() + file_name);
url.setQuery(params);
data.clear();
data.append(params.toString().toUtf8());
request.setUrl(url);
nam->post(request, data);
}
From the Qt docs http://qt-project.org/doc/qt-5.1/qtnetwork/qnetworkaccessmanager.html
Note: 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.
I would suggest calling reply->deleteLater() in your replyFinished() method.
You should call deleteLater() for an QNetworkReply object after use.
Note: 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.
More information here: http://harmattan-dev.nokia.com/docs/library/html/qt4/qnetworkaccessmanager.html
Thank you everyone for the hel.
It was very simply the "deleteLater()" on the reply in the
void replyFinished(QNetworkReply *reply)
{
}
and it should have looked like this
void replyFinished(QNetworkReply *reply)
{
// after all of your processing
reply->deleteLater();
}
it was such a small problem but one that drove me crazy for a long time so i hope this helps
I am trying to save cookies that are produced by my app to disk location such as C:\Users\Username\AppData\Local\MyCompany\MyApp. I have implemented a webview and have pretty much finished coding my simple browser the final thing to do is save cookies.
I am can qDebug() the cookies I get from the webapp and they show the cookies are formed correctly but I am a)unsure where to go from there and b) not 100% sure on how to make a subclass of the cookiejar class?
Below I create my cookiejar object in my MainWindow constructor
view = new QWebView(this);
jar = new QNetworkCookieJar;
view->page()->networkAccessManager()->setCookieJar(jar);
And in my replyfinished slot I can see the cookie contained in the reply and I attempt to save it but nothing happens and I receive no run time errors. There isn't a great deal of stuff out there on this and have seen a few posts where the instruction was to make a subclass QNetworkCookieJar but have not made a subclass in Qt/C++ before.
Is there a simple way to store cookies, I am not looking for anything fancy. The cookies just make sure some check boxes are ticked on the login page.
// SLOT that accepts the read data from the webpage
void MainWindow::slotReplyFinished(QNetworkReply *reply){
if(reply->isFinished()){
QVariant variantCookies = reply->header(QNetworkRequest::SetCookieHeader);
QList<QNetworkCookie> cookies = qvariant_cast<QList<QNetworkCookie> >(variantCookies);
qDebug() << "Cookies reply: " << cookies;
QNetworkCookie cookie; //Create a cookie
jar = new QNetworkCookieJar;
//view->page()->networkAccessManager()->setCookieJar(jar);
jar->setCookiesFromUrl(cookies, reply->request().url());
//qDebug() << "Saved cookies: " << jar->getAllCookies();
}
qDebug() << "Network reply: " << reply->errorString() << reply->error() << reply->request().url();
}
You will need to subclass QNetworkCookieJar and in that class you should implement your own persistent storage.
class MyNetworkCookieJar : public QNetworkCookieJar {
public:
bool saveCookiesToDisk() {
// .. my implementation
return true; // if i did
}
bool loadCookiesFromDisk() {
// .. load from disk
return false; // if unable to.
}
}
The sample application from Qt project implements a persistent cookie store, it could be a good starting point for you: http://qt.gitorious.org/qt/qt/trees/4.8/demos/browser
look at cookiejar.h and cookiejar.cpp
Base of qt example, http://qt.gitorious.org/qt/qt/trees/4.8/demos/browser, i wrote this class that save and use one cookie for me. Perhaps it helps you too. Note that it just save one cookie and not list of cookies.
#include "cookiejar.h"
CookieJar::CookieJar(QObject *parent)
: QNetworkCookieJar(parent)
, m_loaded(false)
{
}
void CookieJar::load()
{
if (m_loaded)
return;
QSettings settings;
settings.beginGroup(QLatin1String("cookies"));
QList<QNetworkCookie> savedCookies = QNetworkCookie::parseCookies(settings.value("cookies").toByteArray());
for (int j = 0; j < savedCookies.count(); j++)
insertCookie(savedCookies.at(j));
m_loaded = true;
emit cookiesChanged();
}
void CookieJar::save()
{
if (!m_loaded)
return;
QList<QNetworkCookie> cookies = allCookies();
QSettings settings;
settings.beginGroup(QLatin1String("cookies"));
settings.setValue("cookies", cookies[0].toRawForm());
}
QList<QNetworkCookie> CookieJar::cookiesForUrl(const QUrl &url) const
{
// This function is called by the default QNetworkAccessManager::createRequest(),
// which adds the cookies returned by this function to the request being sent.
CookieJar *that = const_cast<CookieJar*>(this);
if (!m_loaded)
that->load();
return QNetworkCookieJar::cookiesForUrl(url);
}
bool CookieJar::setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url)
{
if (!m_loaded)
load();
QNetworkCookieJar::setCookiesFromUrl(cookieList, url);
save(); //Save cookie permanently in setting file.
emit cookiesChanged();
return true;
}
#Musa's answer is good but it only saves one cookie. I recommend using the Qt folk's implementation from the old qmlviewer located here: http://code.qt.io/cgit/qt/qt.git/tree/tools/qml/qmlruntime.cpp?h=4.7#n438
Here's the code:
class PersistentCookieJar : public QNetworkCookieJar {
public:
PersistentCookieJar(QObject *parent) : QNetworkCookieJar(parent) { load(); }
~PersistentCookieJar() { save(); }
virtual QList<QNetworkCookie> cookiesForUrl(const QUrl &url) const
{
QMutexLocker lock(&mutex);
return QNetworkCookieJar::cookiesForUrl(url);
}
virtual bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url)
{
QMutexLocker lock(&mutex);
return QNetworkCookieJar::setCookiesFromUrl(cookieList, url);
}
private:
void save()
{
QMutexLocker lock(&mutex);
QList<QNetworkCookie> list = allCookies();
QByteArray data;
foreach (QNetworkCookie cookie, list) {
if (!cookie.isSessionCookie()) {
data.append(cookie.toRawForm());
data.append("\n");
}
}
QSettings settings;
settings.setValue("Cookies",data);
}
void load()
{
QMutexLocker lock(&mutex);
QSettings settings;
QByteArray data = settings.value("Cookies").toByteArray();
setAllCookies(QNetworkCookie::parseCookies(data));
}
mutable QMutex mutex;
};