QUrl construction piece by piece - c++

I tried to construct QUrl piece by piece:
QUrl url{"https://host.org/path"};
url.setScheme("http");
url.setPort(81);
url.setUserName("user");
url.setPassword("password");
url.setHost("server.com");
QUrlQuery urlQuery;
urlQuery.setQueryItems({{"key1", "value1"}, {"key2", "value2"}, {"key3", "value3"}});
url.setQuery(urlQuery);
url.setFragment("fragment");
//url.setPath("dir/file.htm");
qDebug() << url;
Output (password is accidentally missed on the way):
QUrl("http://user#server.com:81/path?key1=value1&key2=value2&key3=value3#fragment")
First of all, if QUrl is default-constructed, then using setters I can't add anything into it at all.
In above code if I uncomment last but one line, then output became QUrl(""). That is QUrl::setPath clean up the whole internal representation of QUrl instance.
Are both mentioned behaviours normal? Or are they the bugs?
I use Qt 5.7.1.
It seems, that simple string concatenation is much less bug prone.

To answer at least some of your questions:
qDebug() << url; eats the password and that is a good thing. Why? Because qDebug and friends are often used to write log files and having password in log files or even on the console is bad, really bad. So the default is that qDebug eats the password. If you need it call qDebug() << url.toString(). You have been warned ;)
Why QUrl url("server.com"); url.setScheme("http"); results in http:server.com is because in QUrl url("server.com"); "server.com" is parsed and recognized as the path and not the host.
I am using 5.7.0 and using a default constructed QUrl with setters work fine:
QUrl url;
url.setScheme("http");
url.setHost("server.com");
qDebug() << url; // QUrl("http://server.com")
The reason why setPath makes the URL null is because it is ill-formed. The path must start with an slash. Use url.setPath("/dir/file.htm");.
Cheers and keep fighting!

Related

QT: How to download from url while pressing a button

I have this code:
QNetworkAccessManager man;
QNetworkRequest req(QUrl("URL"));
QString ua("HttpRequestDemo/0.1 (Win64) Qt/5.14.0");
req.setHeader(QNetworkRequest::UserAgentHeader, QVariant(ua));
QNetworkReply* reply = man.get(req);
QObject::connect(reply){
QByteArray read = reply->readLine();
QFile out("file.txt");
out.open(QIODevice::WriteOnly|QIODevice::Text);
out.write(read);
out.close();
})
This works on the main.cpp file, using the QCoreApplication, but I want to use the QApplication and download a specific data while pressing a button.
I put the same code on the on_pushButton_clicked() in the mainwindow.cpp file and it didn't even generate the file from the url.
The problem is that man and req go out of scope and are destroyed as soon as your on_pushButton_clicked() function returns, at which point the request probably hasn't even been sent yet.
You need to make sure that these objects outlive the current scope, either by making them members of the window class, or by allocating them on the heap and setting some QObject (maybe also the window class) as the parent.
The problem is that if you put the same code in a method like X you make QNetworkAccessManager a local variable that will be removed instantly that the connection is asynchronous. The solution is to make QNetworkAccessManager an attribute of the class.
*.h
private:
QNetworkAccessManager man;
*.cpp
void Klass::on_pushButton_clicked(){
QNetworkRequest req(QUrl("URL"));
QString ua("HttpRequestDemo/0.1 (Win64) Qt/5.14.2");
req.setHeader(QNetworkRequest::UserAgentHeader, QVariant(ua));
QNetworkReply* reply = man.get(req);
connect(reply, &QNetworkReply::finished, [&]() {
QByteArray read = reply->readAll();
QFile out("file.txt");
out.open(QIODevice::WriteOnly|QIODevice::Text);
out.write(read);
out.close();
reply->close();
reply->deleteLater();
})
}
If you are planning on potentially queuing very many downloads, I strongly recommend using libcurl in your Qt app. I was using QNetworkAccessManager to down 100+ financial quote files, and it would fail downloading ~ 1/3 of the time, and take a while to download. I switched to libcurl, and after figuring out how to get my crypto root certificates setup for https, it runs much faster, and almost never fails. I run it as a dll.
And yes, you will need to make sure the network manager, whether QNetworkManager or curl, doesn't go out of scope upon exiting the button handler. A more conventional pattern, although not necessarily better, is to either have a pointer to e.g. QNetworkManager in your parent class, and new it, or use a std::unique_ptr and std::make_unique (purportedly safer). Creating large objects on the stack can cause problems (in the old days, dare I say, stack overflows), and so is usually done on the heap. In this case, it's not very big, so it doesn't really matter. Alternatively, a form creating big objects might itself be created on the heap.

Json QtNetworkReply to QByteArray

I need to do a request in Qt/c++ to get a JSON file, and then parse it and fill my object.
The request seems good, and "it looks like" my QtNetworkReply reply is filled.
But after many attempts, I still don't understand how can I convert it into a QbyteArray (I don't even know if it's the right thing to do...), for being able to convert it into my class.
Here's my code :
QNetworkAccessManager networkManager;
QUrl url("https://api.myjson.com/bins/uvki"); //url from a free json host
QNetworkRequest request;enter code here
request.setUrl(url);
QNetworkReply* reply = networkManager.get(request);
QByteArray reponse;
if (reply == NULL)
{
std::cout << "Damn" << std::endl;
exit(2);
}
reponse = reply->readAll();
if (reponse == NULL)
{
std::cout << "i hate you" << std::endl;
exit(1000);
}
I might have done some stupid stuff, I only have 2 days of c++
Can you tell me how I can convert my "reply" into my "reponse"?
The answer provided by #MichaelBoone is correct.
In addtion, with C++11, you can simplify the code by using Qt 5's QObject::connection syntax and a lambda function
QJsonDocument document;
QNetworkReply* pReply = networkManager.get(request);
connect(reply, &QNetworkReply::finished, [=](){
// the reply will return here
QByteArray response = pReply->readAll();
document = QJsonDocument::fromBinaryData(response);
});
Qt 5's connections syntax has the advantage of compile-time verification of the connection, which is not present when using the SIGNAL and SLOT macros.
You have to connect the finished() signal from reply object, or from the NetworkManager to get the results. You will also need to make *reply a class member, or you won't be able to access it within your handler SLOT.
QNetworkReply* reply = networkManager.get(request);
connect(reply, SIGNAL(finished()), this, SLOT(YourFunctionHere()));
void YourFunctionHere(){
//handle the data
}
QNetworkReply is a non-blocking function, like most QT Network functions, it is asynchronous. By the time you are reaching your conditional if statement to check the reply, it hasn't yet received a response from the network.
As far as handling the download afterwards, you are correct in using a QByteArray.
QByteArray QIODevice::readAll()
This is an overloaded function.
Reads all available data from the device, and returns it as a
QByteArray.
From there you use QJsonDocument.
QJsonDocument QJsonDocument::fromBinaryData(const QByteArray & data,
DataValidation validation = Validate)
Creates a QJsonDocument from data.
Edit - Sorry I don't have the reputation to comment, but I feel The answer provided by TheDarkKnight lends itself better to the one-off nature of a "Reply" and is less encumbered by having to create a new slot. lambdas are just very cool, and the compile time verification is nice.

store input of line edit to string qt

I'm a beginner.I am making a simple gui program using qt in which you enter a url/website and that program will open that webpage in chrome.I used line edit in which user enters url and i used returnPressed() slot, but the problem is (it might sound stupid) that i don't know how to take the input by user and store it in a string so that i can pass that string as parameter to chrome.Is im asking something wrong.also tell me how can i save input to a txt file, i know how to do that in a console program.Is this process is same with others like text edit etc.
My mainwindow.cpp:
QString exeloc = "F:\\Users\\Amol-2\\AppData\\Local\\Google\\Chrome\\Application\\chrome.exe";
void MainWindow::on_site_returnPressed()
{
QString site;
getwchar(site);
QString space=" ";
QString result = exeloc + space + site;
QProcess::execute(result);
}
What im doing wrong.
thanks
You've got your approach slightly wrong, I can see where you're coming from though. It's actually a lot more simple than you're trying, Qt has a QDesktopServices class that allows you to interact with various system items, including open urls in the browser. There's documentation on it here.
QLineEdit has a text() function that will return a QString. So you can do something like this:
QString site = ui->site->text();
You don't have to use QProcess to open a web site in a browser. You can use QDesktopServices::openUrl static function.
Like this:
QString site = ui->site->text();
QUrl url(site);
QDesktopServices::openUrl(url);
Remember to include QDesktopServices and QUrl headers:
#include <QDesktopServices>
#include <QUrl>

how can I create QUrl from encoded url

I have a QString which contains encoded URL, that means, the url as you would insert it to browser, for example:
QString string = "http://domain.tld/index.php?some%20encoded&another%20encoded";
When I create QUrl like
QUrl(string);
it randomly decides what should be encoded and what shouldn't, in this case it leave "?" and "&" decoded, but AGAIN encode the percent symbol. So the target php application receive "some%20encoded" instead of "some encoded".
This seems to be some feature of QT when it automatically attempt to parse "and fix" url. This feature can be disabled by calling
QUrl(string, QUrl::StrictMode);
which works perfectly in qt 5+ but in qt 4, despite it compiles, it has same behaviour as if I didn't provide StrictMode parameter. How can I create url from string I encoded myself and which needs no further encoding?
What's you are looking for is
QUrl QUrl::fromEncoded (const QByteArray &input ) [static]
QUrl doc

QNetworkRequest and QUrl encoding c++

Im trying to connect to gmail and consume the atom file.
Im having problem with passwords that contain !#$%^&* chars.
pNetworkManager = new QNetworkAccessManager(this);
connect(pNetworkManager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(result(QNetworkReply*)));
Settings settings;
settings.load();
QString url;
url.append("https://");
url.append(settings.getUserName());
url.append(":");
url.append(QUrl::toPercentEncoding(settings.getPassword()));
url.append("#mail.google.com/mail/feed/atom");
pNetworkManager->get(QNetworkRequest(QUrl(url.toUtf8())));
I get reply "Protocol "" is unknown"
Qt 4.8
How is this done properly
Why don't you construct a QUrl directly?
QUrl url("https://mail.google.com/mail/feed/atom");
url.setUserName(settings.getUserName());
url.setPassword(settings.getPassword());
pNetworkManager->get(QNetworkRequest(url));
It handles all the necessary encoding and QNetworkRequest takes it directly anyway. No need to mess with string encodings.