重构为独立类后,QNetworkRequest (HTTP GET) 不会触发
Posted
技术标签:
【中文标题】重构为独立类后,QNetworkRequest (HTTP GET) 不会触发【英文标题】:QNetworkRequest (HTTP GET) doesn't fire, after refactoring into a standalone class 【发布时间】:2011-02-07 14:53:49 【问题描述】:我最近开始了将大约 2 个月前编写的大型单片音频播放器应用程序模块化的繁琐过程。
此过程进展顺利,尽管其中一种方法(ScrobbleMedia - 可以预见地足以使 HTTP 请求向 last.fm 提交有关播放曲目的信息)似乎不再发出网络请求。
但是,将传递给 QNetworkAccessManager 实例/QNetworkRequest 的 QUrl 对象正在正确构建。
作为比较,代码的功能性 Mercurial 修订版可在 BitBucket 上获得。
重构后的 ScrobbleMedia 方法目前如下所示:
#include "scrobblemedia.h"
#include <QDebug>
#include <cstdio>
ScrobbleMedia::ScrobbleMedia(QString asUsername, QString asPassword,
QString asArtist, QString asTrack, QString asAlbum)
QString KEndPointURL = "http://lastfmstats.livefrombmore.com/universalscrobbler/scrobble.php";
QUrl iScrobbleEndPoint(KEndPointURL);
QNetworkAccessManager *iScrobbleDispatcher = new QNetworkAccessManager(this);
iScrobbleEndPoint.addQueryItem("submissionType","track");
iScrobbleEndPoint.addQueryItem("username", asUsername);
iScrobbleEndPoint.addQueryItem("password", asPassword);
iScrobbleEndPoint.addQueryItem("artist", asArtist);
iScrobbleEndPoint.addQueryItem("track", asTrack);
iScrobbleEndPoint.addQueryItem("album", asAlbum);
iScrobbleEndPoint.addQueryItem("number","1");
iScrobbleEndPoint.addQueryItem("duration","200");
iScrobbleDispatcher->get(QNetworkRequest(iScrobbleEndPoint));
connect(iScrobbleDispatcher, SIGNAL(finished(QNetworkReply*)),
SLOT(replyFinished(QNetworkReply*)));
// QString Outside = iScrobbleEndPoint.toEncoded();
qDebug() << "Received: " +
asUsername + " " +
asPassword + " " +
asArtist + " " +
asTrack + " " +
asAlbum;
qDebug() << iScrobbleEndPoint.toString();
ScrobbleMedia::~ScrobbleMedia()
相关的头文件如下所示:
#ifndef SCROBBLEMEDIA_H
#define SCROBBLEMEDIA_H
#include <QString>
#include <QtNetwork>
#include <QUrl>
#include <QNetworkAccessManager>
class ScrobbleMedia : public QObject
Q_OBJECT;
private:
public:
ScrobbleMedia(QString asUsername, QString asPassword, QString asArtist, QString asTrack, QString asAlbum);
~ScrobbleMedia();
;
#endif // SCROBBLEMEDIA_H
我目前正在针对 Windows 7 x86-64 下 Qt 库(作为 Qt SDK 2010.05 的一部分包含)的 MinGW 版本构建应用程序本身。
任何帮助将不胜感激。
提前致谢。
【问题讨论】:
两个问题:为什么Q_OBJECT宏后面有分号?为什么标题中没有声明replyFinished() 槽?如果您在 Windows 上,您可能希望使用 CONFIG+=console qmake 选项重新编译您的项目,因为它可以让您看到运行时 Qt 警告(例如“没有这样的插槽”消息)。 谢谢,谢尔盖。今晚我会更新代码,让你知道会发生什么(因为我现在手头没有 Qt 工具链)。分号是习惯性的,我不确定它是否会有所作为。 @Tyson,您还应该提供您的 Qt SDK 版本。对于旧版本的 Qt,QNAM 和 finished() 信号存在许多问题。 @Tyson,至于分号,把它放在一个扩展为只有上帝而巨魔知道什么的宏之后尤其危险。但即使不考虑这一点,在函数体内以外的任何地方都使用额外的分号本身就非常危险,而且它可能会破坏许多编译器。 @Tyson,未定义的引用只是意味着您已在类中声明它,但尚未在实现文件中定义它。 (比如,忘了加ScrobbleMedia::
。)
【参考方案1】:
在阅读了许多不同的信息来源(其中大部分是相互矛盾的)之后,我发现以下结果是一个可行的解决方案 - 尽管调试输出中存在一个看起来不影响操作的外观问题(Object::connect: No such signal QNetworkReplyImpl::finished(QNetworkReply*) in ../AudioPlayer/scrobblemedia.cpp:29
):
在scrobblemedia.cpp
:
#include "scrobblemedia.h"
#include <QDebug>
#include <cstdio>
ScrobbleMedia::ScrobbleMedia(QString asUsername, QString asPassword,
QString asArtist, QString asTrack, QString asAlbum)
QByteArray iDataSink;
QEventLoop iLoop;
QString KEndPointURL = "http://lastfmstats.livefrombmore.com/universalscrobbler/scrobble.php";
QUrl iScrobbleEndPoint(KEndPointURL);
iScrobbleEndPoint.addQueryItem("submissionType","track");
iScrobbleEndPoint.addQueryItem("username", asUsername);
iScrobbleEndPoint.addQueryItem("password", asPassword);
iScrobbleEndPoint.addQueryItem("artist", asArtist);
iScrobbleEndPoint.addQueryItem("track", asTrack);
iScrobbleEndPoint.addQueryItem("album", asAlbum);
iScrobbleEndPoint.addQueryItem("number","1");
iScrobbleEndPoint.addQueryItem("duration","200");
QNetworkAccessManager iScrobbleDispatcher;
QNetworkRequest iScrobbleRequest(iScrobbleEndPoint);
QNetworkReply *iScrobbleReply = iScrobbleDispatcher.get(iScrobbleRequest);
QObject::connect(iScrobbleReply, SIGNAL(finished(QNetworkReply*)), &iLoop,
SLOT(quit()));
iDataSink = iScrobbleReply->readAll();
qDebug() << "Received: " + asUsername + " " + asPassword + " " + asArtist + " " + asTrack + " " + asAlbum;
qDebug() << iScrobbleEndPoint.toString();
iLoop.exec();
ScrobbleMedia::~ScrobbleMedia()
void ScrobbleMedia::replyFinished(QNetworkReply*)
void ScrobbleMedia::reallyDone()
qDebug() << "We've probably successfully Scrobbled...";
在scrobblemedia.h
:
#ifndef SCROBBLEMEDIA_H
#define SCROBBLEMEDIA_H
#include <QString>
#include <QtNetwork>
#include <QUrl>
#include <QNetworkAccessManager>
class ScrobbleMedia : public QObject
Q_OBJECT
private:
public:
ScrobbleMedia(QString asUsername, QString asPassword, QString asArtist, QString asTrack, QString asAlbum);
~ScrobbleMedia();
private slots:
void replyFinished(QNetworkReply*);
void reallyDone();
;
#endif // SCROBBLEMEDIA_H
感谢大家的帮助。
希望此代码将来可以作为其他人的有用模板。
【讨论】:
reallyDone() 插槽究竟在哪里被调用?我没有在代码中看到它。为什么需要事件循环?如果它不是多线程应用程序,那么使用单独的事件循环至少是不必要的,甚至可能是危险的。最后,您的问题不是“化妆品”。你得到它是因为你试图连接到 iScrobbleReply 中不存在的信号finished(QNetworkReply*)
。它只有 finished() 信号,没有参数。它是具有带有 QNetworkReply* 参数的 finished() 插槽的 QNetworkAccessManager。这就是为什么你的 quit() 方法永远不会被调用的原因。
reallyDone() 不会在任何地方调用 - 它只是之前尝试让事情正常工作的结果。事件循环的想法是在***.com/questions/2572985/… 提出的,似乎对我有用。
考虑到这一点,我删除了reallyDone()
,并定义了void finished(QNetworkReply*)
,此外还提供了它的存根实现。代码本身仍然有效(我可以在 Wireshark 中看到 HTTP 请求),但是干净利落地处理“无底槽”问题是以后的事情,因为我很快就要离开去某个地方了。
更新:我已将connect()
调用更新为QObject::connect(iScrobbleReply, SIGNAL(finished()), &iLoop, SLOT(quit()));
,看来不仅应用程序仍然可以工作,而且插槽错误不再出现。以上是关于重构为独立类后,QNetworkRequest (HTTP GET) 不会触发的主要内容,如果未能解决你的问题,请参考以下文章
QNetworkRequest::User 和 QNetworkRequest::UserMax 之间的区别
如何使用 QNetworkRequest 将 Qlabel 与 jpg 一起使用?
如何为我使用 QNetworkAccessManager 上传的图像设置 QNetworkRequest::ContentTypeHeader 和 QNetworkRequest::ContentLe