来自 Qt 中 main.cpp 代码的 MainWindow
Posted
技术标签:
【中文标题】来自 Qt 中 main.cpp 代码的 MainWindow【英文标题】:MainWindow from code from the main.cpp in Qt 【发布时间】:2016-09-22 15:47:48 【问题描述】:想了解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 函数。
如果有人可以为我提供所有必需的修改以更好地理解,那就太好了。
【问题讨论】:
如答案中所述,一个选项是在堆上分配networkManager
对象,并将指向它的指针作为MainWindow
类中的成员保留。这样做会遇到什么问题?
@Mike 我是 C++ 和 Qt 的新手,所以这些步骤中的每一个似乎都令人生畏。我尝试在头文件中定义networkManager
,然后尝试在MainWindow.cpp
中创建它的新对象,但不知道如何继续并调用它。得到许多错误。同样对于 lambda,我找不到任何在线资源来指导我。如果您能再次节省时间并为我提供代码供我学习,我将不胜感激。
【参考方案1】:
您可能希望将用户界面和控制器(业务逻辑)分离到单独的类中。
main()
的主体实例化了 ui 和控制器并将它们连接起来。每 5 秒获取新结果的计时器。计时器也可以滚动到 Controller
中 - 我将它作为一个示例将其分离出来,作为在不修改现有类的情况下添加功能的示例。
main.cpp
// https://github.com/KubaO/***n/tree/master/questions/into-mainwin-39643510
#include "mainwindow.h"
#include "controller.h"
int main(int argc, char *argv[])
QApplication appargc, 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
信号,而当上一个请求已完成,没有更多待处理的请求。
控制器.h
#ifndef CONTROLLER_H
#define CONTROLLER_H
#include <QtNetwork>
class Controller : public QObject
Q_OBJECT
QNetworkAccessManager managerthis;
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
槽将状态返回到Normal
。
主窗口.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QtWidgets>
class MainWindow : public QWidget
Q_OBJECT
QFormLayout layoutthis;
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
主窗口.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);
【讨论】:
太好了,我现在看到了我的大部分缺点 + 我看到了分离设计的重要性。您是否还可以添加带有计时器的版本,因为它可以在其他答案的编辑(***.com/a/39521698)中看到?尝试按照您的示例进行操作,但不确定我是否必须为其创建一个新的私人插槽.. @nk-fford 如果您在MainWindow
类中创建计时器,您可以将计时器的QTimer::timeout
信号连接到MainWindow::request
信号。
@thuga 能否请您显示代码(也许编辑答案)以说明您的意思,因为我尝试了这个但我得到但到处都是错误.. 我是 qt noob
@nk-fford 哪一部分让您感到困惑?将信号连接到信号?启动计时器?您将其与按钮完全相同,只需使用计时器对象和QTimer::timeout
信号即可。
@thuga 因此,在 mainwindow.h 中,我将按钮的专用插槽替换为 onTimeout。在connect
之前的mainwindow.cpp 中,我创建了一个QTimer 对象并启动它,然后我使用该对象定义connect(&timer,&QTimer::timeout,this,&MainWindow::onTimeout);
。最后,我再次用 onTimeout 东西替换了最后的按钮槽定义。但我没有得到任何数据。 (为了简单起见,我使用了 froggato 的版本)。请帮助我迷路了【参考方案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
【讨论】:
您能否说得更清楚一点,因为我是这一切的新手?例如,我将所有内容(除了main
定义和QApplication a(argc, argv)
)都放在mainwindow.cpp
的ui->setupUi(this);
行下。我得到了很多错误..请你显示代码供我学习吗?
@nk-fford 好的。我将在这里编写整个代码,供您学习如何编码。告诉我你知道如何在 Designer 中设计 UI 吗?
我很感激。是的,我可以在设计器中制作 GUI。
networkManager
对象正在泄漏,请使用networkManager = new QNetworkAccessManager(this);
对其进行实例化,以便在MainWindow
被破坏时将其删除。此外,readOnly
属性最好在设计器中完成。否则,我认为应该是这样,谢谢@HiImFrogatto。
@nk-fford 请接受 Kuba 的回答,它比我的要好。总是尝试将东西分成单一职责的类。 Single responsibility principle.以上是关于来自 Qt 中 main.cpp 代码的 MainWindow的主要内容,如果未能解决你的问题,请参考以下文章
如何在 qt creator 的 main.cpp 中有一个计时器,以便在单击按钮时更新 GUI?
qml-main.cpp中的两种启动Qt Quick App模式
main.cpp 和 mainwindow.cpp 之间的交互
qt 创建完工程运行main.cpp错误提示:-1: error: Cannot run compiler 'cl'. Output解决办法