How to read data from QNetworkReply being used by QWebPage? - qt

I use QWebPage to download a webpage as well as all its resources. At the same time I'd like to get hold on raw data being downloaded by Qt during this process. Doing this by reading data from QNetworkReply in void QNetworkAccessManager::finished(QNetworkReply * reply)
signal is not a good solution as data could have been already read by QWebPage itself. This is because
QNetworkReply is a sequential-access
QIODevice, which means that once data
is read from the object, it no longer
kept by the device.
according to detailed description of QNetworkReply.
However QWebPage can be configured to use custom QNetworkAccessManager with overriden createRequest method
QNetworkReply * QNetworkAccessManager::createRequest ( Operation op, const QNetworkRequest & req, QIODevice * outgoingData = 0 )
I think the right solution would be to create a proxy for QNetworkReply and return it in the createRequest method. This proxy should allow for reading data from reply as is the case with the original QNetworkReply (so that QWebPage could read data from it) but at the same time this proxy should allow for reading data by other objects after it have been read by QWebPage. In other words we need tee for QNetworkReply's IODevice base class.
How to write this proxy?

It looks like someone has already wanted the same and wrote a proxy for the QNetworkReply.

Related

Singleton QNetworkAccessManager not directing the response to desired form

In a small Qt application, I have a NetworkAccessManager(NAM) wrapped in a singleton class. NAM object of this class is used by four independent form classes. Each form class fires a request to a separate php (and php takes at least 3 seconds with sleep(3)).
Now there are two cases:
Case I: When NAM, for each form, is connected to form's slot IN CONSTRUCTOR.
In this case, when i send requests from all four forms simultaneously; all the responses are directed to just one form (the one that was first in firing the request) and not to the form that requested it.
Case II: When NAM is connected (for firing request) and disconnected (when response is received) to form's slot IN FUNCTIONS (and not constructor).
In this case, when i send requests from all four forms simultaneously (i.e. in less than 3 seconds); then only the first response is returned and the rest never returned. This is in accordance with the code as singleton NAM was disconnected as soon as it received the reply for the first request. Therefore other requests could not be processed.
All form classes are identical and use the same code. Here is the code (Case II specific):
void Request2::slotStartRequest(){
m_Req2 = NetworkAccessManager::getInstance();
connect(m_Req2, SIGNAL(finished(QNetworkReply*)), this, SLOT(slotReplyFinished(QNetworkReply*)));
QString postData = "value=222";
m_Req2->post(QNetworkRequest(QUrl(path)), postData.toUtf8());
}
void Request2::slotReplyFinished(QNetworkReply *reply){
disconnect(m_Req2, SIGNAL(finished(QNetworkReply*)), this, SLOT(slotReplyFinished(QNetworkReply*)));
ui->TEdit_Req2->setText(reply->readAll());
reply->deleteLater();
}
Though the code for both cases are very similar. This code is Case II specific. For Case I, only change is that 'connect' and 'getInstance' codes are put in constructor (and there is no 'disconnect').
The code will work fine with one NetworkAccessManager object per form class.
But how do I achieve the desired behaviour (with one NAM for whole application) that response is directed only to the form that requested it and also the requests for other forms should not be affected ?
The NAM function post() creates a new QNetworkReply instance to manage that request. It is possible to connect directly to QNetworkReply signals instead of signals of QNetworkAccessManager:
void Request2::slotStartRequest(){
QString postData = "value=222";
QNetworkAccessManager *nam = NetworkAccessManager::getInstance();
m_Reply = nam->post(QNetworkRequest(QUrl(path)), postData.toUtf8());
connect(m_Reply, SIGNAL(finished()), this, SLOT(slotReplyFinished()));
}
Now only this instance of QNetworkReply *m_Reply can call the signal of this. Note that there is no QNetworkReply *reply argument in the finished() signal:
void Request2::slotReplyFinished(){
ui->TEdit_Req2->setText(m_Reply->readAll());
m_Reply->deleteLater();
}
My suggestion (what I do) is to keep track of which object made the request on the manager, perhaps update your manager to take in the object pointer
m_Req2->post(QNetworkRequest(QUrl(path)), postData.toUtf8(), this);
Then on the Manager, use that pointer and use the QMetaObject to invoke the method, instead of emitting a signal: http://doc.qt.io/qt-5/qmetaobject.html#invokeMethod.
So you get something like this, on the Manager
QMetaObject::invokeMethod(obj, "slotReplyFinished", Qt::DirectConnection,
Q_ARG(QNetworkReply*, reply));
(not tested, but you should get the idea)
[edit: fixed code]

Qt5 QSerialPort write data

How I can write data in serial port, with delay between send's messages?
This is my code:
void MainWindow::on_pushButton_Done_clicked()
{
if(sport->isOpen()){
sport->clear();
QString cmd = Phase+Mode;
//Write Stop
sport->write("stop!", 5);
//Write Mode
sport->write(cmd.toStdString().c_str(), cmd.toStdString().length());
//Write Speed
sport->write(Speed.toStdString().c_str(), Speed.toStdString().length());
//Write Direction
sport->write(Direction.toStdString().c_str(), Direction.toStdString().length());
//Run
sport->write("start!", 6);
}
}
My device receives an error message when I call this function.
Thank you.
2 options:
use waitForBytesWritten to ensure the bytes are written and then a short sleep
however this will block the thread and will block the gui
the other is using a QTimer to trigger another slot a few times and a field that will indicate what needs to be sent
Looks like you are trying to program some step motor controller or something similar.
Usually in such controllers you should wait for controller response to verify that command was processed properly.
It looks like that your design of code is very bad. Move everything related with this controller to separate class, which has set of slots, something like: starRotateLeftWithSpeed(double). Code will be cleaner and it will be easy to use thread if you decide to use methods like waitForBytesWritten proposed in another answer.
Definitely you should read controller manual more carefully.

Serialize QFileInfo

I have a list of files and I'd like to serialize the file info for every file and send it through socket.
I saw it's possible to serialize like this for example:
QByteArray ba;
QDataStream ds(&ba);
ds << my_stringlist;
QByteArray ba;
QDataStream ds(&ba);
ds >> my_stringlist;
but I couldn't find support for QFileInfo. Is it possible to serialize this Qt data type?
Is there any way to get an easy full serialization of this type or I just need to break up the data into primitive units?
There is no standard way to do that. You can define your custom QDataStream operators as showed in this answer, or you can write your own functions to convert QFileInfo to QVariant and back, and use QVariant serialization. In all these ways you need to break up the data into primitive units, yes.
However I think serializing QFileInfo is pointless. You should use QFileInfo::absoluteFilePath() to get the file's path and serialize that path instead. A new QFileInfo object can be easily constructed from that path if your receiving code is running on the same machine.
If your code is running on the other machine, you couldn't use deserialized QFileInfo even if it would be possible. It's because QFileInfo may or may not store information about file. When you run e.g. QFileInfo::isFile, it may make a request to the underlying file system.
So I think it's better to request all required data from QFileInfo add send this data instead of sending QFileInfo. Or you can just send the absolute file path.

Is it possible to control download speed using QNetworkAccessManager

Can we restrict QNetworkAccessManager from consuming whole bandwidth, by restricting the download speed, as we do see such options available with almost every download manager?
This is not possible out of the box. But have a look at the Qt Torrent Example, especially the class RateController (ratecontroller.h | ratecontroller.cpp). This class does almost what you want by controlling not only one but a set of connections.
However, this rate controller is operating on QTcpSockets (to be exact on PeerWireClients), so you need to change the type of the "peers" to be QIODevice, which I hope isn't that hard, since PeerWireClient inherits from QTcpSocket, which itself inherits from QIODevice:
// old
void addSocket(PeerWireClient *socket);
// new
void addDevice(QIODevice *device);
(Note that the RateController from the Torrent example controlls both upload and download, but you only need to control the download rate. So you can remove unnecessary code.)
Then you need to make requests made by your QNetworkAccessManager use this rate controller. This can be done by reimplementing QNetworkAccessManager and overwriting (extending) the method QNetworkAccessManager::createRequest, which will be called whenever a new request gets created. This method returns the QNetworkReply* (which inherits from QIODevice*) where the download will be read from, so telling the rate controller to control this device will limit the download rate:
QNetworkReply *MyNetworkAccessManager::createRequest(
QNetworkAccessManager::Operation op,
const QNetworkRequest &req,
QIODevice *outgoingData)
{
// original call to QNetworkAccessManager in order to get the reply
QNetworkReply *reply = QNetworkAccessManager::createRequest(op, req, outgoingData);
// add this reply (which is a QIODevice*) to the rate controller
rateController.addDevice(reply);
return reply;
}
You will not have to subclass QNetworkAccessManager if you already know the pieces of code where you actually perform requests. The methods get() and post() return a QNetworkReply* which you can also just add to the rate controller. (But this way, you manually do this outside of the manager, which is doesn't fulfill the concept of information/implementation hiding, in this case the fact that downloads are rate-controlled.)

Using QTWebKit to display a website stored in memory

Currently I have my HTML, JS, CSS, graphics, etc stored locally on hard disk and access them using QWebFrame::SetUrl( QUrl::fromLocalFile( "appFolder\html\index.html" )). At some point I am going to need to encrypt the locally stored files so I'm looking for a way to either decrypt them as they're requested or to decrypt them all into memory and access them that way.
I know I can use QWebFrame::setContent( htmlData ) to load the HTML from memory so I can load the encrypted HTML file, decrypt it in memory and then display it that way, but how would I go about the other data (JS, CSS, graphics, etc) which is currently stored in subfolders?
Alternatively, is there a way I can intercept requests for access to all the HTML, JS, CSS, etc files and decrypt them as they're loaded?
By using my own NetworkAccessManager I can intercept calls to createRequest so I can see when each file is being loaded, but I can't see how to use this to decrypt the data on the fly. I can also connect a slot function to the finished(QNetworkReply*) signal, but at that point the data has already been read - the QIODevice's current position is pointing to the end of the file.
I'd be very grateful for any advice or pointers in the right direction.
I think in your case the best solution is to inherit QNetworkReply class and use this new class in reimplemented QNetworkAccessManager::createRequest() function.
In general, you should reimplement next virtual functions of QNetworkReply:
bytesAvailable(), readData(char *data, qint64 maxSize), close(), abort().
For example, readData should be the folowing:
qint64 NetworkReplyEx::readData(char *data, qint64 maxSize)
{
return m_buffer.read(data, maxSize);
}
where m_buffer is already decrypted data.
Also you need to add all necessary logic in this class to get encrypted data, decrypt this data...
In the end you should manually emit finished() signal inside new class, so QWebView or other related class will get decrypted html.

Resources