【问题标题】:QT Callback with result after download is completed下载完成后带结果的 QT 回调
【发布时间】:2026-02-18 19:55:02
【问题描述】:

我有一个 QT 应用程序,其中几个不同的子组件需要从 Internet 获取数据。我想要一个Network 类来完成所有的网络交互,然后以某种方式通知调用者。我不知道如何使用插槽来做到这一点,所以我编写了一个回调解决方案:

void  Network::downloadFile(QString& urlstr, const std::function<void (QString, QByteArray, QNetworkReply::NetworkError)>& callback) {
    auto processDownload = [&]() {
        QNetworkReply * reply = qobject_cast<QNetworkReply *>(sender());
        QByteArray result;
        QString filename = reply->url().fileName();
        if (reply->error() == QNetworkReply::NoError) { // Success
            result = reply->readAll();
         }
        callback(filename, result, reply->error());
        reply->deleteLater();
    };

    QNetworkReply *reply = nam_->get(QNetworkRequest(QUrl(urlstr)));
    connect(reply, &QNetworkReply::downloadProgress, this, &Network::downloadProgress);
    connect(reply, &QNetworkReply::finished, this, processDownload);
}

但是,一旦调用callback,我就会收到段错误。我尝试了几种不同的方法来定义回调,即使它为空,它仍然会崩溃(主要是段错误,有时是错误的函数调用)。定义为:

std::function&lt;void(QJsonObject, QNetworkReply::NetworkError)&gt; f = std::bind(&amp;MainWindow::onResult, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);

我采取了类似static_cast&lt;parent_class*&gt;(this-&gt;parent())-&gt;onResult(..) 的方法,但这并不是很好的解耦。我怎样才能以可以传递(或发信号给)任何通用 std::function 的方式做到这一点?

编辑:Downloadfile 通常这样调用:

void MainWindow::DoNetworkRQ(QString url) {
    std::function<void(QJsonObject, QNetworkReply::NetworkError)> f = std::bind(&MainWindow::onResult, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
    network_.downloadFile(url, f);
}

这会导致段错误。如何以 QT 方式执行此操作?

【问题讨论】:

  • C++ 基础 - 您的 callback 变量超出范围。复制它而不是只传递对 lambda 的引用。
  • 我想访问对象实例,所以不能复制,对吧?什么是正确的“QT”方式来做到这一点? @chehrlic
  • 您能否展示您如何调用downloadFile,以便我们查看您为回调传递的内容?
  • @JarMan 添加了详细信息
  • "我想访问对象实例,所以不能复制,对吧?" - 不知道你在说什么,但我说局部变量 callback 超出范围,因为你只通过引用 lambda 传递它们,因此应用程序将崩溃。请阅读有关 lambda 捕获的信息。

标签: qt qt5 qtnetwork


【解决方案1】:

@chehrlic 是正确的,您正在创建一个在调用之前超出范围的回调函数。您的问题不需要以“Qt 方式”解决。但是信号和槽会起作用,所以我会告诉你怎么做。

首先,将您的信号连接到您的插槽:

void MainWindow::DoNetworkRQ(QString url) {
    connect(&network_, &Network::someSignal, this, &MainWindow::onResult);
    network_.downloadFile(url, f);
}

然后,当你的网络任务完成时发出信号:

void  Network::downloadFile(QString& urlstr, const std::function<void (QString, QByteArray, QNetworkReply::NetworkError)>& callback) {
    auto processDownload = [&]() {
        QNetworkReply * reply = qobject_cast<QNetworkReply *>(sender());
        QByteArray result;
        QString filename = reply->url().fileName();
        if (reply->error() == QNetworkReply::NoError) { // Success
            result = reply->readAll();
         }
        emit someSignal(filename, result, reply->error());
        reply->deleteLater();
    };

    QNetworkReply *reply = nam_->get(QNetworkRequest(QUrl(urlstr)));
    connect(reply, &QNetworkReply::downloadProgress, this, &Network::downloadProgress);
    connect(reply, &QNetworkReply::finished, this, processDownload);
}

【讨论】:

  • 在这种情况下根本不使用回调,网络类中的函数someSignal将连接到主窗口的MainWindow::onResult来做任何我想做的事?
  • 是的,我没有使用回调,因为您说只有在您无法让插槽工作时才这样做。因此,我将向您展示如何使插槽正常​​工作。您当然必须在 Network 类中声明您的信号。然后,只要您的 downloadFile 函数完成其工作,就应该调用 MainWindow::onResult
  • 谢谢,终于明白信号槽机制了!