重构为独立类后,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()), &amp;iLoop, SLOT(quit()));,看来不仅应用程序仍然可以工作,而且插槽错误不再出现。

以上是关于重构为独立类后,QNetworkRequest (HTTP GET) 不会触发的主要内容,如果未能解决你的问题,请参考以下文章

QNetworkRequest::User 和 QNetworkRequest::UserMax 之间的区别

如何使用 QNetworkRequest 将 Qlabel 与 jpg 一起使用?

如何为我使用 QNetworkAccessManager 上传的图像设置 QNetworkRequest::ContentTypeHeader 和 QNetworkRequest::ContentLe

QNetworkRequest 不工作

如何从 QNetworkrequest 中删除标头?

为了重构庞大的代码库,我应该记住啥?