想要了解MainWindow
和main.cpp
之间代码的差异。具体而言,需要如何修改main.cpp
中专门编写的代码块,使其成为mainwindow.cpp
和mainwindow.h
的一部分。
例如,我正在尝试修改此answer中的代码,以便在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
修改为类成员,如何构造代码以及如何替换lambda函数。
如果有人可以为我提供所有必需的修改,以获得更好的理解,那就太棒了。
答案 0 :(得分:3)
您可能希望将用户界面和控制器(业务逻辑)分离为单独的类。
main()
的主体实例化ui和控制器并连接它们。每5秒获取一次新结果的计时器。计时器也可以滚动到Controller
中 - 我将其分离出来作为向现有类添加功能而不修改它的示例。
// 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();
}
控制器对用户界面一无所知,只处理请求。每次处理请求时,它都会发出busy
信号。
如果您希望为多个活动请求提供更好的反馈,则只有在没有待处理请求且添加新请求时才需要发出busy
信号,并且idle
信号将会发出在最后一个请求完成时发出,并且没有更多的待处理请求。
#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
#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
广告位将状态返回到Normal
。
#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
#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);
}
}
答案 1 :(得分:2)
您可以将所有这些代码放在QMainWindow
的构造函数中,并按原样保留lambda函数。
另一种更干净的方法是将这些lambda函数转换为私有槽。使用这种方式,您应该将networkManager
定义为QMainWindow
类的类成员,并且它应该在堆内存中而不是堆栈中分配。要完成此操作,只需定义QNetworkManager*
类成员并在QMainWindow
构造函数中初始化它。
this->networkManager = new QNetworkManager(this);
初始化后,您可以在QMainWindow
课程的所有广告位中使用它。
一个简单的经验法则是:lambda函数中的所有共享变量和主范围都应该是这样的类成员。
代码。 (我测试了它并且工作正常)
<强>的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