Want to understand the difference in code between the MainWindow
and the main.cpp
. Specifically, how a chunk of code written exclusively in the m
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 the Controller
, too - I show it separated out as an example of adding functionality to an existing class without modifying it.
// https://github.com/KubaO/stackoverflown/tree/master/questions/into-mainwin-39643510
#include "mainwindow.h"
#include "controller.h"
int main(int argc, char *argv[])
{
QApplication app{argc, argv};
MainWindow ui;
Controller ctl;
QTimer timer;
timer.start(5*1000);
QObject::connect(&timer, &QTimer::timeout, &ctl, &Controller::get);
QObject::connect(&ctl, &Controller::busy, &ui, [&]{ ui.setState(MainWindow::Loading); });
QObject::connect(&ui, &MainWindow::request, &ctl, &Controller::get);
QObject::connect(&ctl, &Controller::error, &ui, [&]{ ui.setState(MainWindow::Error); });
QObject::connect(&ctl, &Controller::values, &ui, &MainWindow::setFields);
ui.show();
return app.exec();
}
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 an idle
signal would be emitted when the last request has finished and there are no more pending ones.
#ifndef CONTROLLER_H
#define CONTROLLER_H
#include
class Controller : public QObject {
Q_OBJECT
QNetworkAccessManager manager{this};
QNetworkRequest request;
Q_SLOT void onReply(QNetworkReply *);
public:
explicit Controller(QObject * parent = nullptr);
Q_SLOT void get();
Q_SIGNAL void busy();
Q_SIGNAL void error(const QString &);
Q_SIGNAL void values(const QString & name, const QString & gender, const QString & region);
};
#endif // CONTROLLER_H
#include "controller.h"
Controller::Controller(QObject *parent) : QObject(parent)
{
QUrlQuery query;
query.addQueryItem("amount", "1");
query.addQueryItem("region", "United States");
QUrl url("http://uinames.com/api/");
url.setQuery(query);
request = QNetworkRequest(url);
connect(&manager, &QNetworkAccessManager::finished, this, &Controller::onReply);
}
void Controller::onReply(QNetworkReply * reply) {
if (reply->error() != QNetworkReply::NoError) {
emit error(reply->errorString());
manager.clearAccessCache();
} else {
//parse the reply JSON and display result in the UI
auto jsonObject = QJsonDocument::fromJson(reply->readAll()).object();
auto fullName = jsonObject["name"].toString();
fullName.append(" ");
fullName.append(jsonObject["surname"].toString());
emit values(fullName, jsonObject["gender"].toString(), jsonObject["region"].toString());
}
reply->deleteLater();
}
void Controller::get() {
emit busy();
manager.get(request);
}
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, and Error
state where error information is shown. The setFields
slot returns the state to Normal
.
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
class MainWindow : public QWidget {
Q_OBJECT
QFormLayout layout{this};
QLineEdit lineEditName;
QLineEdit lineEditGender;
QLineEdit lineEditRegion;
QPushButton button{"Get Name"};
QLineEdit * edits[3] = {&lineEditName, &lineEditGender, &lineEditRegion};
public:
enum State { Normal, Loading, Error };
explicit MainWindow(QWidget * parent = nullptr);
Q_SLOT void setFields(const QString & name, const QString & gender, const QString & region);
Q_SLOT void setState(State);
Q_SIGNAL void request();
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent) : QWidget(parent)
{
for(auto edit : edits) edit->setReadOnly(true);
layout.addRow("Name:", &lineEditName);
layout.addRow("Gender:", &lineEditGender);
layout.addRow("Region:", &lineEditRegion);
layout.addRow(&button);
connect(&button, &QPushButton::clicked, this, &MainWindow::request);
}
void MainWindow::setFields(const QString & name, const QString & gender, const QString & region) {
setState(Normal);
lineEditName.setText(name);
lineEditGender.setText(gender);
lineEditRegion.setText(region);
}
void MainWindow::setState(MainWindow::State state) {
if (state == Normal) {
for (auto edit : edits) edit->setEnabled(true);
button.setEnabled(true);
}
else if (state == Loading) {
for (auto edit : edits) edit->setEnabled(false);
button.setEnabled(false);
}
else if (state == Error) {
for (auto edit : edits) edit->setText("Error...");
button.setEnabled(true);
}
}