来自 Qt 中 main.cpp 代码的 MainWindow
MainWindow from code from the main.cpp in Qt
想了解 MainWindow
和 main.cpp
之间的代码差异。具体来说,如何将 main.cpp
中专门编写的一段代码修改为 mainwindow.cpp
和 mainwindow.h
的一部分。
举个例子,我正在尝试修改 中的代码以在 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();
}
编辑
添加了相同答案的计时器部分;请演示这个带有计时器的版本如何完成
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
我努力将 networkManager
修改为 class 成员,如何构建代码以及如何替换 lambda 函数。
如果有人可以提供所有需要的修改,让我更好地理解,那就太好了。
您可以将所有这些代码放入 QMainWindow
的构造函数中,并按原样保留 lambda 函数。
另一种更简洁的方法是将这些 lambda 函数转换为专用槽。使用这种方式,您应该将 networkManager
定义为 QMainWindow
class 的 class 成员,并且它应该分配在堆内存中而不是堆栈中。要完成此操作,只需定义一个 QNetworkManager*
class 成员并在 QMainWindow
构造函数中对其进行初始化。
this->networkManager = new QNetworkManager(this);
初始化后,您可以在 QMainWindow
class.
的所有插槽中使用它
一个简单的经验法则是:lambda 函数和主作用域之间的所有共享变量都应该是 class 成员这样。
代码。 (我测试了它并且工作正常)
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->lineEditGender->setReadOnly(true);
ui->lineEditRegion->setReadOnly(true);
ui->lineEditName->setReadOnly(true);
networkManager = new QNetworkAccessManager(this);
connect(networkManager, &QNetworkAccessManager::finished, this, &MainWindow::onNetworkManagerFinished);
connect(ui->btnGetName, &QPushButton::clicked, this, &MainWindow::onBtnGetNameClicked);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::onNetworkManagerFinished(QNetworkReply *reply)
{
if(reply->error() != QNetworkReply::NoError){
ui->lineEditName->setText("Error");
ui->lineEditGender->setText("Error");
ui->lineEditRegion->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());
ui->lineEditName->setText(fullName);
ui->lineEditGender->setText(jsonObject["gender"].toString());
ui->lineEditRegion->setText(jsonObject["region"].toString());
}
ui->btnGetName->setEnabled(true);
reply->deleteLater();
}
void MainWindow::onBtnGetNameClicked()
{
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
networkManager->get(networkRequest);
ui->btnGetName->setEnabled(false);
ui->lineEditName->setText("Loading. . .");
ui->lineEditGender->setText("Loading. . .");
ui->lineEditRegion->setText("Loading. . .");
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QUrlQuery>
#include <QJsonDocument>
#include <QJsonObject>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void onNetworkManagerFinished(QNetworkReply* reply);
void onBtnGetNameClicked();
private:
Ui::MainWindow *ui;
QNetworkAccessManager *networkManager;
};
#endif // MAINWINDOW_H
您可能希望将用户界面和控制器(业务逻辑)分离成单独的 classes。
main()
的主体实例化 ui 和控制器并连接它们。每 5 秒获取新结果的计时器。计时器也可以滚动到 Controller
中 - 我展示它作为一个例子将它分开作为向现有 class 添加功能而不修改它的示例。
main.cpp
// https://github.com/KubaO/Whosebugn/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();
}
控制器对用户界面一无所知,只负责处理请求。每次开始处理请求时,它都会发出 busy
信号。
如果你想为多个活动请求提供更好的反馈,busy
信号只需要在没有待处理的请求并添加新请求时发出,并且 idle
当最后一个请求完成并且没有更多待处理的请求时,将发出信号。
controller.h
#ifndef CONTROLLER_H
#define CONTROLLER_H
#include <QtNetwork>
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
controller.cpp
#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);
}
用户界面对任何业务逻辑一无所知,它提供了一个足以让业务逻辑使用它的API。它可以处于三种状态之一:Normal
结果可见的状态,Loading
显示忙碌反馈的状态,以及显示错误信息的 Error
状态。 setFields
槽returns状态为Normal
.
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QtWidgets>
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
mainwindow.cpp
#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);
}
}
想了解 MainWindow
和 main.cpp
之间的代码差异。具体来说,如何将 main.cpp
中专门编写的一段代码修改为 mainwindow.cpp
和 mainwindow.h
的一部分。
举个例子,我正在尝试修改 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();
}
编辑
添加了相同答案的计时器部分;请演示这个带有计时器的版本如何完成
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
我努力将 networkManager
修改为 class 成员,如何构建代码以及如何替换 lambda 函数。
如果有人可以提供所有需要的修改,让我更好地理解,那就太好了。
您可以将所有这些代码放入 QMainWindow
的构造函数中,并按原样保留 lambda 函数。
另一种更简洁的方法是将这些 lambda 函数转换为专用槽。使用这种方式,您应该将 networkManager
定义为 QMainWindow
class 的 class 成员,并且它应该分配在堆内存中而不是堆栈中。要完成此操作,只需定义一个 QNetworkManager*
class 成员并在 QMainWindow
构造函数中对其进行初始化。
this->networkManager = new QNetworkManager(this);
初始化后,您可以在 QMainWindow
class.
一个简单的经验法则是:lambda 函数和主作用域之间的所有共享变量都应该是 class 成员这样。
代码。 (我测试了它并且工作正常)
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->lineEditGender->setReadOnly(true);
ui->lineEditRegion->setReadOnly(true);
ui->lineEditName->setReadOnly(true);
networkManager = new QNetworkAccessManager(this);
connect(networkManager, &QNetworkAccessManager::finished, this, &MainWindow::onNetworkManagerFinished);
connect(ui->btnGetName, &QPushButton::clicked, this, &MainWindow::onBtnGetNameClicked);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::onNetworkManagerFinished(QNetworkReply *reply)
{
if(reply->error() != QNetworkReply::NoError){
ui->lineEditName->setText("Error");
ui->lineEditGender->setText("Error");
ui->lineEditRegion->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());
ui->lineEditName->setText(fullName);
ui->lineEditGender->setText(jsonObject["gender"].toString());
ui->lineEditRegion->setText(jsonObject["region"].toString());
}
ui->btnGetName->setEnabled(true);
reply->deleteLater();
}
void MainWindow::onBtnGetNameClicked()
{
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
networkManager->get(networkRequest);
ui->btnGetName->setEnabled(false);
ui->lineEditName->setText("Loading. . .");
ui->lineEditGender->setText("Loading. . .");
ui->lineEditRegion->setText("Loading. . .");
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QUrlQuery>
#include <QJsonDocument>
#include <QJsonObject>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void onNetworkManagerFinished(QNetworkReply* reply);
void onBtnGetNameClicked();
private:
Ui::MainWindow *ui;
QNetworkAccessManager *networkManager;
};
#endif // MAINWINDOW_H
您可能希望将用户界面和控制器(业务逻辑)分离成单独的 classes。
main()
的主体实例化 ui 和控制器并连接它们。每 5 秒获取新结果的计时器。计时器也可以滚动到 Controller
中 - 我展示它作为一个例子将它分开作为向现有 class 添加功能而不修改它的示例。
main.cpp
// https://github.com/KubaO/Whosebugn/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();
}
控制器对用户界面一无所知,只负责处理请求。每次开始处理请求时,它都会发出 busy
信号。
如果你想为多个活动请求提供更好的反馈,busy
信号只需要在没有待处理的请求并添加新请求时发出,并且 idle
当最后一个请求完成并且没有更多待处理的请求时,将发出信号。
controller.h
#ifndef CONTROLLER_H
#define CONTROLLER_H
#include <QtNetwork>
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
controller.cpp
#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);
}
用户界面对任何业务逻辑一无所知,它提供了一个足以让业务逻辑使用它的API。它可以处于三种状态之一:Normal
结果可见的状态,Loading
显示忙碌反馈的状态,以及显示错误信息的 Error
状态。 setFields
槽returns状态为Normal
.
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QtWidgets>
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
mainwindow.cpp
#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);
}
}