Want to understand the difference in code between the MainWindow
and the main.cpp
. Specifically, how a chunk of code written exclusively in the main.cpp
needs to be modified to be part of the mainwindow.cpp
and mainwindow.h
.
As an example, I am trying to modify the code from this fine answer to work in MainWindow
.
main.cpp
#include <QtWidgets>
#include <QtNetwork>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//setup GUI (you could be doing this in the designer)
QWidget widget;
QFormLayout layout(&widget);
QLineEdit lineEditName;
QLineEdit lineEditGender;
QLineEdit lineEditRegion;
auto edits = {&lineEditName, &lineEditGender, &lineEditRegion};
for(auto edit : edits) edit->setReadOnly(true);
layout.addRow("Name:", &lineEditName);
layout.addRow("Gender:", &lineEditGender);
layout.addRow("Region:", &lineEditRegion);
QPushButton button("Get Name");
layout.addRow(&button);
//send request to uinames API
QNetworkAccessManager networkManager;
QObject::connect(&networkManager, &QNetworkAccessManager::finished,
[&](QNetworkReply* reply){
//this lambda is called when the reply is received
//it can be a slot in your GUI window class
//check for errors
if(reply->error() != QNetworkReply::NoError){
for(auto edit : edits) edit->setText("Error");
networkManager.clearAccessCache();
} else {
//parse the reply JSON and display result in the UI
QJsonObject jsonObject= QJsonDocument::fromJson(reply->readAll()).object();
QString fullName= jsonObject["name"].toString();
fullName.append(" ");
fullName.append(jsonObject["surname"].toString());
lineEditName.setText(fullName);
lineEditGender.setText(jsonObject["gender"].toString());
lineEditRegion.setText(jsonObject["region"].toString());
}
button.setEnabled(true);
reply->deleteLater();
});
//url parameters
QUrlQuery query;
query.addQueryItem("amount", "1");
query.addQueryItem("region", "United States");
QUrl url("http://uinames.com/api/");
url.setQuery(query);
QNetworkRequest networkRequest(url);
//send GET request when the button is clicked
QObject::connect(&button, &QPushButton::clicked, [&](){
networkManager.get(networkRequest);
button.setEnabled(false);
for(auto edit : edits) edit->setText("Loading. . .");
});
widget.show();
return a.exec();
}
edit
added the timer part of the same answer; please demonstrate how this version with the timer can done as well
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, [&](){
networkManager.get(networkRequest);
button.setEnabled(false);
for(auto edit : edits) edit->setText("Loading. . .");
});
timer.start(60000); //60000 msecs = 60 secs
I struggle with modifying the networkManager
as class members, how to structure the code and how to replace the lambda functions.
If someone can provide all the required modifications for me to gain a better understanding that would be great.
You would want to separate out the user interface and the controller (business logic) into separate classes.
The body of
main()
instantiates the ui and the controller and connects them. A timer that fetches new results every 5 seconds. The timer could be rolled into theController
, too - I show it separated out as an example of adding functionality to an existing class without modifying it.main.cpp
The controller knows nothing of the user interface, and deals with processing the requests only. It emits a
busy
signal every time a request starts being processed.If you wanted to provide better feedback for multiple active requests, the
busy
signal would need to be emitted only when there were no requests pending and a new one is added, and anidle
signal would be emitted when the last request has finished and there are no more pending ones.controller.h
controller.cpp
The user interface knows nothing of any business logic, it provides an API that's sufficient for the business logic to use it. It can be in one of three states:
Normal
state where results are visible,Loading
state where a busy feedback is shown, andError
state where error information is shown. ThesetFields
slot returns the state toNormal
.mainwindow.h
mainwindow.cpp
You can put all of this code in constructor of your
QMainWindow
and retaining lambda functions as-is.Another more clean way would be transforming those lambda functions into private slots. Using this way, you should define
networkManager
as a class member of yourQMainWindow
class and also it should be allocated in heap memory not stack. To get this done, just define aQNetworkManager*
class member and initialize it in yourQMainWindow
constructor.Once it's been initialized, you can use it in all slots of your
QMainWindow
class.A simple rule of thumb is: all shared variables among lambda functions and the main scope should be class members in this way.
The code. (I tested it and works fine)
main.cpp
mainwindow.cpp
mainwindow.h